demarches-normaliennes/app/javascript/new_design/dossiers/auto-upload-controller.js
Pierre de La Morinerie 444732b117 javascript: don't allow to retry on direct upload 422
When the authenticity token is invalid, the creation of the blob before
the direct upload returns a 422.

In that case, the token will never become valid again, and it is useless
to try again. Don’t show the "Retry" button in this case.

NB: of course the real fix is to understand why the authenticity token
is so often invalid – but this will be for later.
2020-04-08 15:48:23 +02:00

150 lines
4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Uploader from '../../shared/activestorage/uploader';
import ProgressBar from '../../shared/activestorage/progress-bar';
import { ajax, show, hide, toggle } from '@utils';
// Given a file input in a champ with a selected file, upload a file,
// then attach it to the dossier.
//
// On success, the champ is replaced by an HTML fragment describing the attachment.
// On error, a error message is displayed above the input.
export default class AutoUploadController {
constructor(input, file) {
this.input = input;
this.file = file;
}
async start() {
try {
this._begin();
// Sanity checks
const autoAttachUrl = this.input.dataset.autoAttachUrl;
if (!autoAttachUrl) {
throw new Error('Lattribut "data-auto-attach-url" est manquant');
}
// Upload the file (using Direct Upload)
let blobSignedId = await this._upload();
// Attach the blob to the champ
// (The request responds with Javascript, which displays the attachment HTML fragment).
await this._attach(blobSignedId, autoAttachUrl);
// Everything good: clear the original file input value
this.input.value = null;
} catch (error) {
this._failed(error);
throw error;
} finally {
this._done();
}
}
_begin() {
this.input.disabled = true;
this._hideErrorMessage();
}
async _upload() {
const uploader = new Uploader(
this.input,
this.file,
this.input.dataset.directUploadUrl
);
return await uploader.start();
}
async _attach(blobSignedId, autoAttachUrl) {
// Now that the upload is done, display a new progress bar
// to show that the attachment request is still pending.
const progressBar = new ProgressBar(
this.input,
`${this.input.id}-progress-bar`,
this.file
);
progressBar.progress(100);
progressBar.end();
const attachmentRequest = {
url: autoAttachUrl,
type: 'PUT',
data: `blob_signed_id=${blobSignedId}`
};
await ajax(attachmentRequest);
// The progress bar has been destroyed by the attachment HTML fragment that replaced the input,
// so no further cleanup is needed.
}
_failed(error) {
if (!document.body.contains(this.input)) {
return;
}
let progressBar = this.input.parentElement.querySelector('.direct-upload');
if (progressBar) {
progressBar.remove();
}
this._displayErrorMessage(error);
}
_done() {
this.input.disabled = false;
}
_isError422(error) {
// Ajax errors have an xhr attribute
if (error && error.xhr && error.xhr.status == 422) return true;
// Rails DirectUpload errors are returned as a String, e.g. 'Error creating Blob for "Demain.txt". Status: 422'
if (error && error.toString().includes('422')) return true;
return false;
}
_messageFromError(error) {
let allowRetry = !this._isError422(error);
if (
error.xhr &&
error.xhr.status == 422 &&
error.response &&
error.response.errors &&
error.response.errors[0]
) {
return {
title: error.response.errors[0],
description: '',
retry: allowRetry
};
} else {
return {
title: 'Une erreur sest produite pendant lenvoi du fichier.',
description: error.message || error.toString(),
retry: allowRetry
};
}
}
_displayErrorMessage(error) {
let errorNode = this.input.parentElement.querySelector('.attachment-error');
if (errorNode) {
show(errorNode);
let message = this._messageFromError(error);
errorNode.querySelector('.attachment-error-title').textContent =
message.title || '';
errorNode.querySelector('.attachment-error-description').textContent =
message.description || '';
toggle(errorNode.querySelector('.attachment-error-retry'), message.retry);
}
}
_hideErrorMessage() {
let errorElement = this.input.parentElement.querySelector(
'.attachment-error'
);
if (errorElement) {
hide(errorElement);
}
}
}