server: rebuild runtime (colorsJson fix)

This commit is contained in:
2026-10-01 12:24:37 +03:30
parent 6c2e3fd786
commit ac0cd63dbd
4 changed files with 60 additions and 48 deletions
+22 -16
View File
@@ -38,26 +38,32 @@ describe('product image separation', () => {
]);
});
it('recovers color name from product.color when validation stripped nested fields', () => {
const db = serializeColorsJsonForDb(
[{ imageUrl: 'data:image/jpeg;base64,xyz' }],
it('reads object colorsJson with images for API colors/colorImages', () => {
const entries = parseColorEntries(
[
{
name: 'قرمز شرابی',
imageUrl: 'data:image/jpeg;base64,abc',
imageUrls: ['data:image/jpeg;base64,abc'],
},
],
'قرمز شرابی',
);
expect(db).toEqual([
{
name: 'قرمز شرابی',
imageUrl: 'data:image/jpeg;base64,xyz',
imageUrls: ['data:image/jpeg;base64,xyz'],
},
]);
const entries = parseColorEntries(db, 'قرمز شرابی');
expect(entries[0]?.imageUrls).toEqual(['data:image/jpeg;base64,xyz']);
expect(entries.map((e) => e.name)).toEqual(['قرمز شرابی']);
expect(entries[0]?.imageUrls[0]).toBe('data:image/jpeg;base64,abc');
});
it('falls back to product.color when colorsJson rows are empty objects', () => {
const entries = parseColorEntries([{}], 'قرمز شرابی');
expect(entries).toEqual([{ name: 'قرمز شرابی', imageUrls: [] }]);
it('fills color name from product.color when json row has only images', () => {
const entries = parseColorEntries(
[{ imageUrl: 'data:image/jpeg;base64,x', imageUrls: ['data:image/jpeg;base64,x'] }],
'قرمز شرابی',
);
expect(entries).toEqual([
{
name: 'قرمز شرابی',
imageUrl: 'data:image/jpeg;base64,x',
imageUrls: ['data:image/jpeg;base64,x'],
},
]);
});
it('does not merge color images into general list when only imageUrlsJson is set', () => {
+16 -17
View File
@@ -15,6 +15,20 @@ export class ProductColorJsonItemDto {
imageUrls?: string[];
}
function mergeColorImageUrls(
imageUrls?: string[],
imageUrl?: string,
): string[] {
const merged: string[] = [];
const push = (u?: string) => {
const t = u?.trim();
if (t && !merged.includes(t)) merged.push(t);
};
for (const u of imageUrls ?? []) push(u);
push(imageUrl);
return merged;
}
/** نرمال‌سازی colorsJson (رشتهٔ legacy یا آبجکت ادمین) قبل از اعتبارسنجی */
export function transformColorsJson(value: unknown): ProductColorJsonItemDto[] | undefined {
if (value === undefined || value === null) return undefined;
@@ -50,21 +64,7 @@ export function transformColorsJson(value: unknown): ProductColorJsonItemDto[] |
return out;
}
function mergeColorImageUrls(
imageUrls?: string[],
imageUrl?: string,
): string[] {
const merged: string[] = [];
const push = (u?: string) => {
const t = u?.trim();
if (t && !merged.includes(t)) merged.push(t);
};
for (const u of imageUrls ?? []) push(u);
push(imageUrl);
return merged;
}
/** ذخیره در Prisma — imageUrl + imageUrls برای هر رنگ (از body خام یا DTO) */
/** ذخیره در Prisma — imageUrl + imageUrls برای هر رنگ (از body خام، نه فقط DTO اعتبارسنجی‌شده) */
export function serializeColorsJsonForDb(
raw: unknown,
fallbackColor?: string,
@@ -73,8 +73,7 @@ export function serializeColorsJsonForDb(
if (!Array.isArray(raw)) return undefined;
const colorFallback = fallbackColor?.trim() || '';
const out: Array<{ name: string; imageUrl?: string; imageUrls?: string[] }> =
[];
const out: unknown[] = [];
for (let i = 0; i < raw.length; i++) {
const item = raw[i];
+7 -2
View File
@@ -9,8 +9,11 @@ import {
MinLength,
ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';
import { ProductColorJsonItemDto } from './product-color-json.dto';
import { Transform, Type } from 'class-transformer';
import {
ProductColorJsonItemDto,
transformColorsJson,
} from './product-color-json.dto';
export class CreateProductDto {
@IsString()
@@ -134,6 +137,7 @@ export class CreateProductDto {
isNew?: boolean;
@IsOptional()
@Transform(({ value }) => transformColorsJson(value))
@IsArray()
@ValidateNested({ each: true })
@Type(() => ProductColorJsonItemDto)
@@ -153,6 +157,7 @@ export class UpdateProductDto extends PartialType(
OmitType(CreateProductDto, ['companyId'] as const),
) {
@IsOptional()
@Transform(({ value }) => transformColorsJson(value))
@IsArray()
@ValidateNested({ each: true })
@Type(() => ProductColorJsonItemDto)