This commit is contained in:
@@ -22,6 +22,7 @@ import { createInventoryItemService } from '../modules/inventory/services/invent
|
||||
import { createPublicInventoryService } from '../modules/inventory/services/public-inventory-service.js';
|
||||
import { createPublicImageService } from '../modules/inventory/services/public-image-service.js';
|
||||
import { createInventoryImageRepository } from '../modules/inventory/repositories/inventory-image-repository.js';
|
||||
import { createImageDerivativeJobRepository } from '../modules/inventory/repositories/image-derivative-job-repository.js';
|
||||
import { createInventoryImageService } from '../modules/inventory/services/inventory-image-service.js';
|
||||
import { createLocalImageStorage } from '../infrastructure/images/local-image-storage.js';
|
||||
import { createConditionRepository } from '../modules/inventory/repositories/condition-repository.js';
|
||||
@@ -51,6 +52,7 @@ async function startServer() {
|
||||
const productRepository = createProductRepository(database.db);
|
||||
const inventoryItemRepository = createInventoryItemRepository(database.db);
|
||||
const inventoryImageRepository = createInventoryImageRepository(database.db);
|
||||
const imageDerivativeJobRepository = createImageDerivativeJobRepository(database.db);
|
||||
const listingRepository = createListingRepository(database.db);
|
||||
const sellerRepository = createSellerRepository(database.db);
|
||||
const conditionRepository = createConditionRepository(database.db);
|
||||
@@ -104,6 +106,7 @@ async function startServer() {
|
||||
publicInventoryService: createPublicInventoryService({ inventoryItemRepository, publicImageService }),
|
||||
inventoryImageService: createInventoryImageService({
|
||||
imageStorage: createLocalImageStorage(process.env.IMAGE_STORAGE_ROOT ?? '.local/image-storage'),
|
||||
imageDerivativeJobRepository,
|
||||
inventoryImageRepository,
|
||||
}),
|
||||
listingService: createListingService({ listingRepository, inventoryImageRepository }),
|
||||
|
||||
@@ -264,6 +264,22 @@ export const inventoryImages = pgTable(
|
||||
(table) => [index('inventory_images_inventory_item_id_index').on(table.inventoryItemId)],
|
||||
);
|
||||
|
||||
export const imageDerivativeJobs = pgTable(
|
||||
'image_derivative_jobs',
|
||||
{
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
inventoryImageId: uuid('inventory_image_id')
|
||||
.notNull()
|
||||
.references(() => inventoryImages.id),
|
||||
sourceKey: text('source_key').notNull(),
|
||||
status: text('status').default('PENDING').notNull(),
|
||||
derivatives: jsonb('derivatives').default([]).notNull(),
|
||||
error: text('error'),
|
||||
...timestamps,
|
||||
},
|
||||
(table) => [index('image_derivative_jobs_image_id_index').on(table.inventoryImageId)],
|
||||
);
|
||||
|
||||
export const listings = pgTable('listings', {
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
inventoryItemId: uuid('inventory_item_id')
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { asc, eq } from 'drizzle-orm';
|
||||
|
||||
import { imageDerivativeJobs } from '../../../infrastructure/database/schema.js';
|
||||
|
||||
export function createImageDerivativeJobRepository(db) {
|
||||
return {
|
||||
async create(job) {
|
||||
const [createdJob] = await db.insert(imageDerivativeJobs).values(job).returning();
|
||||
return createdJob;
|
||||
},
|
||||
|
||||
async listByImageId(inventoryImageId) {
|
||||
return db
|
||||
.select()
|
||||
.from(imageDerivativeJobs)
|
||||
.where(eq(imageDerivativeJobs.inventoryImageId, inventoryImageId))
|
||||
.orderBy(asc(imageDerivativeJobs.createdAt));
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createApplicationError } from '../../../shared/errors/application-error.js';
|
||||
import { validateImageSignature, validateInventoryImageMetadata } from '../domain/inventory-image.js';
|
||||
|
||||
export function createInventoryImageService({ inventoryImageRepository, imageStorage }) {
|
||||
export function createInventoryImageService({ inventoryImageRepository, imageDerivativeJobRepository, imageStorage }) {
|
||||
return {
|
||||
async create(input) {
|
||||
validateInventoryImageMetadata(input);
|
||||
@@ -14,6 +14,12 @@ export function createInventoryImageService({ inventoryImageRepository, imageSto
|
||||
throw createApplicationError('INVENTORY_ITEM_NOT_FOUND', 'Inventory item was not found.', 404);
|
||||
}
|
||||
|
||||
await imageDerivativeJobRepository?.create({
|
||||
inventoryImageId: image.id,
|
||||
sourceKey: image.storageKey,
|
||||
status: 'PENDING',
|
||||
});
|
||||
|
||||
return image;
|
||||
},
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ const input = {
|
||||
|
||||
test('stores validated upload bytes before persisting image metadata', async () => {
|
||||
const stored = [];
|
||||
const jobs = [];
|
||||
const service = createInventoryImageService({
|
||||
imageStorage: {
|
||||
async put(value) { stored.push(value); },
|
||||
@@ -27,6 +28,9 @@ test('stores validated upload bytes before persisting image metadata', async ()
|
||||
return { id: 'image-1', ...image };
|
||||
},
|
||||
},
|
||||
imageDerivativeJobRepository: {
|
||||
async create(job) { jobs.push(job); },
|
||||
},
|
||||
});
|
||||
|
||||
const image = await service.upload(input);
|
||||
@@ -36,6 +40,11 @@ test('stores validated upload bytes before persisting image metadata', async ()
|
||||
assert.equal(image.id, 'image-1');
|
||||
assert.equal(image.processingStatus, 'PENDING');
|
||||
assert.equal('data' in image, false);
|
||||
assert.deepEqual(jobs, [{
|
||||
inventoryImageId: 'image-1',
|
||||
sourceKey: input.storageKey,
|
||||
status: 'PENDING',
|
||||
}]);
|
||||
});
|
||||
|
||||
test('removes stored bytes when metadata persistence fails', async () => {
|
||||
|
||||
Reference in New Issue
Block a user