From 17e9344f6e770e6e2f76f11e24b10e20c9a4d6a0 Mon Sep 17 00:00:00 2001 From: luis cielak Date: Wed, 8 Aug 2018 13:46:48 -0400 Subject: [PATCH 01/88] Style form labels for inputs correctly --- styles/elements/_inputs.scss | 22 +++++++++++++--------- templates/components/text_input.html | 2 -- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/styles/elements/_inputs.scss b/styles/elements/_inputs.scss index 459e907d..7ce1c714 100644 --- a/styles/elements/_inputs.scss +++ b/styles/elements/_inputs.scss @@ -65,20 +65,12 @@ } label { - padding: 0 0 $gap 0; + padding: 0; margin: 0; @include h4; @include line-max; position: relative; - .usa-input__help { - display: block; - @include h5; - font-weight: normal; - padding-top: $gap / 2; - @include line-max; - } - .icon { position: absolute; left: 100%; @@ -88,6 +80,14 @@ } } + .usa-input__help { + display: block; + @include h4; + font-weight: normal; + padding: $gap/2 0; + @include line-max; + } + input, textarea, select { @@ -103,6 +103,10 @@ .icon { vertical-align: middle; } + + .usa-input__help { + font-weight: $font-bold; + } } ul { diff --git a/templates/components/text_input.html b/templates/components/text_input.html index 0f50ca89..8145c366 100644 --- a/templates/components/text_input.html +++ b/templates/components/text_input.html @@ -1,6 +1,5 @@ {% macro TextInput(field, placeholder='') -%}
- {{ field(placeholder=placeholder) | safe }} From 41a6d28e8879720413615d6c923f03decfec9d5d Mon Sep 17 00:00:00 2001 From: luis cielak Date: Wed, 8 Aug 2018 13:47:31 -0400 Subject: [PATCH 02/88] Add subtitle style --- styles/elements/_typography.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/styles/elements/_typography.scss b/styles/elements/_typography.scss index fcdc97d9..b1af1be1 100644 --- a/styles/elements/_typography.scss +++ b/styles/elements/_typography.scss @@ -20,6 +20,7 @@ h1, h2, h3, h4, h5, h6 { + .subtitle * { margin-top: 0; + color: $color-gray; } } From d582cf2a412365f81c740b38cd4c0957be17ef60 Mon Sep 17 00:00:00 2001 From: luis cielak Date: Wed, 8 Aug 2018 13:50:48 -0400 Subject: [PATCH 03/88] Increase some padding --- styles/elements/_inputs.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/elements/_inputs.scss b/styles/elements/_inputs.scss index 7ce1c714..69750caf 100644 --- a/styles/elements/_inputs.scss +++ b/styles/elements/_inputs.scss @@ -65,7 +65,7 @@ } label { - padding: 0; + padding: 0 0 $gap/2 0; margin: 0; @include h4; @include line-max; From 74fc5f0ec91f9ff3daf4b87804af6efc35249385 Mon Sep 17 00:00:00 2001 From: luis cielak Date: Wed, 8 Aug 2018 13:54:10 -0400 Subject: [PATCH 04/88] Make sure the label style for input text and radios match --- atst/forms/request.py | 2 +- styles/elements/_inputs.scss | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/atst/forms/request.py b/atst/forms/request.py index f15e9140..fb179587 100644 --- a/atst/forms/request.py +++ b/atst/forms/request.py @@ -62,7 +62,7 @@ class RequestForm(ValidatedForm): ) engineering_assessment = RadioField( - description="Have you completed an engineering assessment of your software systems for cloud readiness?", + "Have you completed an engineering assessment of your software systems for cloud readiness?", choices=[("yes", "Yes"), ("no", "No"), ("in_progress", "In Progress")], ) diff --git a/styles/elements/_inputs.scss b/styles/elements/_inputs.scss index 69750caf..e0cab5be 100644 --- a/styles/elements/_inputs.scss +++ b/styles/elements/_inputs.scss @@ -100,6 +100,10 @@ padding: 0 0 $gap 0; @include h4; + label { + font-weight: $font-bold; + } + .icon { vertical-align: middle; } From 650781c759963421878459829aaf7770ab96f6b1 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Wed, 8 Aug 2018 14:40:17 -0400 Subject: [PATCH 05/88] add data for field pipe and keepCharPositions --- js/components/text_input.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/components/text_input.js b/js/components/text_input.js index e027a800..ecfa3d75 100644 --- a/js/components/text_input.js +++ b/js/components/text_input.js @@ -25,6 +25,8 @@ export default { showError: false, showValid: false, mask: inputValidations[this.validation].mask, + pipe: inputValidations[this.validation].pipe || undefined, + keepCharPositions: inputValidations[this.validation].keepCharPositions || false, renderedValue: this.value } }, From 1c4c49da6257ab5d3f69d90f157be6a35cba8b34 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Wed, 8 Aug 2018 14:40:33 -0400 Subject: [PATCH 06/88] Add new input validation sets --- js/lib/input_validations.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/js/lib/input_validations.js b/js/lib/input_validations.js index 6e7a066d..db21eec9 100644 --- a/js/lib/input_validations.js +++ b/js/lib/input_validations.js @@ -1,5 +1,6 @@ import createNumberMask from 'text-mask-addons/dist/createNumberMask' import emailMask from 'text-mask-addons/dist/emailMask' +import createAutoCorrectedDatePipe from 'text-mask-addons/dist/createAutoCorrectedDatePipe' export default { anything: { @@ -7,14 +8,31 @@ export default { match: /^(?!\s*$).+/, unmask: [], }, + integer: { + mask: createNumberMask({ prefix: '', allowDecimal: false }), + match: /^[1-9]\d*$/, + unmask: [] + }, dollars: { mask: createNumberMask({ prefix: '$', allowDecimal: true }), match: /^-?\d+\.?\d*$/, unmask: ['$',','] }, + gigabytes: { + mask: createNumberMask({ prefix: '', suffix:'GB', allowDecimal: false }), + match: /^[1-9]\d*$/, + unmask: ['GB'] + }, email: { mask: emailMask, match: /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/, unmask: [], + }, + date: { + mask: [/\d/,/\d/,'/',/\d/,/\d/,'/',/\d/,/\d/,/\d/,/\d/], + match: /(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d/, + unmask: [], + pipe: createAutoCorrectedDatePipe('mm/dd/yyyy HH:MM'), + keepCharPositions: true } } From c80490f7177fcdd8ba9152a56e65d75cbca8f154 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Wed, 8 Aug 2018 14:40:58 -0400 Subject: [PATCH 07/88] Vue-ify text-input macro --- templates/components/text_input.html | 73 +++++++++++++++++++++------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/templates/components/text_input.html b/templates/components/text_input.html index 0f50ca89..b620cf35 100644 --- a/templates/components/text_input.html +++ b/templates/components/text_input.html @@ -1,23 +1,60 @@ -{% macro TextInput(field, placeholder='') -%} -
-
+ {%- endmacro %} From 22266959d669375b279ab24afe90ec8e6ff00904 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Wed, 8 Aug 2018 14:50:08 -0400 Subject: [PATCH 08/88] Add input validations and some placeholder changes to request form page 1 --- templates/requests/screen-1.html | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/templates/requests/screen-1.html b/templates/requests/screen-1.html index 7dbfb367..43266b04 100644 --- a/templates/requests/screen-1.html +++ b/templates/requests/screen-1.html @@ -22,27 +22,27 @@

All fields are required, unless specified optional.

General

-{{ TextInput(f.dod_component) }} -{{ TextInput(f.jedi_usage,placeholder="e.g. We are migrating XYZ application to the cloud so that...") }} +{{ OptionsInput(f.dod_component) }} +{{ TextInput(f.jedi_usage, paragraph=True, placeholder="e.g. We are migrating XYZ application to the cloud so that...") }}

Cloud Readiness

-{{ TextInput(f.num_software_systems,placeholder="Number of systems") }} +{{ TextInput(f.num_software_systems, validation='integer') }} {{ OptionsInput(f.jedi_migration) }} {{ OptionsInput(f.rationalization_software_systems) }} {{ OptionsInput(f.technical_support_team) }} {{ OptionsInput(f.organization_providing_assistance) }} {{ OptionsInput(f.engineering_assessment) }} -{{ TextInput(f.data_transfers) }} -{{ TextInput(f.expected_completion_date) }} +{{ OptionsInput(f.data_transfers) }} +{{ TextInput(f.expected_completion_date, validation='date', placeholder='MM / DD / YYYY') }} {{ OptionsInput(f.cloud_native) }}

Financial Usage

-{{ TextInput(f.estimated_monthly_spend) }} +{{ TextInput(f.estimated_monthly_spend, validation='dollars') }}

So this means you are spending approximately $X annually

-{{ TextInput(f.dollar_value) }} -{{ TextInput(f.number_user_sessions) }} -{{ TextInput(f.average_daily_traffic) }} -{{ TextInput(f.start_date) }} +{{ TextInput(f.dollar_value, validation='dollars') }} +{{ TextInput(f.number_user_sessions, validation='integer') }} +{{ TextInput(f.average_daily_traffic, placeholder='Gigabytes per day', validation='gigabytes') }} +{{ TextInput(f.start_date, validation='date', placeholder='MM / DD / YYYY') }} {% endblock %} From 993a6635b2d4192aefd1ad1feac04f2dd4181fd8 Mon Sep 17 00:00:00 2001 From: luis cielak Date: Wed, 8 Aug 2018 17:39:03 -0400 Subject: [PATCH 09/88] Render error validation message with proper color --- templates/components/options_input.html | 2 +- templates/components/text_input.html | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/components/options_input.html b/templates/components/options_input.html index ff0d8814..06f4c3db 100644 --- a/templates/components/options_input.html +++ b/templates/components/options_input.html @@ -1,7 +1,7 @@ {% from "components/icon.html" import Icon %} {% macro OptionsInput(field, inline=False) -%} -
+
diff --git a/templates/components/text_input.html b/templates/components/text_input.html index 8145c366..0f50ca89 100644 --- a/templates/components/text_input.html +++ b/templates/components/text_input.html @@ -1,5 +1,6 @@ {% macro TextInput(field, placeholder='') -%}
+ {{ field(placeholder=placeholder) | safe }} From 5b102651268c8c131efd2fe59c9d3134fb71e4c0 Mon Sep 17 00:00:00 2001 From: luis cielak Date: Wed, 8 Aug 2018 17:44:37 -0400 Subject: [PATCH 10/88] Render error message with proper color for input text --- templates/components/text_input.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/components/text_input.html b/templates/components/text_input.html index 0f50ca89..1f272934 100644 --- a/templates/components/text_input.html +++ b/templates/components/text_input.html @@ -1,5 +1,5 @@ {% macro TextInput(field, placeholder='') -%} -
+
+ + + {%- endmacro %} From c0f3c0f8a640d836c91361c40ce895eb6968e85d Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Thu, 9 Aug 2018 10:11:44 -0400 Subject: [PATCH 13/88] Fix financial verification form submission --- atst/routes/requests/financial_verification.py | 2 +- ...ubmitted.html.to => financial_verification_submitted.html} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename templates/requests/{financial_verification_submitted.html.to => financial_verification_submitted.html} (84%) diff --git a/atst/routes/requests/financial_verification.py b/atst/routes/requests/financial_verification.py index 38420287..ce70eaee 100644 --- a/atst/routes/requests/financial_verification.py +++ b/atst/routes/requests/financial_verification.py @@ -41,4 +41,4 @@ def update_financial_verification(request_id): @requests_bp.route("/requests/financial_verification_submitted") def financial_verification_submitted(): - pass + return render_template("requests/financial_verification_submitted.html") diff --git a/templates/requests/financial_verification_submitted.html.to b/templates/requests/financial_verification_submitted.html similarity index 84% rename from templates/requests/financial_verification_submitted.html.to rename to templates/requests/financial_verification_submitted.html index 21088724..01d6ef89 100644 --- a/templates/requests/financial_verification_submitted.html.to +++ b/templates/requests/financial_verification_submitted.html @@ -1,4 +1,4 @@ -{% extends "../base.html.to" %} +{% extends "base.html" %} {% block content %} @@ -15,4 +15,4 @@
-{% end %} +{% endblock %} From 994af840d9707aff8b79c9069711314646856cbf Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Thu, 9 Aug 2018 10:26:58 -0400 Subject: [PATCH 14/88] Fix NewlineListField displaying too many newlines --- atst/forms/fields.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/atst/forms/fields.py b/atst/forms/fields.py index bc542bf5..00e53529 100644 --- a/atst/forms/fields.py +++ b/atst/forms/fields.py @@ -29,8 +29,10 @@ class NewlineListField(Field): widget = TextArea() def _value(self): - if self.data: - return "\n".join(self.data) + if isinstance(self.data, list): + return '\n'.join(self.data) + elif self.data: + return self.data else: return "" From edcd8bb92148569d762305faaf95af76d9efe57e Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Thu, 9 Aug 2018 10:51:04 -0400 Subject: [PATCH 15/88] Require PE id to be defined in financial verification form --- atst/forms/financial.py | 2 +- atst/routes/requests/financial_verification.py | 2 +- tests/routes/test_financial_verification.py | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/atst/forms/financial.py b/atst/forms/financial.py index 68196837..674eace4 100644 --- a/atst/forms/financial.py +++ b/atst/forms/financial.py @@ -70,7 +70,7 @@ class FinancialForm(ValidatedForm): "Unique Item Identifier (UII)s related to your application(s) if you already have them." ) - pe_id = StringField("Program Element (PE) Number related to your request") + pe_id = StringField("Program Element (PE) Number related to your request", validators=[Required()]) treasury_code = StringField("Program Treasury Code") diff --git a/atst/routes/requests/financial_verification.py b/atst/routes/requests/financial_verification.py index ce70eaee..f6b8cfda 100644 --- a/atst/routes/requests/financial_verification.py +++ b/atst/routes/requests/financial_verification.py @@ -25,10 +25,10 @@ def update_financial_verification(request_id): if form.validate(): request_data = {"financial_verification": post_data} - Requests.update(request_id, request_data) valid = form.perform_extra_validation( existing_request.body.get("financial_verification") ) + Requests.update(request_id, request_data) if valid: return redirect(url_for("requests.financial_verification_submitted")) else: diff --git a/tests/routes/test_financial_verification.py b/tests/routes/test_financial_verification.py index e15638df..8e62cc48 100644 --- a/tests/routes/test_financial_verification.py +++ b/tests/routes/test_financial_verification.py @@ -72,3 +72,14 @@ class TestPENumberInForm: assert response.status_code == 302 assert "/requests/financial_verification_submitted" in response.headers.get("Location") + + def test_submit_request_form_with_missing_pe_id(self, monkeypatch, client): + self._set_monkeypatches(monkeypatch) + + data = dict(self.required_data) + data['pe_id'] = '' + + response = self.submit_data(client, data) + + assert "There were some errors, see below" in response.data.decode() + assert response.status_code == 200 From fc436af1349807cc26f1069258b09247e6f648f2 Mon Sep 17 00:00:00 2001 From: dandds Date: Thu, 9 Aug 2018 11:48:33 -0400 Subject: [PATCH 16/88] log errors in error handlers --- atst/domain/exceptions.py | 4 +++- atst/routes/errors.py | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/atst/domain/exceptions.py b/atst/domain/exceptions.py index b92e64c1..ec574232 100644 --- a/atst/domain/exceptions.py +++ b/atst/domain/exceptions.py @@ -27,4 +27,6 @@ class UnauthorizedError(Exception): class UnauthenticatedError(Exception): - pass + @property + def message(self): + return str(self) diff --git a/atst/routes/errors.py b/atst/routes/errors.py index 0d0211b8..e9dcafcf 100644 --- a/atst/routes/errors.py +++ b/atst/routes/errors.py @@ -8,12 +8,14 @@ def make_error_pages(app): @app.errorhandler(exceptions.UnauthorizedError) # pylint: disable=unused-variable def not_found(e): + app.logger.error(e.message) return render_template("not_found.html"), 404 @app.errorhandler(exceptions.UnauthenticatedError) # pylint: disable=unused-variable def unauthorized(e): + app.logger.error(e.message) return render_template('unauthorized.html'), 401 return app From 3f01d455bb440cee7b1d1e9791e3b94af3315ef0 Mon Sep 17 00:00:00 2001 From: dandds Date: Thu, 9 Aug 2018 11:57:37 -0400 Subject: [PATCH 17/88] better name, text for unauthenticated page --- atst/routes/errors.py | 2 +- templates/{unauthorized.html => unauthenticated.html} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename templates/{unauthorized.html => unauthenticated.html} (83%) diff --git a/atst/routes/errors.py b/atst/routes/errors.py index e9dcafcf..5fdac3d1 100644 --- a/atst/routes/errors.py +++ b/atst/routes/errors.py @@ -16,6 +16,6 @@ def make_error_pages(app): # pylint: disable=unused-variable def unauthorized(e): app.logger.error(e.message) - return render_template('unauthorized.html'), 401 + return render_template('unauthenticated.html'), 401 return app diff --git a/templates/unauthorized.html b/templates/unauthenticated.html similarity index 83% rename from templates/unauthorized.html rename to templates/unauthenticated.html index efaa3b95..8fabbdf9 100644 --- a/templates/unauthorized.html +++ b/templates/unauthenticated.html @@ -4,7 +4,7 @@
-

