feat: download completed export jobs
Quality / test (push) Canceled after 0s

This commit is contained in:
2026-09-10 01:31:09 -04:00
parent ad8f675f5e
commit ec322df25c
5 changed files with 34 additions and 0 deletions
@@ -78,4 +78,21 @@ export async function registerAdminExportRoutes(app, options) {
},
});
});
app.get('/exports/jobs/:id/download', async (request, reply) => {
const job = await adminExportService.getExportJob({
sellerId: request.admin.sellerId,
jobId: request.params.id,
});
if (!job) {
throw createApplicationError('EXPORT_JOB_NOT_FOUND', 'Export job was not found.', 404);
}
if (job.status !== 'COMPLETED' || !Array.isArray(job.output)) {
throw createApplicationError('EXPORT_NOT_READY', 'Export is not ready for download.', 409);
}
return reply
.type('application/json; charset=utf-8')
.header('content-disposition', 'attachment; filename="inventory.json"')
.send(JSON.stringify(job.output));
});
}
@@ -125,4 +125,9 @@ test('enqueues and polls an asynchronous inventory export', async (testContext)
assert.equal(enqueue.json().data.id, 'job-1');
assert.equal(poll.statusCode, 200);
assert.deepEqual(poll.json().data.output, [{ id: 'item-1' }]);
const download = await app.inject({ method: 'GET', url: '/api/v1/admin/exports/jobs/job-1/download' });
assert.equal(download.statusCode, 200);
assert.equal(download.headers['content-disposition'], 'attachment; filename="inventory.json"');
assert.deepEqual(JSON.parse(download.body), [{ id: 'item-1' }]);
});
+10
View File
@@ -55,6 +55,7 @@ const inventory = ref([]);
const selectedInventoryIds = ref([]);
const inventoryTags = ref("");
const exportStatus = ref("");
const exportJobId = ref("");
const listings = ref([]);
const sales = ref([]);
const acquisitions = ref([]);
@@ -262,6 +263,7 @@ async function enqueueInventoryExport() {
exportStatus.value = "Starting export...";
try {
const job = await enqueueAdminInventoryExport(csrfToken());
exportJobId.value = job.id;
const completed = await getAdminInventoryExportJob(job.id);
exportStatus.value = `Export ${completed.status.toLowerCase()}.`;
} catch (requestError) {
@@ -542,6 +544,14 @@ onMounted(load);
Queue JSON export
</button>
<span v-if="exportStatus" role="status">{{ exportStatus }}</span>
<a
v-if="exportJobId && exportStatus === 'Export completed.'"
class="button button--secondary"
:href="`/api/v1/admin/exports/jobs/${exportJobId}/download`"
download="inventory.json"
>
Download queued export
</a>
</p>
<p v-if="isLoading" class="state state--loading" role="status">
Loading inventory...