demarches-normaliennes/app/javascript/shared/activestorage/uploader.js
Pierre de La Morinerie 432967bd76 javascript: make Uploader always throw the same kind of errors
A DirectUpload may fail for several reasons, and return many types of
errors (string, xhr response, Error objects, etc).

For convenience, wrap all these errors in a FileUploadError object.

- It makes easier for clients of the Uploader class to handle errors;
- It allows to propagate the error code and failure responsability.
2020-04-16 11:20:45 +02:00

95 lines
2.5 KiB
JavaScript

import { DirectUpload } from '@rails/activestorage';
import { ajax } from '@utils';
import ProgressBar from './progress-bar';
import FileUploadError, { errorFromDirectUploadMessage, ERROR_CODE_ATTACH } from './file-upload-error';
/**
Uploader class is a delegate for DirectUpload instance
used to track lifecycle and progress of an upload.
*/
export default class Uploader {
constructor(input, file, directUploadUrl, autoAttachUrl) {
this.directUpload = new DirectUpload(file, directUploadUrl, this);
this.progressBar = new ProgressBar(input, this.directUpload.id, file);
this.autoAttachUrl = autoAttachUrl;
}
/**
Upload (and optionally attach) the file.
Returns the blob signed id on success.
Throws a FileUploadError on failure.
*/
async start() {
this.progressBar.start();
try {
let blobSignedId = await this._upload();
if (this.autoAttachUrl) {
await this._attach(blobSignedId);
}
this.progressBar.end();
this.progressBar.destroy();
return blobSignedId;
} catch (error) {
this.progressBar.error(error.message);
throw error;
}
}
/**
Upload the file using the DirectUpload instance, and return the blob signed_id.
Throws a FileUploadError on failure.
*/
async _upload() {
return new Promise((resolve, reject) => {
this.directUpload.create((errorMsg, attributes) => {
if (errorMsg) {
let error = errorFromDirectUploadMessage(errorMsg);
reject(error);
} else {
resolve(attributes.signed_id);
}
});
});
}
/**
Attach the file by sending a POST request to the autoAttachUrl.
Throws a FileUploadError on failure (containing the first validation
error message, if any).
*/
async _attach(blobSignedId) {
const attachmentRequest = {
url: this.autoAttachUrl,
type: 'PUT',
data: `blob_signed_id=${blobSignedId}`
};
try {
await ajax(attachmentRequest);
} catch (e) {
let message = e.response && e.response.errors && e.response.errors[0];
throw new FileUploadError(
message || 'Error attaching file.',
e.xhr.status,
ERROR_CODE_ATTACH
);
}
}
uploadRequestDidProgress(event) {
const progress = (event.loaded / event.total) * 100;
if (progress) {
this.progressBar.progress(progress);
}
}
directUploadWillStoreFileWithXHR(xhr) {
xhr.upload.addEventListener('progress', event =>
this.uploadRequestDidProgress(event)
);
}
}