[FIX] base_import: prevent the list out of range error

When the user uploads a file the length of the list will return 1, but when
it tries to retrieve a file which is not in the list or wants to upload another
file while the process is ongoing it will throw a IndexError.

After this commit, the race condition is satisfied i.e. now the user will not
able to upload the file while the uploading is still going on.

sentry-3938949468

closes odoo/odoo#120012

X-original-commit: 482e0034e1a5882ea1e0bc4ddb56d8ef5f5a1a92
Signed-off-by: Samuel Degueldre <sad@odoo.com>
This commit is contained in:
Ayushi Gorai
2023-04-27 18:25:40 +02:00
parent 9fa61411c2
commit c51c66805d
3 changed files with 35 additions and 3 deletions
@@ -2,7 +2,7 @@
import { useService } from "@web/core/utils/hooks";
import { Component, onMounted, useRef } from "@odoo/owl";
import { Component, onMounted, useRef, useState } from "@odoo/owl";
/**
* Custom file input
@@ -26,6 +26,10 @@ export class FileInput extends Component {
setup() {
this.http = useService("http");
this.fileInputRef = useRef("file-input");
this.state = useState({
// Disables upload button if currently uploading.
isDisable: false,
});
onMounted(() => {
if (this.props.autoOpen) {
@@ -70,12 +74,14 @@ export class FileInput extends Component {
* - resId: the id of the resModel target instance
*/
async onFileInputChange() {
this.state.isDisable = true;
const parsedFileData = await this.uploadFiles(this.httpParams);
// When calling onUpload, also pass the files to allow to get data like their names
this.props.onUpload(parsedFileData, this.fileInputRef.el ? this.fileInputRef.el.files : []);
// Because the input would not trigger this method if the same file name is uploaded,
// we must clear the value after handling the upload
this.fileInputRef.el.value = null;
this.state.isDisable = false;
}
/**
@@ -2,7 +2,7 @@
<templates xml:space="preserve">
<t t-name="web.FileInput" owl="1">
<span class="o_file_input" t-att-class="{ 'd-none': props.hidden }" aria-atomic="true">
<span class="o_file_input" t-att-class="{ 'd-none': props.hidden, 'show opacity-50 pe-none': state.isDisable}" aria-atomic="true">
<span t-if="!props.hidden" class="o_file_input_trigger" t-on-click.prevent="onTriggerClicked">
<t t-slot="default">
<button class="btn btn-primary">Choose File</button>
@@ -14,6 +14,7 @@
t-att-multiple="props.multiUpload"
t-att-accept="props.acceptedFileExtensions"
t-ref="file-input"
t-att-disabled="state.isDisable"
t-on-change="onFileInputChange"
/>
</span>
@@ -1,7 +1,7 @@
/** @odoo-module **/
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
import { editInput, getFixture, mount, patchWithCleanup, triggerEvent } from "@web/../tests/helpers/utils";
import { editInput, getFixture, mount, patchWithCleanup, triggerEvent, makeDeferred, nextTick } from "@web/../tests/helpers/utils";
import { FileInput } from "@web/core/file_input/file_input";
import { registry } from "@web/core/registry";
@@ -138,4 +138,29 @@ QUnit.module("Components", ({ beforeEach }) => {
await editInput(target, ".o_file_input input", file);
assert.verifySteps(["fake_file.txt"], "file has been uploaded a second time");
});
QUnit.test("Upload button is disabled if attachment upload is not finished", async (assert) => {
assert.expect(2);
const uploadedPromise = makeDeferred();
await createFileInput({
mockPost: async (route, params) => {
if (route === "/web/binary/upload_attachment") {
await uploadedPromise;
}
return "[]";
},
props: {},
});
//enable button
const input = target.querySelector(".o_file_input input");
await triggerEvent(input, null, "change", {}, { skipVisibilityCheck: true });
//disable button
assert.ok(input.disabled, "the upload button should be disabled on upload");
uploadedPromise.resolve();
await nextTick();
assert.notOk(input.disabled, "the upload button should be enabled for upload");
});
});