diff --git a/.secrets.baseline b/.secrets.baseline
index 8926613d..717bf9b4 100644
--- a/.secrets.baseline
+++ b/.secrets.baseline
@@ -3,7 +3,7 @@
"files": null,
"lines": null
},
- "generated_at": "2019-08-13T09:59:21Z",
+ "generated_at": "2019-08-14T14:39:14Z",
"plugins_used": [
{
"base64_limit": 4.5,
@@ -169,7 +169,7 @@
"hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207",
"is_secret": false,
"is_verified": false,
- "line_number": 30,
+ "line_number": 32,
"type": "Hex High Entropy String"
}
],
diff --git a/atst/forms/task_order.py b/atst/forms/task_order.py
index c9c9e19b..47323319 100644
--- a/atst/forms/task_order.py
+++ b/atst/forms/task_order.py
@@ -7,7 +7,7 @@ from wtforms.fields import (
HiddenField,
)
from wtforms.fields.html5 import DateField
-from wtforms.validators import Required, Optional
+from wtforms.validators import Required, Optional, Length
from flask_wtf import FlaskForm
from .data import JEDI_CLIN_TYPES
@@ -65,10 +65,25 @@ class CLINForm(FlaskForm):
class AttachmentForm(BaseForm):
- filename = HiddenField(id="attachment_filename")
- object_name = HiddenField(id="attachment_object_name")
+ filename = HiddenField(
+ id="attachment_filename",
+ validators=[
+ Length(max=100, message=translate("forms.attachment.filename.length_error"))
+ ],
+ )
+ object_name = HiddenField(
+ id="attachment_object_name",
+ validators=[
+ Length(
+ max=40, message=translate("forms.attachment.object_name.length_error")
+ )
+ ],
+ )
accept = ".pdf,application/pdf"
+ def validate(self, *args, **kwargs):
+ return super().validate(*args, **{**kwargs, "flash_invalid": False})
+
class TaskOrderForm(BaseForm):
number = StringField(label=translate("forms.task_order.number_description"))
diff --git a/js/components/upload_input.js b/js/components/upload_input.js
index 22a7c81c..273c1d20 100644
--- a/js/components/upload_input.js
+++ b/js/components/upload_input.js
@@ -48,6 +48,7 @@ export default {
attachment: this.initialData || null,
showErrors: this.initialErrors,
changed: false,
+ uploadError: null,
}
},
@@ -63,15 +64,15 @@ export default {
methods: {
addAttachment: async function(e) {
const file = e.target.files[0]
- try {
- await this.uploader.upload(file, this.objectName)
+
+ const response = await this.uploader.upload(file, this.objectName)
+ if (response.ok) {
this.attachment = e.target.value
- this.showErrors = false
this.$refs.attachmentFilename.value = file.name
this.$refs.attachmentObjectName.value = this.objectName
- } catch (err) {
- console.log(err)
+ } else {
this.showErrors = true
+ this.uploadError = true
}
this.changed = true
@@ -90,6 +91,7 @@ export default {
this.$refs.attachmentInput.value = null
}
this.showErrors = false
+ this.uploadError = false
this.changed = true
emitEvent('field-change', this, {
diff --git a/js/lib/upload.js b/js/lib/upload.js
index f102c851..4a300b32 100644
--- a/js/lib/upload.js
+++ b/js/lib/upload.js
@@ -70,7 +70,7 @@ class MockUploader {
}
async upload(file, objectName) {
- return Promise.resolve({})
+ return Promise.resolve({ ok: true })
}
}
diff --git a/js/test_templates/upload_input_error_template.html b/js/test_templates/upload_input_error_template.html
index efb83e29..02256f0d 100644
--- a/js/test_templates/upload_input_error_template.html
+++ b/js/test_templates/upload_input_error_template.html
@@ -25,14 +25,6 @@
Browse
-
-
-
-
-
-
+
+ There was an error uploading your file. Please try again. If you encounter repeated problems uploading this file, please contact CCPO.
+
- ['Test Error Message']
+ Test Error Message
diff --git a/js/test_templates/upload_input_template.html b/js/test_templates/upload_input_template.html
index 37412a66..70c73407 100644
--- a/js/test_templates/upload_input_template.html
+++ b/js/test_templates/upload_input_template.html
@@ -25,7 +25,6 @@
Browse
-
+
+ There was an error uploading your file. Please try again. If you encounter repeated problems uploading this file, please contact CCPO.
+
diff --git a/script/seed_sample.py b/script/seed_sample.py
index 7bc9cb9d..c6c164a4 100644
--- a/script/seed_sample.py
+++ b/script/seed_sample.py
@@ -1,10 +1,11 @@
# Add root application dir to the python path
import os
import sys
-from datetime import timedelta, date, timedelta
+from datetime import timedelta, date
import random
from faker import Faker
from werkzeug.datastructures import FileStorage
+from uuid import uuid4
parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.append(parent_dir)
@@ -30,9 +31,9 @@ from atst.routes.dev import _DEV_USERS as DEV_USERS
from tests.factories import (
random_service_branch,
- random_task_order_number,
TaskOrderFactory,
CLINFactory,
+ AttachmentFactory,
)
fake = Faker()
@@ -167,20 +168,20 @@ def add_task_orders_to_portfolio(portfolio):
yesterday = today - timedelta(days=1)
five_days = timedelta(days=5)
- with open("tests/fixtures/sample.pdf", "rb") as fp:
- pdf = FileStorage(fp, content_type="application/pdf")
+ def build_pdf():
+ return {"filename": "sample_task_order.pdf", "object_name": str(uuid4())}
- draft_to = TaskOrderFactory.build(portfolio=portfolio, pdf=None)
- unsigned_to = TaskOrderFactory.build(portfolio=portfolio, pdf=pdf)
- upcoming_to = TaskOrderFactory.build(
- portfolio=portfolio, signed_at=yesterday, pdf=pdf
- )
- expired_to = TaskOrderFactory.build(
- portfolio=portfolio, signed_at=yesterday, pdf=pdf
- )
- active_to = TaskOrderFactory.build(
- portfolio=portfolio, signed_at=yesterday, pdf=pdf
- )
+ draft_to = TaskOrderFactory.build(portfolio=portfolio, pdf=None)
+ unsigned_to = TaskOrderFactory.build(portfolio=portfolio, pdf=build_pdf())
+ upcoming_to = TaskOrderFactory.build(
+ portfolio=portfolio, signed_at=yesterday, pdf=build_pdf()
+ )
+ expired_to = TaskOrderFactory.build(
+ portfolio=portfolio, signed_at=yesterday, pdf=build_pdf()
+ )
+ active_to = TaskOrderFactory.build(
+ portfolio=portfolio, signed_at=yesterday, pdf=build_pdf()
+ )
clins = [
CLINFactory.build(
diff --git a/templates/components/upload_input.html b/templates/components/upload_input.html
index 6e71d157..a56cb0bf 100644
--- a/templates/components/upload_input.html
+++ b/templates/components/upload_input.html
@@ -30,9 +30,6 @@
Browse
- {% if field.errors %}
- {{ Icon('alert',classes="icon-validation") }}
- {% endif %}
- {% for error, error_message in field.errors.items() %}
- {{error_message}}
+
+ {{ "forms.task_order.upload_error" | translate }}
+
+ {% for error, error_messages in field.errors.items() %}
+ {{error_messages[0]}}
{% endfor %}
diff --git a/tests/routes/task_orders/test_new.py b/tests/routes/task_orders/test_new.py
index 478baedf..bdc3a76d 100644
--- a/tests/routes/task_orders/test_new.py
+++ b/tests/routes/task_orders/test_new.py
@@ -1,16 +1,18 @@
import pytest
from flask import url_for, get_flashed_messages
from datetime import timedelta, date
+from uuid import uuid4
from atst.domain.task_orders import TaskOrders
from atst.models.task_order import Status as TaskOrderStatus
from atst.models import TaskOrder
from tests.factories import CLINFactory, PortfolioFactory, TaskOrderFactory, UserFactory
+from tests.utils import captured_templates
-def build_pdf_form_data(filename="sample.pdf", object_name="object_name"):
- return {"pdf-filename": filename, "pdf-object_name": object_name}
+def build_pdf_form_data(filename="sample.pdf", object_name=None):
+ return {"pdf-filename": filename, "pdf-object_name": object_name or uuid4()}
@pytest.fixture
@@ -101,6 +103,42 @@ def test_task_orders_submit_form_step_one_add_pdf_delete_pdf(
assert response.status_code == 302
+def test_task_orders_submit_form_step_one_validates_filename(
+ app, client, user_session, portfolio
+):
+ user_session(portfolio.owner)
+ with captured_templates(app) as templates:
+ client.post(
+ url_for(
+ "task_orders.submit_form_step_one_add_pdf", portfolio_id=portfolio.id
+ ),
+ data={"pdf-filename": "a" * 1024},
+ follow_redirects=True,
+ )
+
+ _, context = templates[-1]
+
+ assert "filename" in context["form"].pdf.errors
+
+
+def test_task_orders_submit_form_step_one_validates_object_name(
+ app, client, user_session, portfolio
+):
+ user_session(portfolio.owner)
+ with captured_templates(app) as templates:
+ client.post(
+ url_for(
+ "task_orders.submit_form_step_one_add_pdf", portfolio_id=portfolio.id
+ ),
+ data={"pdf-object_name": "a" * 41},
+ follow_redirects=True,
+ )
+
+ _, context = templates[-1]
+
+ assert "object_name" in context["form"].pdf.errors
+
+
def test_task_orders_form_step_two_add_number(client, user_session, task_order):
user_session(task_order.creator)
response = client.get(
diff --git a/translations.yaml b/translations.yaml
index f03ed93c..f57c61d3 100644
--- a/translations.yaml
+++ b/translations.yaml
@@ -149,7 +149,13 @@ forms:
portfolio:
name_label: Portfolio name
name_length_validation_message: Portfolio names can be between 4-100 characters
+ attachment:
+ object_name:
+ length_error: Object name may be no longer than 40 characters.
+ filename:
+ length_error: Filename may be no longer than 100 characters.
task_order:
+ upload_error: There was an error uploading your file. Please try again. If you encounter repeated problems uploading this file, please contact CCPO.
app_migration:
both: 'Yes, migrating from both an on-premise data center and another cloud provider'
cloud: 'Yes, migrating from another cloud provider'