Merge pull request #710 from buster-so/staging

hotfix on multiple trigger tasks for download
This commit is contained in:
dal 2025-08-14 12:39:05 -06:00 committed by GitHub
commit e2a6454b33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 19 deletions

View File

@ -121,12 +121,19 @@ describe('downloadMetricFileHandler', () => {
organizationId: mockOrganizationId,
});
// Verify task was triggered with correct parameters
expect(tasks.trigger).toHaveBeenCalledWith('export-metric-data', {
metricId: mockMetricId,
userId: mockUser.id,
organizationId: mockOrganizationId,
});
// Verify task was triggered with correct parameters and idempotency
expect(tasks.trigger).toHaveBeenCalledWith(
'export-metric-data',
{
metricId: mockMetricId,
userId: mockUser.id,
organizationId: mockOrganizationId,
},
{
idempotencyKey: `export-${mockUser.id}-${mockMetricId}`,
idempotencyKeyTTL: '5m',
}
);
// Verify successful response
expect(result).toMatchObject({

View File

@ -50,12 +50,21 @@ export async function downloadMetricFileHandler(
}
try {
// Trigger the export task
const handle = await tasks.trigger('export-metric-data', {
metricId,
userId: user.id,
organizationId,
});
// Trigger the export task with idempotency to prevent duplicates
// If the same user tries to download the same metric within 5 minutes,
// it will return the existing task instead of creating a new one
const handle = await tasks.trigger(
'export-metric-data',
{
metricId,
userId: user.id,
organizationId,
},
{
idempotencyKey: `export-${user.id}-${metricId}`,
idempotencyKeyTTL: '5m', // 5 minutes TTL
}
);
// Poll for task completion with timeout
const startTime = Date.now();

View File

@ -22,9 +22,9 @@ export const MetricDataTruncatedWarning: React.FC<MetricDataTruncatedWarningProp
setIsDownloading(true);
setHasError(false);
// Create a timeout promise that rejects after 3 minutes
// Create a timeout promise that rejects after 2 minutes (matching backend timeout)
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Download timeout')), 3 * 60 * 1000); // 3 minutes
setTimeout(() => reject(new Error('Download timeout')), 2 * 60 * 1000); // 2 minutes
});
// Race between the API call and the timeout
@ -36,14 +36,17 @@ export const MetricDataTruncatedWarning: React.FC<MetricDataTruncatedWarningProp
// Simply navigate to the download URL
// The response-content-disposition header will force a download
window.location.href = response.downloadUrl;
// Keep button disabled for longer since download is async
// User can click again after 5 seconds if needed
setTimeout(() => {
setIsDownloading(false);
}, 5000);
} catch (error) {
console.error('Failed to download metric file:', error);
setHasError(true);
} finally {
// Add a small delay before removing loading state since download happens async
setTimeout(() => {
setIsDownloading(false);
}, 1000);
// Re-enable button immediately on error so user can retry
setIsDownloading(false);
}
};