diff --git a/app/javascript/dashboard/routes/dashboard/settings/data/Show.vue b/app/javascript/dashboard/routes/dashboard/settings/data/Show.vue
index 2bab87f75..89f1d651e 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/data/Show.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/data/Show.vue
@@ -36,6 +36,7 @@ const errorsOpen = ref(true);
const skipLogsOpen = ref(true);
let pollTimer;
let isPageActive = false;
+let importRequestVersion = 0;
const hasActiveImport = computed(() => isActiveImport(dataImport.value));
@@ -51,6 +52,7 @@ const fetchImport = async ({
manual = false,
requestedSkipLogsType = selectedSkipLogsType.value,
} = {}) => {
+ const requestVersion = importRequestVersion;
if (showLoader) {
isLoading.value = true;
} else if (manual) {
@@ -61,6 +63,8 @@ const fetchImport = async ({
const response = await DataImportsAPI.show(route.params.dataImportId, {
skip_logs_type: requestedSkipLogsType || undefined,
});
+ if (requestVersion !== importRequestVersion) return;
+
dataImport.value = response.data;
selectedSkipLogsType.value =
response.data.skip_logs_filters?.selected_source_object_type ||
@@ -106,6 +110,13 @@ const refreshImportInBackground = async () => {
}
};
+const startPolling = () => {
+ stopPolling();
+ if (!isPageActive || !hasActiveImport.value) return;
+
+ pollTimer = window.setInterval(refreshImportInBackground, POLL_INTERVAL_MS);
+};
+
const abandonImport = async () => {
isAbandoning.value = true;
try {
@@ -120,6 +131,8 @@ const abandonImport = async () => {
const retryImport = async () => {
isRetrying.value = true;
+ importRequestVersion += 1;
+ stopPolling();
try {
const response = await DataImportsAPI.retry(dataImport.value.id);
dataImport.value = response.data;
@@ -128,6 +141,7 @@ const retryImport = async () => {
useAlert(t('DATA_IMPORTS.ALERTS.IMPORT_RETRY_FAILED'));
} finally {
isRetrying.value = false;
+ if (hasActiveImport.value) startPolling();
}
};
@@ -164,13 +178,6 @@ const downloadSkipLogs = async () => {
}
};
-const startPolling = () => {
- stopPolling();
- if (!isPageActive || !hasActiveImport.value) return;
-
- pollTimer = window.setInterval(refreshImportInBackground, POLL_INTERVAL_MS);
-};
-
const handleVisibilityChange = () => {
if (isPageActive && !document.hidden && hasActiveImport.value) {
refreshImportInBackground();
diff --git a/app/javascript/dashboard/routes/dashboard/settings/data/specs/showActions.spec.js b/app/javascript/dashboard/routes/dashboard/settings/data/specs/showActions.spec.js
index eb7b83393..8552e03de 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/data/specs/showActions.spec.js
+++ b/app/javascript/dashboard/routes/dashboard/settings/data/specs/showActions.spec.js
@@ -3,6 +3,7 @@ import { KeepAlive, defineComponent, h, nextTick } from 'vue';
import { useAlert } from 'dashboard/composables';
import DataImportsAPI from 'dashboard/api/dataImports';
import Show from '../Show.vue';
+import { POLL_INTERVAL_MS } from '../importStatus';
vi.mock('dashboard/api/dataImports', () => ({
default: {
@@ -35,8 +36,28 @@ const SettingsLayoutStub = {
const ImportDetailHeaderStub = {
name: 'ImportDetailHeader',
+ props: {
+ dataImport: {
+ type: Object,
+ default: null,
+ },
+ },
emits: ['retry'],
- template: '',
+ template: `
+
+ `,
+};
+
+const deferredRequest = () => {
+ let resolve;
+ const promise = new Promise(resolvePromise => {
+ resolve = resolvePromise;
+ });
+ return { promise, resolve };
};
const mountShow = () => {
@@ -101,4 +122,40 @@ describe('data import detail actions', () => {
expect(useAlert).toHaveBeenCalledWith('DATA_IMPORTS.ALERTS.IMPORT_RETRIED');
wrapper.unmount();
});
+
+ it('ignores an older poll response after retry succeeds', async () => {
+ const pollRequest = deferredRequest();
+ DataImportsAPI.retry.mockResolvedValue({
+ data: {
+ id: 1,
+ status: 'pending',
+ stalled: false,
+ skip_logs_filters: {},
+ },
+ });
+ const wrapper = mountShow();
+ await nextTick();
+ await flushPromises();
+
+ DataImportsAPI.show.mockReturnValueOnce(pollRequest.promise);
+ await vi.advanceTimersByTimeAsync(POLL_INTERVAL_MS);
+ expect(DataImportsAPI.show).toHaveBeenCalledTimes(2);
+
+ await wrapper.find('[data-test="retry"]').trigger('click');
+ await flushPromises();
+ expect(wrapper.find('[data-test="retry"]').exists()).toBe(false);
+
+ pollRequest.resolve({
+ data: {
+ id: 1,
+ status: 'processing',
+ stalled: true,
+ skip_logs_filters: {},
+ },
+ });
+ await flushPromises();
+
+ expect(wrapper.find('[data-test="retry"]').exists()).toBe(false);
+ wrapper.unmount();
+ });
});