This commit is contained in:
@@ -53,6 +53,7 @@ import { createPricingProviderRegistry } from "../modules/pricing/providers/pric
|
||||
import { createFixturePricingProvider } from "../modules/pricing/providers/fixture-pricing-provider.js";
|
||||
import { createHttpPricingProvider } from "../modules/pricing/providers/http-pricing-provider.js";
|
||||
import { createAdminExportService } from "../modules/exports/services/admin-export-service.js";
|
||||
import { createInventoryExportJobRepository } from "../modules/exports/repositories/inventory-export-job-repository.js";
|
||||
import { createSellerSettingsRepository } from "../modules/settings/repositories/seller-settings-repository.js";
|
||||
import { createSellerSettingsService } from "../modules/settings/services/seller-settings-service.js";
|
||||
|
||||
@@ -166,6 +167,7 @@ async function startServer() {
|
||||
}),
|
||||
inventoryItemService,
|
||||
adminExportService: createAdminExportService({
|
||||
inventoryExportJobRepository: createInventoryExportJobRepository(database.db),
|
||||
inventoryItemService,
|
||||
}),
|
||||
sellerSettingsService,
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { and, asc, eq } from 'drizzle-orm';
|
||||
|
||||
import { inventoryExportJobs } from '../../../infrastructure/database/schema.js';
|
||||
|
||||
export function createInventoryExportJobRepository(db) {
|
||||
return {
|
||||
async create(job) {
|
||||
const [created] = await db.insert(inventoryExportJobs).values(job).returning();
|
||||
return created;
|
||||
},
|
||||
|
||||
async findByIdForSeller(id, sellerId) {
|
||||
const [job] = await db.select().from(inventoryExportJobs).where(and(
|
||||
eq(inventoryExportJobs.id, id),
|
||||
eq(inventoryExportJobs.sellerId, sellerId),
|
||||
));
|
||||
return job ?? null;
|
||||
},
|
||||
|
||||
async claimNext() {
|
||||
return db.transaction(async (transaction) => {
|
||||
const [job] = await transaction.select().from(inventoryExportJobs)
|
||||
.where(eq(inventoryExportJobs.status, 'PENDING'))
|
||||
.orderBy(asc(inventoryExportJobs.createdAt))
|
||||
.limit(1).for('update', { skipLocked: true });
|
||||
if (!job) return null;
|
||||
const [claimed] = await transaction.update(inventoryExportJobs)
|
||||
.set({ status: 'PROCESSING', updatedAt: new Date(), error: null })
|
||||
.where(and(eq(inventoryExportJobs.id, job.id), eq(inventoryExportJobs.status, 'PENDING')))
|
||||
.returning();
|
||||
return claimed ?? null;
|
||||
});
|
||||
},
|
||||
|
||||
async complete(id, output) {
|
||||
const [completed] = await db.update(inventoryExportJobs)
|
||||
.set({ status: 'COMPLETED', output, updatedAt: new Date(), error: null })
|
||||
.where(and(eq(inventoryExportJobs.id, id), eq(inventoryExportJobs.status, 'PROCESSING')))
|
||||
.returning();
|
||||
return completed ?? null;
|
||||
},
|
||||
|
||||
async fail(id, error) {
|
||||
const [failed] = await db.update(inventoryExportJobs)
|
||||
.set({ status: 'FAILED', error, updatedAt: new Date() })
|
||||
.where(and(eq(inventoryExportJobs.id, id), eq(inventoryExportJobs.status, 'PROCESSING')))
|
||||
.returning();
|
||||
return failed ?? null;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -48,4 +48,34 @@ export async function registerAdminExportRoutes(app, options) {
|
||||
.header('content-disposition', 'attachment; filename="inventory.xls"')
|
||||
.send(data);
|
||||
});
|
||||
|
||||
app.post('/exports/inventory.json/jobs', async (request, reply) => {
|
||||
const job = await adminExportService.enqueueInventoryJson({
|
||||
sellerId: request.admin.sellerId,
|
||||
});
|
||||
return reply.code(202).send({
|
||||
data: { id: job.id, status: job.status, createdAt: job.createdAt },
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/exports/jobs/:id', 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);
|
||||
}
|
||||
return reply.send({
|
||||
data: {
|
||||
id: job.id,
|
||||
format: job.format,
|
||||
status: job.status,
|
||||
error: job.error ?? null,
|
||||
output: job.status === 'COMPLETED' ? job.output : null,
|
||||
createdAt: job.createdAt,
|
||||
updatedAt: job.updatedAt,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import { toAdminInventoryItemDto } from '@pokemon-inventory/contracts/inventory';
|
||||
|
||||
export function createAdminExportService({ inventoryItemService }) {
|
||||
export function createAdminExportService({ inventoryItemService, inventoryExportJobRepository }) {
|
||||
async function inventoryRows(sellerId) {
|
||||
const inventoryItems = await inventoryItemService.listBySellerId(sellerId);
|
||||
return inventoryItems.map(toAdminInventoryItemDto);
|
||||
}
|
||||
|
||||
return {
|
||||
async enqueueInventoryJson({ sellerId }) {
|
||||
return inventoryExportJobRepository.create({ sellerId, format: 'JSON' });
|
||||
},
|
||||
|
||||
async getExportJob({ sellerId, jobId }) {
|
||||
return inventoryExportJobRepository.findByIdForSeller(jobId, sellerId);
|
||||
},
|
||||
|
||||
async inventoryJson({ sellerId }) {
|
||||
return inventoryRows(sellerId);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user