From c9fb944425a3e2ec4c93f89f914cbfd31f4fc0d5 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Fri, 7 Jun 2024 19:07:15 +0800 Subject: [PATCH] fix(delegate): avoid merging into object of Decimal, Date, etc. --- packages/runtime/package.json | 1 + packages/runtime/src/enhancements/delegate.ts | 2 + pnpm-lock.yaml | 3 + tests/regression/tests/issue-1487.test.ts | 71 +++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 tests/regression/tests/issue-1487.test.ts diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 65996c8e9..9a6bfb923 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -83,6 +83,7 @@ "decimal.js": "^10.4.2", "deepcopy": "^2.1.0", "deepmerge": "^4.3.1", + "is-plain-object": "^5.0.0", "logic-solver": "^2.0.1", "lower-case-first": "^2.0.2", "pluralize": "^8.0.0", diff --git a/packages/runtime/src/enhancements/delegate.ts b/packages/runtime/src/enhancements/delegate.ts index 8e4c3569a..31ddd2730 100644 --- a/packages/runtime/src/enhancements/delegate.ts +++ b/packages/runtime/src/enhancements/delegate.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import deepcopy from 'deepcopy'; import deepmerge, { type ArrayMergeOptions } from 'deepmerge'; +import { isPlainObject } from 'is-plain-object'; import { lowerCaseFirst } from 'lower-case-first'; import { DELEGATE_AUX_RELATION_PREFIX } from '../constants'; import { @@ -1094,6 +1095,7 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler { const result = deepmerge(upMerged, downMerged, { arrayMerge: combineMerge, + isMergeableObject: isPlainObject, // avoid messing with Decimal, Date, etc. }); return result; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 763ac5df4..dfeda8f4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -418,6 +418,9 @@ importers: deepmerge: specifier: ^4.3.1 version: 4.3.1 + is-plain-object: + specifier: ^5.0.0 + version: 5.0.0 logic-solver: specifier: ^2.0.1 version: 2.0.1 diff --git a/tests/regression/tests/issue-1487.test.ts b/tests/regression/tests/issue-1487.test.ts new file mode 100644 index 000000000..6acfcdcfe --- /dev/null +++ b/tests/regression/tests/issue-1487.test.ts @@ -0,0 +1,71 @@ +import { createPostgresDb, dropPostgresDb, loadSchema } from '@zenstackhq/testtools'; +import Decimal from 'decimal.js'; + +describe('issue 1487', () => { + it('regression2', async () => { + const dbUrl = await createPostgresDb('issue-1487'); + let prisma: any; + + try { + const r = await loadSchema( + ` + model LineItem { + id Int @id @default(autoincrement()) + price Decimal + createdAt DateTime @default(now()) + + orderId Int + order Order @relation(fields: [orderId], references: [id]) + } + model Order extends BaseType { + total Decimal + createdAt DateTime @default(now()) + lineItems LineItem[] + } + model BaseType { + id Int @id @default(autoincrement()) + entityType String + + @@delegate(entityType) + } + `, + { + provider: 'postgresql', + dbUrl, + enhancements: ['omit', 'delegate'], + } + ); + + prisma = r.prisma; + const db = r.enhance(); + + const create = await db.Order.create({ + data: { + total: new Decimal(100_100.99), + lineItems: { create: [{ price: 90_000.66 }, { price: 20_100.33 }] }, + }, + }); + + const order = await db.Order.findFirst({ where: { id: create.id }, include: { lineItems: true } }); + expect(Decimal.isDecimal(order.total)).toBe(true); + expect(order.createdAt instanceof Date).toBe(true); + expect(order.total.toString()).toEqual('100100.99'); + order.lineItems.forEach((item: any) => { + expect(Decimal.isDecimal(item.price)).toBe(true); + expect(item.price.toString()).not.toEqual('[object Object]'); + }); + + const lineItems = await db.LineItem.findMany(); + lineItems.forEach((item: any) => { + expect(item.createdAt instanceof Date).toBe(true); + expect(Decimal.isDecimal(item.price)).toBe(true); + expect(item.price.toString()).not.toEqual('[object Object]'); + }); + } finally { + if (prisma) { + await prisma.$disconnect(); + } + await dropPostgresDb('issue-1487'); + } + }); +});