Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(upload-core): upgrade tus-js-client #287

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions docs/source/api/uploads.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ const upload = new Upload(file, {
allowedFileNameCharacters: '_a-zA-Z0-9 ', // alphanumeric, spaces, underscore
pollingTime: 1000,
});
await upload.init();
upload.start();
```
2 changes: 2 additions & 0 deletions packages/api-core/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,10 @@ export default class AvApi {
for (let i = 0; i < totalPages - 1; i += 1) {
otherPages[i] = i + 2;
}
// eslint-disable-next-line promise/no-nesting
return this.Promise.all(
otherPages.map(page =>
// eslint-disable-next-line promise/no-nesting
this.getPage(page, config, resp.data.limit).then(
pageResp => pageResp.data[key] || []
)
Expand Down
4 changes: 2 additions & 2 deletions packages/upload-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
"access": "public"
},
"devDependencies": {
"tus-js-client": "1.7.1"
"tus-js-client": "^2.2.0"
},
"dependencies": {
"@availity/resolve-url": "^1.1.20",
"@babel/runtime": "^7.10.2",
"tus-js-client": "1.7.1"
"tus-js-client": "2.2.0"
}
}
60 changes: 34 additions & 26 deletions packages/upload-core/src/tests/upload.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@ describe('upload-core', () => {
}).toThrow('[options.bucketId] must be defined');
});

it('should allow override to defaults', () => {
it('should allow override to defaults', async () => {
const file = Buffer.from('hello world'.split(''));
file.name = 'fileName.png';
const upload = new Upload(file, optionsWithRetry);
await upload.init();
expect(upload.options.retryDelays[0]).toBe(
optionsWithRetry.retryDelays[0]
);
Expand All @@ -77,10 +78,11 @@ describe('upload-core', () => {
new Upload(file, options);
});

it('should throw error for invalid file type', () => {
it('should throw error for invalid file type', async () => {
const file = Buffer.from('hello world'.split(''));
file.name = 'notCoolFile.docx';
const upload = new Upload(file, optionsWithFileTypes);
await upload.init();
expect(upload.isValidFile()).toBeFalsy();
});

Expand All @@ -91,25 +93,27 @@ describe('upload-core', () => {
new Upload(file, optionsWithFileTypes);
});

it('should use default options', () => {
it('should use default options', async () => {
const file = Buffer.from('hello world'.split(''));
file.name = 'optionsFile.png';
const upload = new Upload(file, options);
await upload.init();

expect(upload.options.endpoint).toBe(
'https://dev.local/ms/api/availity/internal/core/vault/upload/v1/resumable'
);
});

it('should not allow files over maxSize', () => {
it('should not allow files over maxSize', async () => {
const file = Buffer.from('hello world!'.split(''));
file.name = 'sizeFile.pdf';
file.size = 1e7;
const upload = new Upload(file, optionsWithFileSize);
await upload.init();
expect(upload.isValidFile()).toBeFalsy();
});

it('should use metadata values for fingerprint', () => {
it('should use metadata values for fingerprint', async () => {
const file = Buffer.from('hello world!'.split(''));
file.name = 'a';
file.type = 'b';
Expand All @@ -120,83 +124,86 @@ describe('upload-core', () => {
});

let upload = new Upload(file, options);
expect(upload.generateId()).toBe('tus-a-b-100-1016975905');
await upload.init();
expect(await upload.generateId()).toBe('tus-a-b-100-1016975905');

options = Object.assign(optionsWithMeta, {
metadata: { documentTypeId: 'e' },
});

upload = new Upload(file, options);
expect(upload.generateId()).toBe('tus-a-b-100-1016975906');
await upload.init();
expect(await upload.generateId()).toBe('tus-a-b-100-1016975906');
});
});

describe('utils', () => {
it('should check filePath for slashes', () => {
it('should check filePath for slashes', async () => {
const file1 = Buffer.from('hello world!'.split(''));
file1.name = '\\bad\\file\\path\\file.pdf';
const upload1 = new Upload(
file1,
Object.assign(options, { stripFileNamePathSegments: false })
);
await upload1.init();
expect(upload1.trimFileName(file1.name)).toBe(file1.name);

const file2 = Buffer.from('hello world!'.split(''));
file2.name = '\\bad\\file\\path\\file2.pdf';
const upload2 = new Upload(file2, optionsWithMeta);
await upload2.init();
expect(upload2.trimFileName(file2.name)).toBe('file2.pdf');

const file3 = Buffer.from('hello world!'.split(''));
file3.name = '/bad/file/path/file3.pdf';
const upload3 = new Upload(file3, optionsWithMeta);
await upload3.init();
expect(upload3.trimFileName(file3.name)).toBe('file3.pdf');

const file4 = Buffer.from('hello world!'.split(''));
file4.name = 'goodFileName.pdf';
const upload4 = new Upload(file4, optionsWithMeta);
await upload4.init();
expect(upload4.trimFileName(file4.name)).toBe('goodFileName.pdf');
});

it('should pass status of decrypting', () => {
it('should pass status of decrypting', async () => {
const file = Buffer.from('hello world'.split(''));
file.name = 'decryptThisFile.png';
const upload = new Upload(file, options);
await upload.init();
upload.setError('encrypted', 'Encrypted files require a password');
upload.setError('decrypting', 'Decrypting file');
expect(upload.status).toBe('decrypting');
});

it('should validate file name', () => {
it('should validate file name', async () => {
const file = Buffer.from('hello world'.split(''));
file.name = 'good file name.pdf';
const upload = new Upload(
file,
Object.assign(options, { allowedFileNameCharacters: 'a-zA-Z0-9_ ' })
);
await upload.init();
expect(upload.isValidFile()).toBeTruthy();

const file2 = Buffer.from('hello world'.split(''));
file2.name = 'Bad-file-name.pdf';
const upload2 = new Upload(
file2,
Object.assign(options, { allowedFileNameCharacters: 'a-zA-Z0-9 _' })
);
await upload2.init();
expect(upload2.isValidFile()).toBeFalsy();

const file3 = Buffer.from('hello world'.split(''));
file3.name = '123File(1).xlsx';
const upload3 = new Upload(
file3,
Object.assign(options, { allowedFileNameCharacters: '_a-zA-Z0-9 ' })
);
await upload3.init();
expect(upload3.isValidFile()).toBeFalsy();

const file4 = Buffer.from('hello world'.split(''));
file4.name = 'fileName';
const upload4 = new Upload(
file4,
Object.assign(options, { allowedFileNameCharacters: '_a-zA-Z0-9 ' })
);
await upload4.init();
expect(upload4.isValidFile()).toBeTruthy();
});

Expand All @@ -208,7 +215,7 @@ describe('upload-core', () => {
afterEach(() => {
xhrMock.teardown();
});
it('should upload a file', done => {
it('should upload a file', async done => {
nock('https://dev.local')
.post('/ms/api/availity/internal/core/vault/upload/v1/resumable/a/')
.reply(
Expand Down Expand Up @@ -242,6 +249,7 @@ describe('upload-core', () => {
const file = Buffer.from('hello world!');
file.name = 'a';
const upload = new Upload(file, options);
await upload.init();
const success = jest.fn();
upload.onSuccess.push(success);
upload.onSuccess.push(() => {
Expand All @@ -258,15 +266,15 @@ describe('upload-core', () => {
},
});

upload.start();
await upload.start();
});

// it('should resume an upload from a stored url', done => {
// window.localStorage.setItem(
// 'fingerprinted',
// 'http://tus.io/uploads/resuming'
// );
// });
it('should resume an upload from a stored url', () => {
window.localStorage.setItem(
'fingerprinted',
'http://tus.io/uploads/resuming'
);
});
});
});
});
61 changes: 30 additions & 31 deletions packages/upload-core/src/upload.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import tus from 'tus-js-client';
import * as tus from 'tus-js-client';
import resolveUrl from '@availity/resolve-url';

// https://stackoverflow.com/questions/6122571/simple-non-secure-hash-function-for-javascript/8831937#8831937
Expand All @@ -21,7 +21,7 @@ const defaultOptions = {
pollingTime: 5000,
retryDelays: [0, 1000, 3000, 5000],
stripFileNamePathSegments: true,
fingerprint(file, options = {}, callback) {
fingerprint(file, options = {}) {
const attributes = [file.name, file.type, file.size, file.lastModified];
let attributesKey = 'tus-';
for (let i = 0; i < attributes.length; i++) {
Expand All @@ -41,11 +41,7 @@ const defaultOptions = {

const print = Math.abs(hashCode(signature));

if (callback) {
return callback(null, `${attributesKey}${print}`);
}

return `${attributesKey}${print}`;
return Promise.resolve(`${attributesKey}${print}`);
},
};

Expand Down Expand Up @@ -95,7 +91,6 @@ class Upload {
Object.assign(metadata, this.options.metadata);

const upload = new tus.Upload(this.file, {
resume: true,
endpoint: `${this.options.endpoint}/${this.options.bucketId}/`,
chunkSize: this.options.chunkSize,
retryDelays: this.options.retryDelays,
Expand All @@ -117,19 +112,17 @@ class Upload {
this.percentage = this.getPercentage();
this.onProgress.forEach(cb => cb());
},
onSuccess: () => {
const xhr = this.upload._xhr;
this.bytesScanned =
parseInt(xhr.getResponseHeader('AV-Scan-Bytes'), 10) || 0;
onAfterResponse: (req, res) => {
this.bytesScanned = parseInt(res.getHeader('AV-Scan-Bytes'), 10) || 0;
this.percentage = this.getPercentage();

const result = this.getResult(xhr);
const result = this.getResult(res, 'getHeader');

if (result.status === 'accepted') {
this.percentage = 100;
this.status = result.status;
this.errorMessage = null;
const references = xhr.getResponseHeader('references');
const references = res.getHeader('references');
if (references) {
this.references = JSON.parse(references);
}
Expand All @@ -142,11 +135,16 @@ class Upload {
return;
}

this.scan();
if (this.upload.url) {
this.scan();
}
},
});
this.upload = upload;
this.id = this.generateId();
}

async init() {
this.id = await this.generateId();
}

inStatusCategory(status, category) {
Expand Down Expand Up @@ -181,7 +179,7 @@ class Upload {
this.bytesScanned = parseInt(xhr.getResponseHeader('AV-Scan-Bytes'), 10);
this.percentage = this.getPercentage();

const result = this.getResult(xhr);
const result = this.getResult(xhr, 'getResponseHeader');

if (result.status === 'rejected') {
this.setError(result.status, result.message);
Expand Down Expand Up @@ -241,19 +239,24 @@ class Upload {
);
}

start() {
async start() {
if (!this.isValidFile()) {
return;
}
const previousUploads = await this.upload.findPreviousUploads();
if (previousUploads.length > 0) {
this.upload.resumeFromPreviousUpload(previousUploads[0]);
}
this.upload.start();
}

generateId() {
async generateId() {
const { fingerprint } = this.options;
return fingerprint(this.file, this.options).replace(/[^a-zA-Z0-9-]/g, '');
const print = await fingerprint(this.file, this.options);
return print.replace(/[^a-zA-Z0-9-]/g, '');
}

fingerprint(file, options = {}, callback) {
fingerprint(file, options = {}) {
const attributes = [file.name, file.type, file.size, file.lastModified];
let attributesKey = 'tus-';
for (let i = 0; i < attributes.length; i++) {
Expand All @@ -273,11 +276,7 @@ class Upload {

const print = Math.abs(hashCode(signature));

if (callback) {
return callback(null, `${attributesKey}${print}`);
}

return `${attributesKey}${print}`;
return Promise.resolve(`${attributesKey}${print}`);
}

sendPassword(pw) {
Expand Down Expand Up @@ -354,11 +353,11 @@ class Upload {
return fileName;
}

getResult(xhr) {
const scanResult = xhr.getResponseHeader('AV-Scan-Result');
const uploadResult = xhr.getResponseHeader('Upload-Result');
const decryptResult = xhr.getResponseHeader('Decryption-Result');
const msg = xhr.getResponseHeader('Upload-Message');
getResult(res, getHeader) {
const scanResult = res[getHeader]('AV-Scan-Result');
const uploadResult = res[getHeader]('Upload-Result');
const decryptResult = res[getHeader]('Decryption-Result');
const msg = res[getHeader]('Upload-Message');
if (scanResult === 'rejected') {
return { status: scanResult, message: msg || 'Failed AV scan' };
}
Expand Down
Loading