From 24b3b4063d7d61bc1ec264df7dad9e4d3d0dc67a Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 27 May 2026 21:41:33 +0500 Subject: [PATCH] refactor: extract validateGalleryImages helper --- .../__tests__/validate-gallery-images.test.js | 32 ++++++++++++++++ server/src/lib/validate-gallery-images.js | 23 ++++++++++++ server/src/routes/api/admin-products.js | 37 +++++-------------- 3 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 server/src/lib/__tests__/validate-gallery-images.test.js create mode 100644 server/src/lib/validate-gallery-images.js diff --git a/server/src/lib/__tests__/validate-gallery-images.test.js b/server/src/lib/__tests__/validate-gallery-images.test.js new file mode 100644 index 0000000..6818bd6 --- /dev/null +++ b/server/src/lib/__tests__/validate-gallery-images.test.js @@ -0,0 +1,32 @@ +import { describe, it, expect, vi } from 'vitest' +import { validateGalleryImages } from '../validate-gallery-images.js' + +describe('validateGalleryImages', () => { + it('returns null when urls is empty', async () => { + const prisma = { galleryImage: { findMany: vi.fn() } } + const result = await validateGalleryImages(prisma, []) + expect(result).toBeNull() + }) + + it('throws 400 when image not found', async () => { + const prisma = { galleryImage: { findMany: vi.fn().mockResolvedValue([]) } } + await expect(validateGalleryImages(prisma, ['/uploads/missing.jpg'])).rejects.toMatchObject({ statusCode: 400 }) + }) + + it('throws 400 when image not yet resized', async () => { + const prisma = { + galleryImage: { findMany: vi.fn().mockResolvedValue([{ url: '/uploads/img.jpg', isResized: false }]) }, + } + await expect(validateGalleryImages(prisma, ['/uploads/img.jpg'])).rejects.toMatchObject({ statusCode: 400 }) + }) + + it('returns existing images when all valid and resized', async () => { + const images = [ + { url: '/uploads/img1.jpg', isResized: true }, + { url: '/uploads/img2.jpg', isResized: true }, + ] + const prisma = { galleryImage: { findMany: vi.fn().mockResolvedValue(images) } } + const result = await validateGalleryImages(prisma, ['/uploads/img1.jpg', '/uploads/img2.jpg']) + expect(result).toEqual(images) + }) +}) diff --git a/server/src/lib/validate-gallery-images.js b/server/src/lib/validate-gallery-images.js new file mode 100644 index 0000000..ec49047 --- /dev/null +++ b/server/src/lib/validate-gallery-images.js @@ -0,0 +1,23 @@ +export async function validateGalleryImages(prisma, urls) { + if (!urls || urls.length === 0) return null + + const existing = await prisma.galleryImage.findMany({ + where: { url: { in: urls } }, + select: { url: true, isResized: true }, + }) + + const galleryMap = new Map(existing.map((g) => [g.url, g])) + const notFound = urls.filter((u) => !galleryMap.has(u)) + if (notFound.length > 0) { + throw Object.assign(new Error(`Gallery images not found: ${notFound.join(', ')}`), { statusCode: 400 }) + } + + const notResized = urls.filter((u) => galleryMap.get(u) && !galleryMap.get(u).isResized) + if (notResized.length > 0) { + throw Object.assign(new Error('Some gallery images have not been processed yet. Please try again later.'), { + statusCode: 400, + }) + } + + return existing +} diff --git a/server/src/routes/api/admin-products.js b/server/src/routes/api/admin-products.js index 3992e43..5d8d1e3 100644 --- a/server/src/routes/api/admin-products.js +++ b/server/src/routes/api/admin-products.js @@ -1,4 +1,5 @@ import { prisma } from '../../lib/prisma.js' +import { validateGalleryImages } from '../../lib/validate-gallery-images.js' const CREATE_PRODUCT_SCHEMA = { body: { @@ -87,20 +88,10 @@ export async function registerAdminProductRoutes(fastify) { if (Array.isArray(body.imageUrls) && body.imageUrls.length > 0) { const urls = body.imageUrls.map((u) => String(u || '').trim()).filter(Boolean) if (urls.length > 0) { - const galleryImages = await prisma.galleryImage.findMany({ - where: { url: { in: urls } }, - select: { url: true, isResized: true }, - }) - const galleryMap = new Map(galleryImages.map((g) => [g.url, g])) - const notFound = urls.filter((u) => !galleryMap.has(u)) - const notResized = urls.filter((u) => galleryMap.get(u) && !galleryMap.get(u).isResized) - if (notFound.length > 0) { - return reply.code(400).send({ error: 'Некоторые изображения не найдены в галерее' }) - } - if (notResized.length > 0) { - return reply - .code(400) - .send({ error: 'Изображения должны быть обработаны (resize) перед прикреплением к товару' }) + try { + await validateGalleryImages(prisma, urls) + } catch (err) { + return reply.code(err.statusCode || 400).send({ error: err.message }) } } } @@ -214,20 +205,10 @@ export async function registerAdminProductRoutes(fastify) { if (body.imageUrls !== undefined && Array.isArray(body.imageUrls)) { const urls = body.imageUrls.map((u) => String(u || '').trim()).filter(Boolean) if (urls.length > 0) { - const galleryImages = await prisma.galleryImage.findMany({ - where: { url: { in: urls } }, - select: { url: true, isResized: true }, - }) - const galleryMap = new Map(galleryImages.map((g) => [g.url, g])) - const notFound = urls.filter((u) => !galleryMap.has(u)) - const notResized = urls.filter((u) => galleryMap.get(u) && !galleryMap.get(u).isResized) - if (notFound.length > 0) { - return reply.code(400).send({ error: 'Некоторые изображения не найдены в галерее' }) - } - if (notResized.length > 0) { - return reply - .code(400) - .send({ error: 'Изображения должны быть обработаны (resize) перед прикреплением к товару' }) + try { + await validateGalleryImages(prisma, urls) + } catch (err) { + return reply.code(err.statusCode || 400).send({ error: err.message }) } } }