Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.

Feature/filestack int #822

Merged
merged 4 commits into from
Jun 7, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions app/directives/tc-fp-file-input/tc-file-input.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/*eslint no-undef:0*/
import angular from 'angular'

describe('Topcoder File Input Directive', function() {
var scope, element, isolateScope, fileInput

beforeEach(function() {
bard.appModule('topcoder')
bard.inject(this, '$compile', '$rootScope', '$timeout')
scope = $rootScope.$new()

var html = '' +
'<form>' +
'<tc-file-input ' +
'label-text="Submission"' +
'field-id="SUBMISSION_ZIP"' +
'button-text="Add File"' +
'file-type="zip"' +
'placeholder="Attach all visible files as a single .zip file"' +
'mandatory="true"' +
'set-file-reference="vm.setFileReference(file, fieldId)"' +
'ng-model="vm.submissionForm.submissionZip"' +
' />' +
'</form>'
var form = angular.element(html)
element = form.find('tc-file-input')
$compile(form)(scope)
scope.$digest()

isolateScope = element.isolateScope()
})

beforeEach(function() {
fileInput = $(element).find('.none')[0]
})

afterEach(function() {
scope.$destroy()
fileInput = undefined
})

bard.verifyNoOutstandingHttpRequests()

describe('selectFile', function() {
it('triggers a click on the file input', function() {
var mockClick = sinon.spy(fileInput, 'click')

isolateScope.selectFile()
scope.$digest()

expect(mockClick).calledOnce
})
})

describe('a change event on the file input', function() {
var fileNameInput, fileList, mockSetFileReference

beforeEach(function() {
fileNameInput = $(element).find('input[type=text]')[0]
fileList = {
0: {
name: 'test.zip',
size: 50,
type: 'application/zip'
},
length: 1,
item: function (index) { return index }
}

mockSetFileReference = sinon.spy(isolateScope, 'setFileReference')
})

afterEach(function() {
fileNameInput = undefined
fileList = undefined
mockSetFileReference = undefined
})

it('sets the value of the fileNameInput with the name of the file', function() {
$(fileInput).triggerHandler({
type: 'change',
target: { files: fileList }
})

$timeout.flush()

expect(fileNameInput.value).to.equal('test.zip')
})

describe('with a valid file', function() {
beforeEach(function() {
$(fileInput).triggerHandler({
type: 'change',
target: { files: fileList }
})
$timeout.flush()
})

it('calls setFileReference', function() {
expect(mockSetFileReference).calledOnce
})

it('has ng-valid-filesize class', function() {
expect($(fileInput).hasClass('ng-valid-filesize')).to.be.true
})

it('has ng-valid-required class', function() {
expect($(fileInput).hasClass('ng-valid-required')).to.be.true
})

it('works with Windows file type application/x-zip', function(){
fileList[0].type = 'application/x-zip'

$(fileInput).triggerHandler({
type: 'change',
target: { files: fileList }
})

$timeout.flush()

expect(mockSetFileReference).called
expect($(fileInput).hasClass('ng-valid-filesize')).to.be.true
expect($(fileInput).hasClass('ng-valid-required')).to.be.true
})

it('works with Windows file type application/x-zip-compressed', function(){
fileList[0].type = 'application/x-zip-compressed'

$(fileInput).triggerHandler({
type: 'change',
target: { files: fileList }
})

$timeout.flush()

expect(mockSetFileReference).called
expect($(fileInput).hasClass('ng-valid-filesize')).to.be.true
expect($(fileInput).hasClass('ng-valid-required')).to.be.true
})
})

describe('with a file type that\'s not in the list of fileTypes given to the directive', function() {
beforeEach(function() {
fileList[0].type = 'image/png'

$(fileInput).triggerHandler({
type: 'change',
target: { files: fileList }
})

$timeout.flush()
})

it('does not call setFileReference', function() {
expect(mockSetFileReference).not.calledOnce
})

it('has ng-touched and ng-invalid-required classes', function() {
expect($(fileInput).hasClass('ng-invalid-required')).to.be.true
expect($(fileInput).hasClass('ng-touched')).to.be.true
})
})

describe('with a file extension that is not in the list of fileTypes given to the directive', function() {
beforeEach(function() {
fileList[0].name = 'submission.zip.jpg'

$(fileInput).triggerHandler({
type: 'change',
target: { files: fileList }
})

$timeout.flush()
})

it('does not call setFileReference', function() {
expect(mockSetFileReference).not.calledOnce
})

it('has ng-touched and ng-invalid-required classes', function() {
expect($(fileInput).hasClass('ng-invalid-required')).to.be.true
expect($(fileInput).hasClass('ng-touched')).to.be.true
})
})

describe('with a file that\'s greater than 500MB', function() {
beforeEach(function() {
fileList[0].size = 524288001

$(fileInput).triggerHandler({
type: 'change',
target: { files: fileList }
})

$timeout.flush()
})

it('does not call setFileReference', function() {
expect(mockSetFileReference).not.calledOnce
})

it('has ng-touched and ng-invalid-filesize classes', function() {
expect($(fileInput).hasClass('ng-invalid-filesize')).to.be.true
expect($(fileInput).hasClass('ng-touched')).to.be.true
})
})
})
})
80 changes: 80 additions & 0 deletions app/directives/tc-fp-file-input/tc-fp-file-input.directive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import angular from 'angular'
import _ from 'lodash'

