Merge pull request #1033 from dod-ccpo/ajax-uploads
Get presigned cloud upload token through an ajax call
This commit is contained in:
commit
b3e48aa6e7
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"exclude": {
|
"exclude": {
|
||||||
"files": null,
|
"files": "^.secrets.baseline$",
|
||||||
"lines": null
|
"lines": null
|
||||||
},
|
},
|
||||||
"generated_at": "2019-08-21T18:40:15Z",
|
"generated_at": "2019-08-28T19:50:39Z",
|
||||||
"plugins_used": [
|
"plugins_used": [
|
||||||
{
|
{
|
||||||
"base64_limit": 4.5,
|
"base64_limit": 4.5,
|
||||||
@ -38,7 +38,7 @@
|
|||||||
"hashed_secret": "d141ce86b0584abb29ee7c24af9afb1e3d871f04",
|
"hashed_secret": "d141ce86b0584abb29ee7c24af9afb1e3d871f04",
|
||||||
"is_secret": false,
|
"is_secret": false,
|
||||||
"is_verified": false,
|
"is_verified": false,
|
||||||
"line_number": 145,
|
"line_number": 156,
|
||||||
"type": "Secret Keyword"
|
"type": "Secret Keyword"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -60,6 +60,22 @@
|
|||||||
"type": "Hex High Entropy String"
|
"type": "Hex High Entropy String"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"alembic/versions/fda6bd7e1b65_clin_delete_cascade.py": [
|
||||||
|
{
|
||||||
|
"hashed_secret": "61d8937fb12b982e07b933c083d9014c34159723",
|
||||||
|
"is_secret": false,
|
||||||
|
"is_verified": false,
|
||||||
|
"line_number": 13,
|
||||||
|
"type": "Hex High Entropy String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hashed_secret": "999a22300a564f9d2bdca555c2170465fd760ae3",
|
||||||
|
"is_secret": false,
|
||||||
|
"is_verified": false,
|
||||||
|
"line_number": 14,
|
||||||
|
"type": "Hex High Entropy String"
|
||||||
|
}
|
||||||
|
],
|
||||||
"atst.ini.example": [
|
"atst.ini.example": [
|
||||||
{
|
{
|
||||||
"hashed_secret": "abcdb568713c255c81376829da20004ba9463fd3",
|
"hashed_secret": "abcdb568713c255c81376829da20004ba9463fd3",
|
||||||
@ -178,10 +194,11 @@
|
|||||||
"hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207",
|
"hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207",
|
||||||
"is_secret": false,
|
"is_secret": false,
|
||||||
"is_verified": false,
|
"is_verified": false,
|
||||||
"line_number": 481,
|
"line_number": 523,
|
||||||
"type": "Hex High Entropy String"
|
"type": "Hex High Entropy String"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"version": "0.12.5"
|
"version": "0.12.5"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,8 @@ from flask import (
|
|||||||
render_template,
|
render_template,
|
||||||
request as http_request,
|
request as http_request,
|
||||||
url_for,
|
url_for,
|
||||||
current_app,
|
current_app as app,
|
||||||
|
jsonify,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import task_orders_bp
|
from . import task_orders_bp
|
||||||
@ -17,9 +18,7 @@ from atst.utils.flash import formatted_flash as flash
|
|||||||
|
|
||||||
|
|
||||||
def render_task_orders_edit(template, portfolio_id=None, task_order_id=None, form=None):
|
def render_task_orders_edit(template, portfolio_id=None, task_order_id=None, form=None):
|
||||||
(token, object_name) = current_app.csp.files.get_token()
|
render_args = {}
|
||||||
render_args = {"token": token, "object_name": object_name}
|
|
||||||
|
|
||||||
if task_order_id:
|
if task_order_id:
|
||||||
task_order = TaskOrders.get(task_order_id)
|
task_order = TaskOrders.get(task_order_id)
|
||||||
portfolio_id = task_order.portfolio_id
|
portfolio_id = task_order.portfolio_id
|
||||||
@ -73,6 +72,15 @@ def update_task_order(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@task_orders_bp.route("/task_orders/<portfolio_id>/upload-token")
|
||||||
|
@user_can(Permissions.CREATE_TASK_ORDER, message="edit task order form")
|
||||||
|
def upload_token(portfolio_id):
|
||||||
|
(token, object_name) = app.csp.files.get_token()
|
||||||
|
render_args = {"token": token, "objectName": object_name}
|
||||||
|
|
||||||
|
return jsonify(render_args)
|
||||||
|
|
||||||
|
|
||||||
@task_orders_bp.route("/task_orders/<task_order_id>/edit")
|
@task_orders_bp.route("/task_orders/<task_order_id>/edit")
|
||||||
@user_can(Permissions.CREATE_TASK_ORDER, message="edit task order form")
|
@user_can(Permissions.CREATE_TASK_ORDER, message="edit task order form")
|
||||||
def edit(task_order_id):
|
def edit(task_order_id):
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
import createNumberMask from 'text-mask-addons/dist/createNumberMask'
|
|
||||||
import { conformToMask } from 'vue-text-mask'
|
|
||||||
|
|
||||||
import { emitEvent } from '../lib/emitters'
|
import { emitEvent } from '../lib/emitters'
|
||||||
import FormMixin from '../mixins/form'
|
import FormMixin from '../mixins/form'
|
||||||
import textinput from './text_input'
|
import textinput from './text_input'
|
||||||
@ -20,12 +17,6 @@ export default {
|
|||||||
|
|
||||||
props: {
|
props: {
|
||||||
name: String,
|
name: String,
|
||||||
token: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
objectName: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
initialData: {
|
initialData: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
@ -40,6 +31,9 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
portfolioId: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
@ -52,8 +46,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created: function() {
|
created: async function() {
|
||||||
this.uploader = buildUploader(this.token)
|
|
||||||
emitEvent('field-mount', this, {
|
emitEvent('field-mount', this, {
|
||||||
optional: this.optional,
|
optional: this.optional,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
@ -71,13 +64,12 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this.uploader.upload(file, this.objectName)
|
const uploader = await this.getUploader()
|
||||||
|
const response = await uploader.upload(file)
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
this.attachment = e.target.value
|
this.attachment = e.target.value
|
||||||
this.$refs.attachmentFilename.value = file.name
|
this.$refs.attachmentFilename.value = file.name
|
||||||
this.$refs.attachmentObjectName.value = this.objectName
|
this.$refs.attachmentObjectName.value = response.objectName
|
||||||
this.$refs.attachmentInput.disabled = true
|
|
||||||
} else {
|
} else {
|
||||||
this.uploadError = true
|
this.uploadError = true
|
||||||
}
|
}
|
||||||
@ -111,6 +103,13 @@ export default {
|
|||||||
this.uploadError = false
|
this.uploadError = false
|
||||||
this.sizeError = false
|
this.sizeError = false
|
||||||
},
|
},
|
||||||
|
getUploader: async function() {
|
||||||
|
return fetch(`/task_orders/${this.portfolioId}/upload-token`, {
|
||||||
|
credentials: 'include',
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(({ token, objectName }) => buildUploader(token, objectName))
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -2,13 +2,14 @@ import Azure from 'azure-storage'
|
|||||||
import 'whatwg-fetch'
|
import 'whatwg-fetch'
|
||||||
|
|
||||||
class AzureUploader {
|
class AzureUploader {
|
||||||
constructor(accountName, containerName, sasToken) {
|
constructor(accountName, containerName, sasToken, objectName) {
|
||||||
this.accountName = accountName
|
this.accountName = accountName
|
||||||
this.containerName = containerName
|
this.containerName = containerName
|
||||||
this.sasToken = sasToken.token
|
this.sasToken = sasToken.token
|
||||||
|
this.objectName = objectName
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload(file, objectName) {
|
async upload(file) {
|
||||||
const blobService = Azure.createBlobServiceWithSas(
|
const blobService = Azure.createBlobServiceWithSas(
|
||||||
`https://${this.accountName}.blob.core.windows.net`,
|
`https://${this.accountName}.blob.core.windows.net`,
|
||||||
this.sasToken
|
this.sasToken
|
||||||
@ -27,14 +28,14 @@ class AzureUploader {
|
|||||||
fileReader.addEventListener('load', f => {
|
fileReader.addEventListener('load', f => {
|
||||||
blobService.createBlockBlobFromText(
|
blobService.createBlockBlobFromText(
|
||||||
this.containerName,
|
this.containerName,
|
||||||
`${objectName}`,
|
`${this.objectName}`,
|
||||||
f.target.result,
|
f.target.result,
|
||||||
options,
|
options,
|
||||||
function(err, result) {
|
(err, result) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
resolve({ ok: false })
|
resolve({ ok: false })
|
||||||
} else {
|
} else {
|
||||||
resolve({ ok: true })
|
resolve({ ok: true, objectName: this.objectName })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -45,11 +46,12 @@ class AzureUploader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AwsUploader {
|
class AwsUploader {
|
||||||
constructor(presignedPost) {
|
constructor(presignedPost, objectName) {
|
||||||
this.presignedPost = presignedPost
|
this.presignedPost = presignedPost
|
||||||
|
this.objectName = objectName
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload(file, objectName) {
|
async upload(file) {
|
||||||
const form = new FormData()
|
const form = new FormData()
|
||||||
Object.entries(this.presignedPost.fields).forEach(([k, v]) => {
|
Object.entries(this.presignedPost.fields).forEach(([k, v]) => {
|
||||||
form.append(k, v)
|
form.append(k, v)
|
||||||
@ -57,34 +59,38 @@ class AwsUploader {
|
|||||||
form.append('file', file)
|
form.append('file', file)
|
||||||
form.set('x-amz-meta-filename', file.name)
|
form.set('x-amz-meta-filename', file.name)
|
||||||
|
|
||||||
return fetch(this.presignedPost.url, {
|
const response = await fetch(this.presignedPost.url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: form,
|
body: form,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return { ok: response.ok, objectName: this.objectName }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockUploader {
|
class MockUploader {
|
||||||
constructor(token) {
|
constructor(token, objectName) {
|
||||||
this.token = token
|
this.token = token
|
||||||
|
this.objectName = objectName
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload(file, objectName) {
|
async upload(file, objectName) {
|
||||||
return Promise.resolve({ ok: true })
|
return Promise.resolve({ ok: true, objectName: this.objectName })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const buildUploader = token => {
|
export const buildUploader = (token, objectName) => {
|
||||||
const cloudProvider = process.env.CLOUD_PROVIDER || 'mock'
|
const cloudProvider = process.env.CLOUD_PROVIDER || 'mock'
|
||||||
if (cloudProvider === 'aws') {
|
if (cloudProvider === 'aws') {
|
||||||
return new AwsUploader(token)
|
return new AwsUploader(token, objectName)
|
||||||
} else if (cloudProvider === 'azure') {
|
} else if (cloudProvider === 'azure') {
|
||||||
return new AzureUploader(
|
return new AzureUploader(
|
||||||
process.env.AZURE_ACCOUNT_NAME,
|
process.env.AZURE_ACCOUNT_NAME,
|
||||||
process.env.AZURE_CONTAINER_NAME,
|
process.env.AZURE_CONTAINER_NAME,
|
||||||
token
|
token,
|
||||||
|
objectName
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return new MockUploader(token)
|
return new MockUploader(token, objectName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,9 @@
|
|||||||
v-bind:initial-errors='true'
|
v-bind:initial-errors='true'
|
||||||
|
|
||||||
v-bind:watch='false'
|
v-bind:watch='false'
|
||||||
|
v-bind:portfolio-id="''"
|
||||||
name='pdf'
|
name='pdf'
|
||||||
:optional='false'
|
:optional='false'
|
||||||
v-bind:token='token'
|
|
||||||
v-bind:object-name='"object_name"'
|
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div v-show="hasAttachment" class="uploaded-file">
|
<div v-show="hasAttachment" class="uploaded-file">
|
||||||
@ -41,6 +40,9 @@
|
|||||||
<template v-if="uploadError">
|
<template v-if="uploadError">
|
||||||
<span class="usa-input__message">There was an error uploading your file. Please try again. If you encounter repeated problems uploading this file, please contact CCPO.</span>
|
<span class="usa-input__message">There was an error uploading your file. Please try again. If you encounter repeated problems uploading this file, please contact CCPO.</span>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="sizeError">
|
||||||
|
<span class="usa-input__message">The file you have selected is too large. Please choose a file no larger than 64MB.</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<span class="usa-input__message">Test Error Message</span>
|
<span class="usa-input__message">Test Error Message</span>
|
||||||
|
|
||||||
|
@ -4,10 +4,9 @@
|
|||||||
v-bind:initial-data='initialvalue'
|
v-bind:initial-data='initialvalue'
|
||||||
|
|
||||||
v-bind:watch='false'
|
v-bind:watch='false'
|
||||||
|
v-bind:portfolio-id="''"
|
||||||
name='pdf'
|
name='pdf'
|
||||||
:optional='false'
|
:optional='false'
|
||||||
v-bind:token='token'
|
|
||||||
v-bind:object-name='"object_name"'
|
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div v-show="hasAttachment" class="uploaded-file">
|
<div v-show="hasAttachment" class="uploaded-file">
|
||||||
@ -41,6 +40,9 @@
|
|||||||
<template v-if="uploadError">
|
<template v-if="uploadError">
|
||||||
<span class="usa-input__message">There was an error uploading your file. Please try again. If you encounter repeated problems uploading this file, please contact CCPO.</span>
|
<span class="usa-input__message">There was an error uploading your file. Please try again. If you encounter repeated problems uploading this file, please contact CCPO.</span>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="sizeError">
|
||||||
|
<span class="usa-input__message">The file you have selected is too large. Please choose a file no larger than 64MB.</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{% from "components/icon.html" import Icon %}
|
{% from "components/icon.html" import Icon %}
|
||||||
|
|
||||||
{% macro UploadInput(field, show_label=False, watch=False, token="", object_name="") -%}
|
{% macro UploadInput(field, portfolio_id, show_label=False, watch=False) -%}
|
||||||
<uploadinput
|
<uploadinput
|
||||||
inline-template
|
inline-template
|
||||||
{% if not field.errors %}
|
{% if not field.errors %}
|
||||||
@ -9,10 +9,9 @@
|
|||||||
v-bind:initial-errors='true'
|
v-bind:initial-errors='true'
|
||||||
{% endif %}
|
{% endif %}
|
||||||
v-bind:watch='{{ watch | string | lower }}'
|
v-bind:watch='{{ watch | string | lower }}'
|
||||||
|
v-bind:portfolio-id="'{{ portfolio_id }}'"
|
||||||
name='{{ field.name }}'
|
name='{{ field.name }}'
|
||||||
:optional='false'
|
:optional='false'
|
||||||
v-bind:token='{{ token | tojson }}'
|
|
||||||
v-bind:object-name='"{{ object_name | string }}"'
|
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div v-show="hasAttachment" class="uploaded-file">
|
<div v-show="hasAttachment" class="uploaded-file">
|
||||||
|
@ -14,8 +14,7 @@
|
|||||||
{% set next_button_text = "Next: Add TO Number" %}
|
{% set next_button_text = "Next: Add TO Number" %}
|
||||||
{% set step = "1" %}
|
{% set step = "1" %}
|
||||||
|
|
||||||
|
|
||||||
{% block to_builder_form_field %}
|
{% block to_builder_form_field %}
|
||||||
{{ TOFormStepHeader('task_orders.form.supporting_docs_header' | translate, 'task_orders.form.supporting_docs_text' | translate) }}
|
{{ TOFormStepHeader('task_orders.form.supporting_docs_header' | translate, 'task_orders.form.supporting_docs_text' | translate) }}
|
||||||
{{ UploadInput(form.pdf, watch=True, token=token, object_name=object_name) }}
|
{{ UploadInput(form.pdf, portfolio.id, watch=True) }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -74,15 +74,11 @@ def test_make_checkbox_input_template(checkbox_input_macro, initial_value_form):
|
|||||||
|
|
||||||
|
|
||||||
def test_make_upload_input_template(upload_input_macro, task_order_form):
|
def test_make_upload_input_template(upload_input_macro, task_order_form):
|
||||||
rendered_upload_macro = upload_input_macro(
|
rendered_upload_macro = upload_input_macro(task_order_form.pdf)
|
||||||
task_order_form.pdf, token="token", object_name="object_name"
|
|
||||||
)
|
|
||||||
write_template(rendered_upload_macro, "upload_input_template.html")
|
write_template(rendered_upload_macro, "upload_input_template.html")
|
||||||
|
|
||||||
|
|
||||||
def test_make_upload_input_error_template(upload_input_macro, task_order_form):
|
def test_make_upload_input_error_template(upload_input_macro, task_order_form):
|
||||||
task_order_form.validate()
|
task_order_form.validate()
|
||||||
rendered_upload_macro = upload_input_macro(
|
rendered_upload_macro = upload_input_macro(task_order_form.pdf)
|
||||||
task_order_form.pdf, token="token", object_name="object_name"
|
|
||||||
)
|
|
||||||
write_template(rendered_upload_macro, "upload_input_error_template.html")
|
write_template(rendered_upload_macro, "upload_input_error_template.html")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user