Unauthorized

+

Log in Failed

From 034c1471540be2dcafed70ef5b7b650947be78db Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Thu, 9 Aug 2018 13:44:30 -0400 Subject: [PATCH 18/88] remove commas from unmasked integer values --- js/lib/input_validations.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/lib/input_validations.js b/js/lib/input_validations.js index db21eec9..68f3b2a8 100644 --- a/js/lib/input_validations.js +++ b/js/lib/input_validations.js @@ -11,7 +11,7 @@ export default { integer: { mask: createNumberMask({ prefix: '', allowDecimal: false }), match: /^[1-9]\d*$/, - unmask: [] + unmask: [','] }, dollars: { mask: createNumberMask({ prefix: '$', allowDecimal: true }), @@ -21,7 +21,7 @@ export default { gigabytes: { mask: createNumberMask({ prefix: '', suffix:'GB', allowDecimal: false }), match: /^[1-9]\d*$/, - unmask: ['GB'] + unmask: [',','GB'] }, email: { mask: emailMask, From c2851c2e8674361a3a7129674814f6c58e4bdd4e Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Thu, 9 Aug 2018 13:46:04 -0400 Subject: [PATCH 19/88] Among other things, make sure theres a name on the inputs so the forms can submit --- templates/components/text_input.html | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/templates/components/text_input.html b/templates/components/text_input.html index b620cf35..b4c990a9 100644 --- a/templates/components/text_input.html +++ b/templates/components/text_input.html @@ -1,14 +1,18 @@ {% from "components/icon.html" import Icon %} {% macro TextInput(field, placeholder='', validation='anything', paragraph=False) -%} - + +
+ v-bind:class="['usa-input usa-input--validation--' + validation, { 'usa-input--error': showError, 'usa-input--success': showValid }]" + class='usa-input {% if field.errors %}usa-input--error{% endif %}'>
{% assets "js_all" %} diff --git a/templates/requests.html b/templates/requests.html index 25520ac2..4bb7509b 100644 --- a/templates/requests.html +++ b/templates/requests.html @@ -4,37 +4,32 @@ {% from "components/modal.html" import Modal %} {% from "components/empty_state.html" import EmptyState %} -{% block modal %} - {% if g.modalOpen %} - {% call Modal() %} -

Your request is now approved!

- -

- Your next step is to create a Task Order (T.O.) associated with - JEDI Cloud. Please consult a Contracting Officer (KO) or - Contracting Officer Representative (COR) to help with this step. -

- -

- Once the Task Order (T.O.) has been created, we will need the following - details to create your account. These details will help keep your cloud - usage in sync with your budget. -

- - {{ Alert("You'll need these details: ", - message="

Task Order Number

Contracting Officer: Name, E-mail and Office

" - ) }} - - -
- Close -
- {% endcall %} - {% endif %} -{% endblock %} - {% block content %} + {% call Modal(name='pendingFinancialVerification', dismissable=True) %} +

Your request is now approved!

+ +

+ Your next step is to create a Task Order (T.O.) associated with + JEDI Cloud. Please consult a Contracting Officer (KO) or + Contracting Officer Representative (COR) to help with this step. +

+ +

+ Once the Task Order (T.O.) has been created, we will need the following + details to create your account. These details will help keep your cloud + usage in sync with your budget. +

+ + {{ Alert("You'll need these details: ", + message="

Task Order Number

Contracting Officer: Name, E-mail and Office

" + ) }} + +
+ Close +
+ {% endcall %} + {% if not requests %} {{ EmptyState( diff --git a/tests/routes/test_request_submit.py b/tests/routes/test_request_submit.py index 428e056e..f8e61400 100644 --- a/tests/routes/test_request_submit.py +++ b/tests/routes/test_request_submit.py @@ -1,6 +1,7 @@ import pytest from tests.mocks import MOCK_USER from tests.factories import RequestFactory +from atst.models.request_status_event import RequestStatus def _mock_func(*args, **kwargs): @@ -27,12 +28,11 @@ def test_submit_autoapproved_reviewed_request(monkeypatch, client, user_session) user_session() monkeypatch.setattr("atst.domain.requests.Requests.get", _mock_func) monkeypatch.setattr("atst.domain.requests.Requests.submit", _mock_func) - monkeypatch.setattr("atst.models.request.Request.status", "approved") - # this just needs to send a known invalid form value + monkeypatch.setattr("atst.models.request.Request.status", RequestStatus.PENDING_FINANCIAL_VERIFICATION) response = client.post( "/requests/submit/1", headers={"Content-Type": "application/x-www-form-urlencoded"}, data="", follow_redirects=False, ) - assert "/requests?modal=True" in response.headers["Location"] + assert "/requests?modal=" in response.headers["Location"] From a9dfa005a919a8f96fb15dc1ec3c66afcd7e5162 Mon Sep 17 00:00:00 2001 From: dandds Date: Thu, 9 Aug 2018 13:53:45 -0400 Subject: [PATCH 22/88] update financial verification copy --- templates/requests.html | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/templates/requests.html b/templates/requests.html index 4bb7509b..879560a2 100644 --- a/templates/requests.html +++ b/templates/requests.html @@ -7,23 +7,22 @@ {% block content %} {% call Modal(name='pendingFinancialVerification', dismissable=True) %} -

Your request is now approved!

+

Request submitted!

- Your next step is to create a Task Order (T.O.) associated with - JEDI Cloud. Please consult a Contracting Officer (KO) or - Contracting Officer Representative (COR) to help with this step. + The next step is to create a Task Order associated with JEDI Cloud. + Please contact a Contracting Officer (KO), Contracting Officer + Representative (COR), or a Financial Manager to help with this step.

- Once the Task Order (T.O.) has been created, we will need the following - details to create your account. These details will help keep your cloud - usage in sync with your budget. + Once the Task Order has been created, you will be asked to provide + details about the task order in the Financial Verification step.

- {{ Alert("You'll need these details: ", - message="

Task Order Number

Contracting Officer: Name, E-mail and Office

" - ) }} +

+ Learn more about the JEDI Task Order and the Financial Verification process. +

Close @@ -42,7 +41,20 @@ {% else %} {{ Alert('Pending Financial Verification', - message="

Your next step is to create a Task Order (T.O.) associated with JEDI Cloud. Please consult a Contracting Officer (KO) or Contracting Officer Representative (COR) to help with this step.

" + message=" +

+ The next step is to create a Task Order associated with JEDI Cloud. + Please contact a Contracting Officer (KO), Contracting Officer + Representative (COR), or a Financial Manager to help with this step. +

+

+ Once the Task Order has been created, you will be asked to provide + details about the task order in the Financial Verification step. +

+

+ Learn more about the JEDI Task Order and the Financial Verification process. +

+ " ) }}
From 5f9b58946576cc37b313bbb2abbaa5dc95c19ac7 Mon Sep 17 00:00:00 2001 From: dandds Date: Thu, 9 Aug 2018 14:10:17 -0400 Subject: [PATCH 23/88] only show financial verification alert on requests index page if relevant --- atst/routes/requests/index.py | 5 ++++- templates/requests.html | 36 +++++++++++++++++++---------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/atst/routes/requests/index.py b/atst/routes/requests/index.py index 5e9d26ca..dc1d137e 100644 --- a/atst/routes/requests/index.py +++ b/atst/routes/requests/index.py @@ -3,6 +3,7 @@ from flask import render_template, g, url_for from . import requests_bp from atst.domain.requests import Requests +from atst.models.request_status_event import RequestStatus def map_request(request): @@ -33,4 +34,6 @@ def requests_index(): mapped_requests = [map_request(r) for r in requests] - return render_template("requests.html", requests=mapped_requests) + pending_fv = any(r["status"] == RequestStatus.PENDING_FINANCIAL_VERIFICATION.value for r in mapped_requests) + + return render_template("requests.html", requests=mapped_requests, pending_financial_verification=pending_fv) diff --git a/templates/requests.html b/templates/requests.html index 879560a2..4dc0e865 100644 --- a/templates/requests.html +++ b/templates/requests.html @@ -40,22 +40,26 @@ {% else %} - {{ Alert('Pending Financial Verification', - message=" -

- The next step is to create a Task Order associated with JEDI Cloud. - Please contact a Contracting Officer (KO), Contracting Officer - Representative (COR), or a Financial Manager to help with this step. -

-

- Once the Task Order has been created, you will be asked to provide - details about the task order in the Financial Verification step. -

-

- Learn more about the JEDI Task Order and the Financial Verification process. -

- " - ) }} + {% if pending_financial_verification %} + + {{ Alert('Pending Financial Verification', + message=" +

+ The next step is to create a Task Order associated with JEDI Cloud. + Please contact a Contracting Officer (KO), Contracting Officer + Representative (COR), or a Financial Manager to help with this step. +

+

+ Once the Task Order has been created, you will be asked to provide + details about the task order in the Financial Verification step. +

+

+ Learn more about the JEDI Task Order and the Financial Verification process. +

+ " + ) }} + + {% endif %}
From 1b2e091a694592f93d8df94e0a74a67a1b5de6f9 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Thu, 9 Aug 2018 14:26:45 -0400 Subject: [PATCH 24/88] get rid of duplicate class declaration --- templates/components/text_input.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/templates/components/text_input.html b/templates/components/text_input.html index 189504ed..4c52a634 100644 --- a/templates/components/text_input.html +++ b/templates/components/text_input.html @@ -8,8 +8,7 @@ inline-template>
+ v-bind:class="['usa-input {% if field.errors %}usa-input--error{% endif %} usa-input--validation--' + validation, { 'usa-input--error': showError, 'usa-input--success': showValid }]">
From a24a8c01ffedcbf0d5baa1b53b57f410860275a0 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Fri, 10 Aug 2018 09:18:28 -0400 Subject: [PATCH 40/88] Add validations to request form inputs --- templates/requests/screen-1.html | 2 +- templates/requests/screen-2.html | 10 +++++----- templates/requests/screen-3.html | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/requests/screen-1.html b/templates/requests/screen-1.html index 43266b04..5f6bbf9f 100644 --- a/templates/requests/screen-1.html +++ b/templates/requests/screen-1.html @@ -33,7 +33,7 @@ {{ OptionsInput(f.organization_providing_assistance) }} {{ OptionsInput(f.engineering_assessment) }} {{ OptionsInput(f.data_transfers) }} -{{ TextInput(f.expected_completion_date, validation='date', placeholder='MM / DD / YYYY') }} +{{ OptionsInput(f.expected_completion_date) }} {{ OptionsInput(f.cloud_native) }}

Financial Usage

diff --git a/templates/requests/screen-2.html b/templates/requests/screen-2.html index be32dc90..ff191fcd 100644 --- a/templates/requests/screen-2.html +++ b/templates/requests/screen-2.html @@ -19,16 +19,16 @@

Please tell us more about you.

-{{ TextInput(f.fname_request,placeholder='First Name') }} -{{ TextInput(f.lname_request,placeholder='Last Name') }} -{{ TextInput(f.email_request,placeholder='jane@mail.mil') }} -{{ TextInput(f.phone_number,placeholder='(123) 456-7890') }} +{{ TextInput(f.fname_request, placeholder='First Name') }} +{{ TextInput(f.lname_request, placeholder='Last Name') }} +{{ TextInput(f.email_request, placeholder='jane@mail.mil', validation='email') }} +{{ TextInput(f.phone_number, placeholder='e.g. (123) 456-7890', validation='usPhone') }}

We want to collect the following information from you for security auditing and determining priviledged user access

{{ TextInput(f.service_branch,placeholder='e.g. US Air Force, US Army, US Navy, Marine Corps, Defense Media Agency') }} {{ OptionsInput(f.citizenship) }} {{ OptionsInput(f.designation) }} -{{ TextInput(f.date_latest_training) }} +{{ TextInput(f.date_latest_training, validation='date') }} {% endblock %} diff --git a/templates/requests/screen-3.html b/templates/requests/screen-3.html index 7de95813..8971215c 100644 --- a/templates/requests/screen-3.html +++ b/templates/requests/screen-3.html @@ -30,7 +30,7 @@ {{ TextInput(f.fname_poc,placeholder='First Name') }} {{ TextInput(f.lname_poc,placeholder='Last Name') }} -{{ TextInput(f.email_poc,placeholder='jane@mail.mil') }} -{{ TextInput(f.dodid_poc,placeholder='10-digit number on the back of the CAC') }} +{{ 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') }} {% endblock %} From 06a2d83dbd8680e110b0570bda736aaffcdea117 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Fri, 10 Aug 2018 09:19:42 -0400 Subject: [PATCH 41/88] Edit labels and descriptions --- atst/forms/org.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/atst/forms/org.py b/atst/forms/org.py index 7fc21986..9ec4036c 100644 --- a/atst/forms/org.py +++ b/atst/forms/org.py @@ -12,9 +12,11 @@ class OrgForm(ValidatedForm): lname_request = StringField("Last Name", validators=[Required(), Alphabet()]) - email_request = EmailField("Email Address", validators=[Required(), Email()]) + email_request = EmailField("E-mail Address", validators=[Required(), Email()]) - phone_number = TelField("Phone Number", validators=[Required(), PhoneNumber()]) + phone_number = TelField("Phone Number", + description='Enter a 10-digit phone number', + validators=[Required(), PhoneNumber()]) service_branch = StringField("Service Branch or Agency", validators=[Required()]) From 239dcb90a13a8b461d1df5ee69f6782ec1a123a2 Mon Sep 17 00:00:00 2001 From: dandds Date: Fri, 10 Aug 2018 11:16:27 -0400 Subject: [PATCH 42/88] autofill user data even if request does not exist yet --- atst/routes/requests/jedi_request_flow.py | 23 ++++++++++------------- atst/routes/requests/requests_form.py | 2 +- tests/routes/test_request_new.py | 11 +++++++++++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/atst/routes/requests/jedi_request_flow.py b/atst/routes/requests/jedi_request_flow.py index fd642478..67a59cc0 100644 --- a/atst/routes/requests/jedi_request_flow.py +++ b/atst/routes/requests/jedi_request_flow.py @@ -33,10 +33,8 @@ class JEDIRequestFlow(object): def _form(self): if self.is_post: return self.form_class()(self.post_data) - elif self.request: - return self.form_class()(data=self.current_step_data) else: - return self.form_class()() + return self.form_class()(data=self.current_step_data) def validate(self): return self.form.validate() @@ -63,15 +61,12 @@ class JEDIRequestFlow(object): # maps user data to fields in OrgForm; this should be moved into the # request initialization process when we have a request schema, or we just # shouldn't record this data on the request - def map_current_user(self): - if self.request: - return { - "fname_request": self.request.creator.first_name, - "lname_request": self.request.creator.last_name, - "email_request": self.request.creator.email - } - else: - return {} + def map_user_data(self, user): + return { + "fname_request": user.first_name, + "lname_request": user.last_name, + "email_request": user.email + } @property def current_step_data(self): @@ -85,9 +80,11 @@ class JEDIRequestFlow(object): data = self.request.body if self.form_section == "information_about_you": form_data = self.request.body.get(self.form_section, {}) - data = { **self.map_current_user(), **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": + data = self.map_user_data(self.current_user) return defaultdict(lambda: defaultdict(lambda: "Input required"), data) diff --git a/atst/routes/requests/requests_form.py b/atst/routes/requests/requests_form.py index 1c6a7c26..50a47217 100644 --- a/atst/routes/requests/requests_form.py +++ b/atst/routes/requests/requests_form.py @@ -9,7 +9,7 @@ from atst.domain.exceptions import UnauthorizedError @requests_bp.route("/requests/new/", methods=["GET"]) def requests_form_new(screen): - jedi_flow = JEDIRequestFlow(screen, request=None) + jedi_flow = JEDIRequestFlow(screen, request=None, current_user=g.current_user) return render_template( "requests/screen-%d.html" % int(screen), diff --git a/tests/routes/test_request_new.py b/tests/routes/test_request_new.py index 06cd47da..7d770a28 100644 --- a/tests/routes/test_request_new.py +++ b/tests/routes/test_request_new.py @@ -80,6 +80,17 @@ def test_creator_info_is_autopopulated(monkeypatch, client, user_session): assert 'value="{}"'.format(user.email) in body +def test_creator_info_is_autopopulated_for_new_request(monkeypatch, client, user_session): + user = UserFactory.create() + user_session(user) + + response = client.get("/requests/new/2") + body = response.data.decode() + assert 'value="{}"'.format(user.first_name) in body + assert 'value="{}"'.format(user.last_name) in body + assert 'value="{}"'.format(user.email) in body + + def test_non_creator_info_is_not_autopopulated(monkeypatch, client, user_session): user = UserFactory.create() creator = UserFactory.create() From 1c101a3c4f0f7a426156198fc32aa60e2c4f84cb Mon Sep 17 00:00:00 2001 From: dandds Date: Fri, 10 Aug 2018 12:05:01 -0400 Subject: [PATCH 43/88] use Requests method to check in pending financial verification --- atst/routes/requests/index.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/atst/routes/requests/index.py b/atst/routes/requests/index.py index dc1d137e..f756836f 100644 --- a/atst/routes/requests/index.py +++ b/atst/routes/requests/index.py @@ -3,7 +3,6 @@ from flask import render_template, g, url_for from . import requests_bp from atst.domain.requests import Requests -from atst.models.request_status_event import RequestStatus def map_request(request): @@ -34,6 +33,6 @@ def requests_index(): mapped_requests = [map_request(r) for r in requests] - pending_fv = any(r["status"] == RequestStatus.PENDING_FINANCIAL_VERIFICATION.value for r in mapped_requests) + pending_fv = any(Requests.is_pending_financial_verification(r) for r in requests) return render_template("requests.html", requests=mapped_requests, pending_financial_verification=pending_fv) From 251bef6bb88585ca06b43ff13d20640ece1a34e0 Mon Sep 17 00:00:00 2001 From: dandds Date: Fri, 10 Aug 2018 12:12:02 -0400 Subject: [PATCH 44/88] move pending financial review text --- templates/components/alert.html | 8 ++++- .../pending_financial_verification.html | 12 +++++++ templates/requests.html | 32 ++----------------- 3 files changed, 21 insertions(+), 31 deletions(-) create mode 100644 templates/fragments/pending_financial_verification.html diff --git a/templates/components/alert.html b/templates/components/alert.html index d3385d1d..1fd30167 100644 --- a/templates/components/alert.html +++ b/templates/components/alert.html @@ -1,6 +1,6 @@ {% from "components/icon.html" import Icon %} -{% macro Alert(title, message=None, actions=None, level='info') -%} +{% macro Alert(title, message=None, actions=None, level='info', fragment=None) -%} {% set role = 'alertdialog' if actions else 'alert' %} {% set levels = { 'warning': { @@ -31,6 +31,12 @@
{{ message | safe }}
{% endif %} + {% if fragment %} +
+ {% include fragment %} +
+ {% endif %} + {% if actions %}
{{ actions | safe }}
{% endif %} diff --git a/templates/fragments/pending_financial_verification.html b/templates/fragments/pending_financial_verification.html new file mode 100644 index 00000000..858b3e26 --- /dev/null +++ b/templates/fragments/pending_financial_verification.html @@ -0,0 +1,12 @@ +

+ The next step is to create a Task Order associated with JEDI Cloud. + Please contact a Contracting Officer (KO), Contracting Officer + Representative (COR), or a Financial Manager to help with this step. +

+

+ Once the Task Order has been created, you will be asked to provide + details about the task order in the Financial Verification step. +

+

+ Learn more about the JEDI Task Order and the Financial Verification process. +

diff --git a/templates/requests.html b/templates/requests.html index 4dc0e865..df6999ca 100644 --- a/templates/requests.html +++ b/templates/requests.html @@ -9,20 +9,7 @@ {% call Modal(name='pendingFinancialVerification', dismissable=True) %}

Request submitted!

-

- The next step is to create a Task Order associated with JEDI Cloud. - Please contact a Contracting Officer (KO), Contracting Officer - Representative (COR), or a Financial Manager to help with this step. -

- -

- Once the Task Order has been created, you will be asked to provide - details about the task order in the Financial Verification step. -

- -

- Learn more about the JEDI Task Order and the Financial Verification process. -

+ {% include 'fragments/pending_financial_verification.html' %}
Close @@ -42,22 +29,7 @@ {% if pending_financial_verification %} - {{ Alert('Pending Financial Verification', - message=" -

- The next step is to create a Task Order associated with JEDI Cloud. - Please contact a Contracting Officer (KO), Contracting Officer - Representative (COR), or a Financial Manager to help with this step. -

-

- Once the Task Order has been created, you will be asked to provide - details about the task order in the Financial Verification step. -

-

- Learn more about the JEDI Task Order and the Financial Verification process. -

- " - ) }} + {{ Alert('Pending Financial Verification', fragment="fragments/pending_financial_verification.html") }} {% endif %} From 4832ecd4192c130d8bd2f5d931167796410ba871 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Fri, 10 Aug 2018 12:42:06 -0400 Subject: [PATCH 45/88] fix mount masking behavior for email fields --- js/components/text_input.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/js/components/text_input.js b/js/components/text_input.js index d377002c..baa07d83 100644 --- a/js/components/text_input.js +++ b/js/components/text_input.js @@ -43,8 +43,12 @@ export default { if (this.value) { this._checkIfValid({ value: this.value, invalidate: true }) - if (this.mask) { - this.value = conformToMask(this.value, this.mask).conformedValue + if (this.mask && this.validation !== 'email') { + const mask = typeof this.mask.mask !== 'function' + ? this.mask + : mask.mask(this.value).filter((val) => val !== '[]') + + this.value = conformToMask(this.value, mask).conformedValue } } }, From cf6c19b28194710e722fd75ef623288ae36c53c7 Mon Sep 17 00:00:00 2001 From: richard-dds Date: Fri, 10 Aug 2018 13:18:06 -0400 Subject: [PATCH 46/88] Only display checkmark if form section is complete --- atst/routes/requests/requests_form.py | 1 + templates/requests/menu.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/atst/routes/requests/requests_form.py b/atst/routes/requests/requests_form.py index d384abdf..efe937cd 100644 --- a/atst/routes/requests/requests_form.py +++ b/atst/routes/requests/requests_form.py @@ -41,6 +41,7 @@ def requests_form_update(screen=1, request_id=None): current=screen, next_screen=screen + 1, request_id=request_id, + jedi_request=jedi_flow.request, can_submit=jedi_flow.can_submit, ) diff --git a/templates/requests/menu.html b/templates/requests/menu.html index d62de246..40de55d3 100644 --- a/templates/requests/menu.html +++ b/templates/requests/menu.html @@ -1,7 +1,7 @@
    {% for s in screens %} - {% if loop.index < current %} + {% if jedi_request and s.section in jedi_request.body %} {% set step_indicator = 'complete' %} {% elif loop.index == current %} {% set step_indicator = 'active' %} From 9d1f816002221728e58fa3bc7f01acbb618808af Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Fri, 10 Aug 2018 13:36:07 -0400 Subject: [PATCH 47/88] Make sure initial server-side errors take precedence and invalidate fields --- js/components/text_input.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/components/text_input.js b/js/components/text_input.js index baa07d83..06eb100c 100644 --- a/js/components/text_input.js +++ b/js/components/text_input.js @@ -72,7 +72,11 @@ export default { // _checkIfValid: function ({ value, invalidate = false}) { // Validate the value - const valid = this._validate(value) + let valid = this._validate(value) + + if (this.initialErrors && this.initialErrors.length) { + valid = false + } // Show error messages or not if (valid) { From cb4f1281d3bce597af801bc4402af8c2cd735de8 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Fri, 10 Aug 2018 13:36:53 -0400 Subject: [PATCH 48/88] bind aria-invalid correctly --- templates/components/text_input.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/components/text_input.html b/templates/components/text_input.html index 99d8be4d..9447f9b6 100644 --- a/templates/components/text_input.html +++ b/templates/components/text_input.html @@ -43,11 +43,11 @@ v-bind:mask='mask' v-bind:pipe='pipe' v-bind:keep-char-positions='keepCharPositions' + v-bind:aria-invalid='showError' id='{{ field.name }}' type='text' ref='input' - placeholder='{{ placeholder }}' - {% if field.errors %}aria-invalid='true'{% endif %}> + placeholder='{{ placeholder }}'> {% endif %} From 168436b1562001614c6b4543062227c7f6e3fca7 Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Fri, 10 Aug 2018 13:46:50 -0400 Subject: [PATCH 49/88] Use multiple date formats in DateRange validator --- atst/domain/date.py | 12 ++++++++++++ atst/forms/fields.py | 12 +++--------- atst/forms/validators.py | 5 +++-- tests/domain/test_date.py | 21 +++++++++++++++++++++ 4 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 atst/domain/date.py create mode 100644 tests/domain/test_date.py diff --git a/atst/domain/date.py b/atst/domain/date.py new file mode 100644 index 00000000..4a131671 --- /dev/null +++ b/atst/domain/date.py @@ -0,0 +1,12 @@ +import pendulum + + +def parse_date(data): + date_formats = ["YYYY-MM-DD", "MM/DD/YYYY"] + for _format in date_formats: + try: + return pendulum.from_format(data, _format).date() + except (ValueError, pendulum.parsing.exceptions.ParserError): + pass + + raise ValueError("Unable to parse string {}".format(data)) diff --git a/atst/forms/fields.py b/atst/forms/fields.py index 00e53529..2c06154a 100644 --- a/atst/forms/fields.py +++ b/atst/forms/fields.py @@ -1,20 +1,14 @@ from wtforms.fields.html5 import DateField from wtforms.fields import Field from wtforms.widgets import TextArea -import pendulum + +from atst.domain.date import parse_date class DateField(DateField): def _value(self): if self.data: - date_formats = ["YYYY-MM-DD", "MM/DD/YYYY"] - for _format in date_formats: - try: - return pendulum.from_format(self.data, _format).date() - except (ValueError, pendulum.parsing.exceptions.ParserError): - pass - - raise ValueError("Unable to parse string {}".format(self.data)) + return parse_date(self.data) else: return None diff --git a/atst/forms/validators.py b/atst/forms/validators.py index 3937dabb..241a5401 100644 --- a/atst/forms/validators.py +++ b/atst/forms/validators.py @@ -2,18 +2,19 @@ import re from wtforms.validators import ValidationError import pendulum +from atst.domain.date import parse_date + def DateRange(lower_bound=None, upper_bound=None, message=None): def _date_range(form, field): now = pendulum.now().date() + date = parse_date(field.data) if lower_bound is not None: - date = pendulum.parse(field.data).date() if (now - lower_bound) > date: raise ValidationError(message) if upper_bound is not None: - date = pendulum.parse(field.data).date() if (now + upper_bound) < date: raise ValidationError(message) diff --git a/tests/domain/test_date.py b/tests/domain/test_date.py new file mode 100644 index 00000000..fe80530d --- /dev/null +++ b/tests/domain/test_date.py @@ -0,0 +1,21 @@ +import pytest +import pendulum + +from atst.domain.date import parse_date + + +def test_date_with_slashes(): + date_str = "1/2/2020" + assert parse_date(date_str) == pendulum.date(2020, 1, 2) + + +def test_date_with_dashes(): + date_str = "2020-1-2" + assert parse_date(date_str) == pendulum.date(2020, 1, 2) + + +def test_invalid_date(): + date_str = "This is not a valid data" + with pytest.raises(ValueError): + parse_date(date_str) + From e734022e50678bcba5360c841e7c00abef728760 Mon Sep 17 00:00:00 2001 From: richard-dds Date: Fri, 10 Aug 2018 13:47:30 -0400 Subject: [PATCH 50/88] Add all atst files to PYTHON_FILES so pylint checks them --- script/test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test b/script/test index bd231db8..d1e22bbf 100755 --- a/script/test +++ b/script/test @@ -10,7 +10,7 @@ export FLASK_ENV=test RESET_DB="true" # Define all relevant python files and directories for this app -PYTHON_FILES="./app.py ./atst ./config" +PYTHON_FILES="./app.py ./atst/** ./config" # Enable Python testing RUN_PYTHON_TESTS="true" From 99b9579cd96e99e673cc9f78e6ab0a4f57a5cec2 Mon Sep 17 00:00:00 2001 From: richard-dds Date: Fri, 10 Aug 2018 13:47:46 -0400 Subject: [PATCH 51/88] Fix linting errors --- atst/forms/financial.py | 3 +-- atst/forms/poc.py | 2 +- atst/forms/request.py | 5 +---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/atst/forms/financial.py b/atst/forms/financial.py index 674eace4..994e84bf 100644 --- a/atst/forms/financial.py +++ b/atst/forms/financial.py @@ -1,7 +1,6 @@ import re from wtforms.fields.html5 import EmailField from wtforms.fields import StringField, SelectField -from wtforms.form import Form from wtforms.validators import Required, Email from atst.domain.exceptions import NotFoundError @@ -41,7 +40,7 @@ def suggest_pe_id(pe_id): def validate_pe_id(field, existing_request): try: - pe_number = PENumbers.get(field.data) + PENumbers.get(field.data) except NotFoundError: suggestion = suggest_pe_id(field.data) error_str = ( diff --git a/atst/forms/poc.py b/atst/forms/poc.py index 66b77064..c595097b 100644 --- a/atst/forms/poc.py +++ b/atst/forms/poc.py @@ -2,7 +2,7 @@ from wtforms.fields import StringField from wtforms.fields.html5 import EmailField from wtforms.validators import Required, Email, Length from .forms import ValidatedForm -from .validators import IsNumber, Alphabet +from .validators import IsNumber class POCForm(ValidatedForm): diff --git a/atst/forms/request.py b/atst/forms/request.py index fb179587..a59aaf19 100644 --- a/atst/forms/request.py +++ b/atst/forms/request.py @@ -1,10 +1,7 @@ from wtforms.fields.html5 import IntegerField -from wtforms.fields import RadioField, StringField, TextAreaField, SelectField -from wtforms.validators import NumberRange, InputRequired +from wtforms.fields import RadioField, TextAreaField, SelectField from .fields import DateField from .forms import ValidatedForm -from .validators import DateRange -import pendulum class RequestForm(ValidatedForm): From 7820ef589afd1104e2e52dc4d8d071e28fd19820 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Fri, 10 Aug 2018 13:50:19 -0400 Subject: [PATCH 52/88] add date field placeholder --- templates/requests/screen-2.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/requests/screen-2.html b/templates/requests/screen-2.html index ff191fcd..50dc9007 100644 --- a/templates/requests/screen-2.html +++ b/templates/requests/screen-2.html @@ -29,6 +29,6 @@ {{ TextInput(f.service_branch,placeholder='e.g. US Air Force, US Army, US Navy, Marine Corps, Defense Media Agency') }} {{ OptionsInput(f.citizenship) }} {{ OptionsInput(f.designation) }} -{{ TextInput(f.date_latest_training, validation='date') }} +{{ TextInput(f.date_latest_training, placeholder='MM / DD / YYYY', validation='date') }} {% endblock %} From e2821bc67641c6d9663b43f4ed8df700cd09a83c Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Fri, 10 Aug 2018 14:03:18 -0400 Subject: [PATCH 53/88] add a modified var, to allow fields to re-validate when there are existing errors --- js/components/text_input.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/components/text_input.js b/js/components/text_input.js index 06eb100c..60e9021f 100644 --- a/js/components/text_input.js +++ b/js/components/text_input.js @@ -29,7 +29,8 @@ export default { pipe: inputValidations[this.validation].pipe || undefined, keepCharPositions: inputValidations[this.validation].keepCharPositions || false, validationError: inputValidations[this.validation].validationError || '', - value: this.initialValue + value: this.initialValue, + modified: false } }, @@ -60,6 +61,7 @@ export default { // When we use the masked-input component, we receive the value directly const value = typeof e === 'object' ? e.target.value : e this.value = value + this.modified = true this._checkIfValid({ value }) }, @@ -74,7 +76,7 @@ export default { // Validate the value let valid = this._validate(value) - if (this.initialErrors && this.initialErrors.length) { + if (!this.modified && this.initialErrors && this.initialErrors.length) { valid = false } From c261c445d6baa77410793623af24291d2d1e7305 Mon Sep 17 00:00:00 2001 From: Andrew Croce Date: Fri, 10 Aug 2018 15:47:28 -0400 Subject: [PATCH 54/88] fix error rendering --- templates/components/text_input.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/components/text_input.html b/templates/components/text_input.html index 9447f9b6..33dfb29e 100644 --- a/templates/components/text_input.html +++ b/templates/components/text_input.html @@ -55,8 +55,8 @@
From 0f4893242bf547ce1510aaf98ea83699d7fe0939 Mon Sep 17 00:00:00 2001 From: Patrick Smith Date: Sun, 12 Aug 2018 12:16:19 -0400 Subject: [PATCH 55/88] Fix tests --- tests/routes/test_request_new.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/routes/test_request_new.py b/tests/routes/test_request_new.py index 7d770a28..d537cc4d 100644 --- a/tests/routes/test_request_new.py +++ b/tests/routes/test_request_new.py @@ -75,9 +75,9 @@ def test_creator_info_is_autopopulated(monkeypatch, client, user_session): response = client.get("/requests/new/2/{}".format(request.id)) body = response.data.decode() - assert 'value="{}"'.format(user.first_name) in body - assert 'value="{}"'.format(user.last_name) in body - assert 'value="{}"'.format(user.email) in body + assert "initial-value='{}'".format(user.first_name) in body + assert "initial-value='{}'".format(user.last_name) in body + assert "initial-value='{}'".format(user.email) in body def test_creator_info_is_autopopulated_for_new_request(monkeypatch, client, user_session): @@ -86,9 +86,9 @@ def test_creator_info_is_autopopulated_for_new_request(monkeypatch, client, user response = client.get("/requests/new/2") body = response.data.decode() - assert 'value="{}"'.format(user.first_name) in body - assert 'value="{}"'.format(user.last_name) in body - assert 'value="{}"'.format(user.email) in body + assert "initial-value='{}'".format(user.first_name) in body + assert "initial-value='{}'".format(user.last_name) in body + assert "initial-value='{}'".format(user.email) in body def test_non_creator_info_is_not_autopopulated(monkeypatch, client, user_session): From 93dd128c4d60290fc9813bcc47cb3c8021e8d655 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 13:55:30 -0400 Subject: [PATCH 56/88] Switch to language:minimal since everything is in a container --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 94b83396..47168f0a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ sudo: required -language: python -python: "3.6" +language: minimal services: - docker git: From bad0e1f098aff9987c6feb141abaf29be6f45a93 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 14:06:26 -0400 Subject: [PATCH 57/88] Add script for managing client ca bundle --- deploy/kubernetes/set_clientca_secret.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100755 deploy/kubernetes/set_clientca_secret.sh diff --git a/deploy/kubernetes/set_clientca_secret.sh b/deploy/kubernetes/set_clientca_secret.sh new file mode 100755 index 00000000..b27fbb2d --- /dev/null +++ b/deploy/kubernetes/set_clientca_secret.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +kubectl -n atat delete secret atst-config-ini +kubectl -n atat create secret generic nginx-client-ca-bundle --from-file="${1}" From 5bd39e1a152e3c8223895d11652e6c597b8d6e31 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 14:06:43 -0400 Subject: [PATCH 58/88] Add client ca bundle to nginx container --- deploy/kubernetes/atst.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/deploy/kubernetes/atst.yml b/deploy/kubernetes/atst.yml index c302f8af..773dbf37 100644 --- a/deploy/kubernetes/atst.yml +++ b/deploy/kubernetes/atst.yml @@ -47,6 +47,9 @@ spec: volumeMounts: - name: nginx-auth-tls mountPath: "/etc/ssl/private" + - name: nginx-client-ca-bundle + mountPath: "/etc/ssl/client-ca-bundle.pem" + subPath: client-ca-bundle.pem - name: nginx-config mountPath: "/etc/nginx/conf.d/atst.conf" subPath: atst.conf @@ -78,6 +81,13 @@ spec: - key: tls.key path: auth.atat.key mode: 0640 + - name: nginx-ca-bundle + secret: + secretName: nginx-client-ca-bundle + items: + - key: client-ca-bundle.pem + path: client-ca-bundle.pem + mode: 0666 - name: nginx-config configMap: name: atst-nginx From c00db63f40df52fefbafb4b65ba6cad77a99d80d Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 14:06:57 -0400 Subject: [PATCH 59/88] Enable client cert validation --- deploy/kubernetes/atst-nginx-configmap.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/kubernetes/atst-nginx-configmap.yml b/deploy/kubernetes/atst-nginx-configmap.yml index 6e2b1d69..29133d4d 100644 --- a/deploy/kubernetes/atst-nginx-configmap.yml +++ b/deploy/kubernetes/atst-nginx-configmap.yml @@ -55,9 +55,9 @@ data: ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4; # Request and validate client certificate - #ssl_verify_client on; - #ssl_verify_depth 10; - #ssl_client_certificate /etc/nginx/ssl/ca/client-ca.pem; + ssl_verify_client on; + ssl_verify_depth 10; + ssl_client_certificate /etc/nginx/ssl/client-ca-bundle.pem; # Guard against HTTPS -> HTTP downgrade add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; always"; location / { From 323eefd121e118c3c8e31df9393ddcdcdfeaaa8b Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 14:09:52 -0400 Subject: [PATCH 60/88] Update code version deployed --- deploy/kubernetes/atst.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/kubernetes/atst.yml b/deploy/kubernetes/atst.yml index 773dbf37..296e6b3f 100644 --- a/deploy/kubernetes/atst.yml +++ b/deploy/kubernetes/atst.yml @@ -24,7 +24,7 @@ spec: fsGroup: 101 containers: - name: atst - image: registry.atat.codes:443/atst-prod:e9b6f76 + image: registry.atat.codes:443/atst-prod:2030b4d envFrom: - configMapRef: name: atst-envvars From be653fd702c34a1b26d1237d68dd78dcea39a126 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 14:11:22 -0400 Subject: [PATCH 61/88] Fix reference --- deploy/kubernetes/atst.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/kubernetes/atst.yml b/deploy/kubernetes/atst.yml index 296e6b3f..a055580c 100644 --- a/deploy/kubernetes/atst.yml +++ b/deploy/kubernetes/atst.yml @@ -81,7 +81,7 @@ spec: - key: tls.key path: auth.atat.key mode: 0640 - - name: nginx-ca-bundle + - name: nginx-client-ca-bundle secret: secretName: nginx-client-ca-bundle items: From e098dc86193e0730b0f490384343b8eca4e93cbf Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 14:18:13 -0400 Subject: [PATCH 62/88] Fix file path --- deploy/kubernetes/atst-nginx-configmap.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/kubernetes/atst-nginx-configmap.yml b/deploy/kubernetes/atst-nginx-configmap.yml index 29133d4d..553bb30b 100644 --- a/deploy/kubernetes/atst-nginx-configmap.yml +++ b/deploy/kubernetes/atst-nginx-configmap.yml @@ -57,7 +57,7 @@ data: # Request and validate client certificate ssl_verify_client on; ssl_verify_depth 10; - ssl_client_certificate /etc/nginx/ssl/client-ca-bundle.pem; + ssl_client_certificate /etc/ssl/client-ca-bundle.pem; # Guard against HTTPS -> HTTP downgrade add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; always"; location / { From bfe3e18531a5a2c29653ffa560a698e4f8f33353 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 14:45:05 -0400 Subject: [PATCH 63/88] Update atst code version --- deploy/kubernetes/atst.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/kubernetes/atst.yml b/deploy/kubernetes/atst.yml index a055580c..141cc86b 100644 --- a/deploy/kubernetes/atst.yml +++ b/deploy/kubernetes/atst.yml @@ -24,7 +24,7 @@ spec: fsGroup: 101 containers: - name: atst - image: registry.atat.codes:443/atst-prod:2030b4d + image: registry.atat.codes:443/atst-prod:93b9317 envFrom: - configMapRef: name: atst-envvars From 60d3379fe7e18cc7a40dd86d4f9b80da4bd882af Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 14:58:46 -0400 Subject: [PATCH 64/88] Sync CRLs and cache them --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 47168f0a..bcd1fa52 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,10 @@ before_script: script: - docker run --add-host "postgreshost:${postgres_ip}" --add-host "redishost:${redis_ip}" "${TESTER_IMAGE_NAME}" + - docker run -d --entrypoint='/bin/sh' --name current-atst-tester "${TESTER_IMAGE_NAME}" + - docker container exec -t current-atst-tester script/sync-crls + - docker cp current-atst-tester:crl ./crl + - docker container stop current-atst-tester before_deploy: - docker build --tag "${PROD_IMAGE_NAME}" . -f deploy/docker/prod/Dockerfile From 1fb037a6be4d1e2c38cce84fc97afd47ebdb423b Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 15:18:46 -0400 Subject: [PATCH 65/88] Add tty so container does not automatically stop right away --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bcd1fa52..c3fa7dcd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ before_script: script: - docker run --add-host "postgreshost:${postgres_ip}" --add-host "redishost:${redis_ip}" "${TESTER_IMAGE_NAME}" - - docker run -d --entrypoint='/bin/sh' --name current-atst-tester "${TESTER_IMAGE_NAME}" + - docker run -d --entrypoint='/bin/sh' -t --name current-atst-tester "${TESTER_IMAGE_NAME}" - docker container exec -t current-atst-tester script/sync-crls - docker cp current-atst-tester:crl ./crl - docker container stop current-atst-tester From 4168956f882ddb0c1d31e0f0b3642f66ab58c268 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 16:04:34 -0400 Subject: [PATCH 66/88] Add rsync package for sync-crl script --- script/alpine_setup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/alpine_setup b/script/alpine_setup index b9eeb9a7..41096326 100755 --- a/script/alpine_setup +++ b/script/alpine_setup @@ -10,7 +10,7 @@ APP_USER="atst" APP_UID="8010" # Add additional packages required by app dependencies -ADDITIONAL_PACKAGES="postgresql-libs python3 uwsgi uwsgi-python3" +ADDITIONAL_PACKAGES="postgresql-libs python3 rsync uwsgi uwsgi-python3" # Run the shared alpine setup script source ./script/include/run_alpine_setup From 6a3b569c09e73531b3c86ee74b1fd825172bfdd6 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 16:11:54 -0400 Subject: [PATCH 67/88] Update crl copy to use full path --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c3fa7dcd..21de5c1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ script: - docker run --add-host "postgreshost:${postgres_ip}" --add-host "redishost:${redis_ip}" "${TESTER_IMAGE_NAME}" - docker run -d --entrypoint='/bin/sh' -t --name current-atst-tester "${TESTER_IMAGE_NAME}" - docker container exec -t current-atst-tester script/sync-crls - - docker cp current-atst-tester:crl ./crl + - docker cp current-atst-tester:/opt/atat/atst/crl ./crl - docker container stop current-atst-tester before_deploy: From 8c52e53679d9141398e0427714c676bbb97b8361 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 16:22:14 -0400 Subject: [PATCH 68/88] Cache the crl subdir --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 21de5c1f..0f1ddfe1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,9 @@ env: global: - TESTER_IMAGE_NAME=atst-tester - PROD_IMAGE_NAME=atst-prod +cache: + directories: + - crl before_install: # Use sed to replace the SSH URL with the public URL From a20df689f8f91279e2bb47536ca40b30468e902d Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 16:39:08 -0400 Subject: [PATCH 69/88] Save container with CRLs added --- .travis.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f1ddfe1..defa6b90 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,9 @@ git: submodules: false env: global: - - TESTER_IMAGE_NAME=atst-tester - PROD_IMAGE_NAME=atst-prod + - TESTER_IMAGE1_NAME=atst-tester-nocrls + - TESTER_IMAGE2_NAME=atst-tester cache: directories: - crl @@ -25,14 +26,15 @@ before_script: - export postgres_ip="$(docker inspect -f "{{ .NetworkSettings.IPAddress }}" postgres96)" - export redis_ip="$(docker inspect -f "{{ .NetworkSettings.IPAddress }}" redis)" - docker login -u $ATAT_DOCKER_REGISTRY_USERNAME -p $ATAT_DOCKER_REGISTRY_PASSWORD $ATAT_DOCKER_REGISTRY_URL - - docker build --tag "${TESTER_IMAGE_NAME}" --add-host "postgreshost:${postgres_ip}" --add-host "redishost:${redis_ip}" . -f deploy/docker/tester/Dockerfile + - docker build --tag "${TESTER_IMAGE1_NAME}" --add-host "postgreshost:${postgres_ip}" --add-host "redishost:${redis_ip}" . -f deploy/docker/tester/Dockerfile script: - - docker run --add-host "postgreshost:${postgres_ip}" --add-host "redishost:${redis_ip}" "${TESTER_IMAGE_NAME}" - - docker run -d --entrypoint='/bin/sh' -t --name current-atst-tester "${TESTER_IMAGE_NAME}" + - docker run -d --entrypoint='/bin/sh' -t --name current-atst-tester "${TESTER_IMAGE1_NAME}" - docker container exec -t current-atst-tester script/sync-crls + - docker commit current-atst-tester "${TESTER_IMAGE2_NAME}" - docker cp current-atst-tester:/opt/atat/atst/crl ./crl - docker container stop current-atst-tester + - docker run --add-host "postgreshost:${postgres_ip}" --add-host "redishost:${redis_ip}" "${TESTER_IMAGE2_NAME}" before_deploy: - docker build --tag "${PROD_IMAGE_NAME}" . -f deploy/docker/prod/Dockerfile From 0619e020424f6a95a001d819d6e4277d2e42f55f Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Wed, 8 Aug 2018 17:31:37 -0400 Subject: [PATCH 70/88] Pull ca-chain.pem from Kubernetes --- deploy/kubernetes/atst.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deploy/kubernetes/atst.yml b/deploy/kubernetes/atst.yml index 141cc86b..de812fa3 100644 --- a/deploy/kubernetes/atst.yml +++ b/deploy/kubernetes/atst.yml @@ -32,6 +32,9 @@ spec: - name: atst-config mountPath: "/opt/atat/atst/atst-overrides.ini" subPath: atst-overrides.ini + - name: nginx-client-ca-bundle + mountPath: "/opt/atat/atst/ssl/server-certs/ca-chain.pem" + subPath: client-ca-bundle.pem - name: uwsgi-config mountPath: "/opt/atat/atst/uwsgi-config.ini" subPath: uwsgi-config.ini From bc0521fba9695acc1c2ebcfffc1cf60642eeabd9 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Thu, 9 Aug 2018 08:36:08 -0400 Subject: [PATCH 71/88] Remove deploy branch restriction to push testable image --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index defa6b90..8071ea06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,4 +48,4 @@ deploy: provider: script script: echo "** Image push only for now... stay tuned! **" on: - branch: master + all_branches: true From 6f1cd6276d868338e3c398bceb5a6ec490bdb879 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Fri, 10 Aug 2018 17:40:34 -0400 Subject: [PATCH 72/88] Update deployed code version --- deploy/kubernetes/atst.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/kubernetes/atst.yml b/deploy/kubernetes/atst.yml index de812fa3..f62a0eca 100644 --- a/deploy/kubernetes/atst.yml +++ b/deploy/kubernetes/atst.yml @@ -24,7 +24,7 @@ spec: fsGroup: 101 containers: - name: atst - image: registry.atat.codes:443/atst-prod:93b9317 + image: registry.atat.codes:443/atst-prod:5ac3343 envFrom: - configMapRef: name: atst-envvars From ed3a49a8279b8e833c58b5c46cd2c6291e8e9df4 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Sat, 11 Aug 2018 13:12:33 -0400 Subject: [PATCH 73/88] Fix crl file copying Old syntax copied the container crl directory into the local crl directory as a subdir, resulting in the content being in ./crl/crl/ --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8071ea06..27b95c6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ script: - docker run -d --entrypoint='/bin/sh' -t --name current-atst-tester "${TESTER_IMAGE1_NAME}" - docker container exec -t current-atst-tester script/sync-crls - docker commit current-atst-tester "${TESTER_IMAGE2_NAME}" - - docker cp current-atst-tester:/opt/atat/atst/crl ./crl + - docker cp current-atst-tester:/opt/atat/atst/crl/* ./crl/ - docker container stop current-atst-tester - docker run --add-host "postgreshost:${postgres_ip}" --add-host "redishost:${redis_ip}" "${TESTER_IMAGE2_NAME}" From ac1f403313be681a785d324e649a4a7369e976d1 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Sat, 11 Aug 2018 13:15:09 -0400 Subject: [PATCH 74/88] Purge the CRL cache directory --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 27b95c6c..28b540f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ before_install: - git submodule update --init --recursive before_script: + - rm -rf ./crl/* - docker run -d --name postgres96 postgres:9.6-alpine - docker run -d --name redis redis:4.0.10-alpine - docker run --link postgres96:postgres96 --link redis:redis waisbrot/wait From 094bb1467a19aabba2564e290c7303c8674951b1 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Sat, 11 Aug 2018 13:38:54 -0400 Subject: [PATCH 75/88] Fix syntax (docker uses . not * for dir contents) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 28b540f0..fca656f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ script: - docker run -d --entrypoint='/bin/sh' -t --name current-atst-tester "${TESTER_IMAGE1_NAME}" - docker container exec -t current-atst-tester script/sync-crls - docker commit current-atst-tester "${TESTER_IMAGE2_NAME}" - - docker cp current-atst-tester:/opt/atat/atst/crl/* ./crl/ + - docker cp current-atst-tester:/opt/atat/atst/crl/. ./crl/ - docker container stop current-atst-tester - docker run --add-host "postgreshost:${postgres_ip}" --add-host "redishost:${redis_ip}" "${TESTER_IMAGE2_NAME}" From d741c4e37ce812fb3bd29b5013110f0df14cb15e Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Sun, 12 Aug 2018 13:53:15 -0400 Subject: [PATCH 76/88] Add pod spec for debuggable ATST container --- deploy/kubernetes/atst-debugger.yml | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 deploy/kubernetes/atst-debugger.yml diff --git a/deploy/kubernetes/atst-debugger.yml b/deploy/kubernetes/atst-debugger.yml new file mode 100644 index 00000000..d86abd0f --- /dev/null +++ b/deploy/kubernetes/atst-debugger.yml @@ -0,0 +1,43 @@ +apiVersion: v1 +kind: Pod +metadata: + name: atst-debugger + namespace: atat +spec: + securityContext: + fsGroup: 101 + containers: + - name: atst-debugger + image: registry.atat.codes:443/atst-prod:a1916b1 + args: ["/bin/bash", "-c", "while true; do date; sleep 45; done"] + envFrom: + - configMapRef: + name: atst-envvars + volumeMounts: + - name: atst-config + mountPath: "/opt/atat/atst/atst-overrides.ini" + subPath: atst-overrides.ini + - name: uwsgi-config + mountPath: "/opt/atat/atst/uwsgi-config.ini" + subPath: uwsgi-config.ini + - name: uwsgi-socket-dir + mountPath: "/var/run/uwsgi" + volumes: + - name: atst-config + secret: + secretName: atst-config-ini + items: + - key: atst-overrides.ini + path: atst-overrides.ini + mode: 0644 + - name: uwsgi-config + configMap: + name: atst-config + items: + - key: uwsgi-config + path: uwsgi-config.ini + mode: 0644 + - name: uwsgi-socket-dir + emptyDir: + medium: Memory + restartPolicy: Never From b18c853f62a8186f48f5b8565b667534334fa662 Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Sun, 12 Aug 2018 14:09:05 -0400 Subject: [PATCH 77/88] Update code version and add memory requirement --- deploy/kubernetes/atst.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deploy/kubernetes/atst.yml b/deploy/kubernetes/atst.yml index f62a0eca..c62d7a6a 100644 --- a/deploy/kubernetes/atst.yml +++ b/deploy/kubernetes/atst.yml @@ -24,7 +24,10 @@ spec: fsGroup: 101 containers: - name: atst - image: registry.atat.codes:443/atst-prod:5ac3343 + image: registry.atat.codes:443/atst-prod:a1916b1 + resources: + requests: + memory: "2500Mi" envFrom: - configMapRef: name: atst-envvars From 8c43b60bba05dc78ddeb7b7b062ec49e873bfebb Mon Sep 17 00:00:00 2001 From: Devon Mackay Date: Sun, 12 Aug 2018 14:14:11 -0400 Subject: [PATCH 78/88] Revert container publishing to master branch only --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fca656f6..5a0734c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,4 +49,4 @@ deploy: provider: script script: echo "** Image push only for now... stay tuned! **" on: - all_branches: true + branch: master From 1eb8e79f41a8d9a152d46eaa5445c37470f5fd85 Mon Sep 17 00:00:00 2001 From: dandds Date: Mon, 13 Aug 2018 09:44:32 -0400 Subject: [PATCH 79/88] fix bug in request form review step, add more tests --- atst/routes/requests/jedi_request_flow.py | 2 +- tests/routes/test_request_new.py | 10 +++++ tests/test_integration.py | 45 ++++++++++++----------- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/atst/routes/requests/jedi_request_flow.py b/atst/routes/requests/jedi_request_flow.py index 67a59cc0..6623a909 100644 --- a/atst/routes/requests/jedi_request_flow.py +++ b/atst/routes/requests/jedi_request_flow.py @@ -78,7 +78,7 @@ class JEDIRequestFlow(object): if self.request: if self.form_section == "review_submit": data = self.request.body - if self.form_section == "information_about_you": + 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 } else: diff --git a/tests/routes/test_request_new.py b/tests/routes/test_request_new.py index 7d770a28..d3120a6a 100644 --- a/tests/routes/test_request_new.py +++ b/tests/routes/test_request_new.py @@ -102,3 +102,13 @@ def test_non_creator_info_is_not_autopopulated(monkeypatch, client, user_session assert not user.first_name in body assert not user.last_name in body assert not user.email in body + +def test_can_review_data(user_session, client): + creator = UserFactory.create() + user_session(creator) + request = RequestFactory.create(creator=creator) + response = client.get("/requests/new/4/{}".format(request.id)) + body = response.data.decode() + # assert a sampling of the request data is on the review page + assert request.body["primary_poc"]["fname_poc"] in body + assert request.body["information_about_you"]["email_request"] in body diff --git a/tests/test_integration.py b/tests/test_integration.py index e8fa6348..4788e4c4 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1,5 +1,5 @@ import pytest -from tests.mocks import MOCK_USER +from .factories import UserFactory from atst.routes.requests.jedi_request_flow import JEDIRequestFlow @pytest.fixture @@ -7,40 +7,43 @@ def screens(app): return JEDIRequestFlow(3).screens -@pytest.mark.skip() -def test_stepthrough_request_form(monkeypatch, screens, client): +def test_stepthrough_request_form(monkeypatch, user_session, screens, client): + user = UserFactory.create() + user_session(user) + monkeypatch.setattr( - "atst.handlers.request_new.RequestNew.get_current_user", lambda s: MOCK_USER - ) - monkeypatch.setattr( - "atst.handlers.request_new.RequestNew.check_xsrf_cookie", lambda s: True - ) - monkeypatch.setattr( - "atst.handlers.request_new.JEDIRequestFlow.validate", lambda s: True + "atst.routes.requests.jedi_request_flow.JEDIRequestFlow.validate", lambda s: True ) + def post_form(url, redirects=False): + return client.post( + url, + headers={"Content-Type": "application/x-www-form-urlencoded"}, + data="meaning=42", + follow_redirects=redirects + ) + + def take_a_step(inc, req=None): req_url = "/requests/new/{}".format(inc) if req: req_url += "/" + req - response = client.post( - req_url, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - data="meaning=42", - ) - return response + # we do it twice, with and without redirect, in order to get the + # destination url + prelim_resp = post_form(req_url) + response = post_form(req_url, True) + return (prelim_resp.headers.get("Location"), response) # GET the initial form - response = client.get("/requests/new") + response = client.get("/requests/new/1") assert screens[0]["title"] in response.data.decode() # POST to each of the form pages up until review and submit req_id = None for i in range(1, len(screens)): - resp = take_a_step(i, req=req_id) - __import__('ipdb').set_trace() - req_id = resp.effective_url.split("/")[-1] + effective_url, resp = take_a_step(i, req=req_id) + req_id = effective_url.split("/")[-1] screen_title = screens[i]["title"].replace("&", "&") - assert "/requests/new/{}/{}".format(i + 1, req_id) in resp.effective_url + assert "/requests/new/{}/{}".format(i + 1, req_id) in effective_url assert screen_title in resp.data.decode() From 654f9231b5171f60346139bc85048b1eafbe0d16 Mon Sep 17 00:00:00 2001 From: dandds Date: Mon, 13 Aug 2018 10:11:07 -0400 Subject: [PATCH 80/88] show modal for requests pending CCPO approval --- atst/routes/requests/requests_form.py | 2 +- js/index.js | 1 + .../pending_ccpo_approval_alert.html | 11 ++++++ .../pending_ccpo_approval_modal.html | 35 +++++++++++++++++++ templates/requests.html | 9 +++++ 5 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 templates/fragments/pending_ccpo_approval_alert.html create mode 100644 templates/fragments/pending_ccpo_approval_modal.html diff --git a/atst/routes/requests/requests_form.py b/atst/routes/requests/requests_form.py index ca474d61..a8c2798f 100644 --- a/atst/routes/requests/requests_form.py +++ b/atst/routes/requests/requests_form.py @@ -104,7 +104,7 @@ def requests_submit(request_id=None): return redirect("/requests?modal=pendingFinancialVerification") else: - return redirect("/requests") + return redirect("/requests?modal=pendingCCPOApproval") # TODO: generalize this, along with other authorizations, into a policy-pattern diff --git a/js/index.js b/js/index.js index 07da8d42..b13d1a6a 100644 --- a/js/index.js +++ b/js/index.js @@ -21,6 +21,7 @@ const app = new Vue({ modals: { styleguideModal: false, pendingFinancialVerification: false, + pendingCCPOApproval: false, } } }, diff --git a/templates/fragments/pending_ccpo_approval_alert.html b/templates/fragments/pending_ccpo_approval_alert.html new file mode 100644 index 00000000..1d2e9255 --- /dev/null +++ b/templates/fragments/pending_ccpo_approval_alert.html @@ -0,0 +1,11 @@ +

+ We will review and respond to your request in 72 hours. You’ll be notified via email or phone. +

+ +

+ While your request is being reviewed, your next step is to create a Task Order associated with JEDI Cloud. Please contact a Contracting Officer (KO), Contracting Officer Representative (COR), or a Financial Manager to help with this step. +

+ +

+ Learn more about the JEDI Task Order and the Financial Verification process. +

diff --git a/templates/fragments/pending_ccpo_approval_modal.html b/templates/fragments/pending_ccpo_approval_modal.html new file mode 100644 index 00000000..68a48c2d --- /dev/null +++ b/templates/fragments/pending_ccpo_approval_modal.html @@ -0,0 +1,35 @@ +

+ Request submitted. Approval pending. +

+ +

+ We will review and respond to your request in 72 hours. You’ll be notified via email or phone. +

+ +

+ Your request is being reviewed because: +

    +
  • + Your request includes over $1 million for cloud resources +
  • +
  • + We may need more information about your request +
  • +
+

+ +

+ Next Steps +

+ +

+ While your request is being reviewed, your next step is to create a Task Order associated with JEDI Cloud. Please contact a Contracting Officer (KO), Contracting Officer Representative (COR), or a Financial Manager to help with this step. +

+ +

+ Once the Task Order has been created, you will be asked to provide details about the task order in the Financial Verification step. +

+ +

+ Learn more about the JEDI Task Order and the Financial Verification process. +

diff --git a/templates/requests.html b/templates/requests.html index df6999ca..6b1e36f0 100644 --- a/templates/requests.html +++ b/templates/requests.html @@ -16,6 +16,15 @@
{% endcall %} + {% call Modal(name='pendingCCPOApproval', dismissable=True) %} + + {% include 'fragments/pending_ccpo_approval_modal.html' %} + +
+ Close +
+ {% endcall %} + {% if not requests %} {{ EmptyState( From 37bb0c5d303769a13cb6189001872bb92d16aea4 Mon Sep 17 00:00:00 2001 From: dandds Date: Mon, 13 Aug 2018 10:22:35 -0400 Subject: [PATCH 81/88] display alert on requests index page when requests are pending CCPO approval --- atst/routes/requests/index.py | 23 ++++++++++++++++++----- templates/requests.html | 6 ++++++ tests/routes/test_request_submit.py | 4 ++-- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/atst/routes/requests/index.py b/atst/routes/requests/index.py index f756836f..57e7e63a 100644 --- a/atst/routes/requests/index.py +++ b/atst/routes/requests/index.py @@ -9,8 +9,10 @@ def map_request(request): time_created = pendulum.instance(request.time_created) is_new = time_created.add(days=1) > pendulum.now() app_count = request.body.get("details_of_use", {}).get("num_software_systems", 0) - update_url = url_for('requests.requests_form_update', screen=1, request_id=request.id) - verify_url = url_for('requests.financial_verification', request_id=request.id) + update_url = url_for( + "requests.requests_form_update", screen=1, request_id=request.id + ) + verify_url = url_for("requests.financial_verification", request_id=request.id) return { "order_id": request.id, @@ -19,7 +21,9 @@ def map_request(request): "app_count": app_count, "date": time_created.format("M/DD/YYYY"), "full_name": request.creator.full_name, - "edit_link": verify_url if Requests.is_pending_financial_verification(request) else update_url + "edit_link": verify_url if Requests.is_pending_financial_verification( + request + ) else update_url, } @@ -33,6 +37,15 @@ def requests_index(): mapped_requests = [map_request(r) for r in requests] - pending_fv = any(Requests.is_pending_financial_verification(r) for r in requests) + pending_fv_count = [ + True for r in requests if Requests.is_pending_financial_verification(r) + ] + pending_fv = len(pending_fv_count) > 1 + pending_ccpo = len(pending_fv_count) != len(mapped_requests) - return render_template("requests.html", requests=mapped_requests, pending_financial_verification=pending_fv) + return render_template( + "requests.html", + requests=mapped_requests, + pending_financial_verification=pending_fv, + pending_ccpo_approval=pending_ccpo, + ) diff --git a/templates/requests.html b/templates/requests.html index 6b1e36f0..4f8c5cc1 100644 --- a/templates/requests.html +++ b/templates/requests.html @@ -42,6 +42,12 @@ {% endif %} + {% if pending_ccpo_approval %} + + {{ Alert('Request submitted. Approval pending.', fragment="fragments/pending_ccpo_approval_alert.html") }} + + {% endif %} +