(function() {
'use strict'

angular.module('tcUIComponents').directive('tcFpFileInput', ['$rootScope', 'CONSTANTS', 'logger', 'UserService', 'filepickerService', tcFPFileInput])

function tcFPFileInput($rootScope, CONSTANTS, logger, UserService, filepickerService) {
return {
restrict: 'E',
require: '^form',
template: require('./tc-fp-file-input')(),
scope: {
labelText: '@',
fieldId: '@',
placeholder: '@',
fileType: '@',
showFileType: '=',
mandatory: '=',
maxFileSize: '@',
fpServices: '@',
buttonText: '@',
setFileReference: '&',
ngModel: '='
},
link: function(scope, element, attrs, formController) {
// set filePath
var userId = parseInt(UserService.getUserIdentity().userId)
scope.filePath = scope.fieldId + '/'
if (scope.fieldId.indexOf('ZIP') > -1) {
scope.filePath += _.join([userId, scope.fieldId, (new Date()).valueOf()], '-') + '.zip'
}
// set extensions
if (scope.fieldId.indexOf('ZIP') > -1) {
scope.extensions = ".zip"
} else if (scope.fieldId.indexOf('DESIGN_COVER') > -1) {
scope.extensions = ".png,.jpeg,.jpg,.bmp"
}

// set default services
scope.fpServices = scope.fpServices || "COMPUTER,GOOGLE_DRIVE,BOX,DROPBOX"
scope.fpContainer = CONSTANTS.FILE_PICKER_SUBMISSION_CONTAINER_NAME || 'submission-staging-dev'

// set max size
scope.maxSize = 500*1024*1024

var key, value;
/*
*pass original event
*/
element.bind('change', function(event) {
event.preventDefault()
scope.onSuccess(event.originalEvent || event);
$rootScope.$apply()
});
element = element.length ? element[0] : element;
for (key in attrs.$attr){
value = attrs.$attr[key]
element.setAttribute(value, attrs[key])
}
filepickerService.constructWidget(element)

scope.onSuccess = function (event) {
debugger
var fpFile = event.fpfile
var _file = {
name: scope.filename || fpFile.filename,
container: fpFile.container || scope.fpContainer,
path: fpFile.key,
size: fpFile.size,
mimetype: fpFile.mimetype
}
scope.ngModel = _file
scope.setFileReference({file: _file, fieldId: scope.fieldId})
}
}
}
}
})()
19 changes: 19 additions & 0 deletions app/directives/tc-fp-file-input/tc-fp-file-input.jade
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.tc-file-field__label
label.tc-label {{labelText}}
span.lowercase(ng-if="showFileType") {{ ' *(.' + fileType + ')'}}

.tc-file-field__inputs
.tc-label__wrapper
span.tc-label__asterisk.lowercase(ng-if="mandatory") #[span *]mandatory
input.tc-file-field__input(
type="filepicker-dragdrop",
data-fp-maxSize="{{maxSize}}",
data-fp-button-class="tc-btn",
data-fp-services="{{fpServices}}",
data-fp-multiple="false",
data-fp-extensions="{{extensions}}",
data-fp-store-location="s3",
data-fp-store-container="{{fpContainer}}",
data-fp-store-path="{{filePath}}",
on-success="onFileSeleted(event.fpfile)"
)
25 changes: 17 additions & 8 deletions app/services/submissions.service.js
Original file line number Diff line number Diff line change
@@ -11,20 +11,25 @@ import angular from 'angular'
var api = ApiService.getApiServiceProvider('SUBMISSIONS')

var service = {
getPresignedURL: getPresignedURL,
uploadSubmissionFileToS3: uploadSubmissionFileToS3,
updateSubmissionStatus: updateSubmissionStatus,
recordCompletedSubmission: recordCompletedSubmission
startSubmission: startSubmission,
processSubmission: processSubmission
// uploadSubmissionFileToS3: uploadSubmissionFileToS3,
// updateSubmissionStatus: updateSubmissionStatus,
// recordCompletedSubmission: recordCompletedSubmission
}

return service

function getPresignedURL(body, files, progressCallback) {
function startSubmission(body, progressCallback) {
return api.all('submissions').customPOST(body)
.then(function(response) {
progressCallback.call(progressCallback, 'PREPARE', 100)
//progressCallback.call(progressCallback, 'PREPARE', 100)

uploadSubmissionFileToS3(response, response.data.files, files, progressCallback)
// uploadSubmissionFileToS3(response, response.data.files, files, progressCallback)

console.log(response);

processSubmission(response, progressCallback);
})
.catch(function(err) {
logger.error('Could not get presigned url', err)
@@ -35,6 +40,7 @@ import angular from 'angular'
})
}

/**
function uploadSubmissionFileToS3(presignedURLResponse, filesWithPresignedURL, files, progressCallback) {

var promises = filesWithPresignedURL.map(function(fileWithPresignedURL) {
@@ -120,14 +126,17 @@ import angular from 'angular'
progressCallback.call(progressCallback, 'ERROR', err)
})
}
*/

function recordCompletedSubmission(body, progressCallback) {
function processSubmission(body, progressCallback) {
// Once all uploaded, make record and begin processing
return api.one('submissions', body.id).customPOST(body, 'process')
.then(function(response) {
logger.info('Successfully made file record. Beginning processing')

progressCallback.call(progressCallback, 'FINISH', 100)

return response;
})
.catch(function(err) {
logger.error('Could not start processing', err)
8 changes: 7 additions & 1 deletion app/submissions/submissions.module.js
Original file line number Diff line number Diff line change
@@ -8,9 +8,15 @@ import angular from 'angular'
'tc.services',
'tcUIComponents',
'toaster',
'appirio-tech-ng-ui-components'
'appirio-tech-ng-ui-components',
'angular-filepicker'
]

angular.module('tc.submissions', dependencies)
.config(['filepickerProvider', 'CONSTANTS',
function (filepickerProvider, CONSTANTS) {
filepickerProvider.setKey(CONSTANTS.FILE_PICKER_API_KEY || 'AzFINuQoqTmqw0QEoaw9az')
}
])

})()
Loading