From 2e81a089a1b57ebf61d25fc49300fa22f0cda06b Mon Sep 17 00:00:00 2001 From: Yiming Date: Wed, 6 Mar 2024 15:44:17 -0800 Subject: [PATCH] fix(polymorphism): support `orderBy` with base fields (#1086) --- packages/runtime/src/enhancements/delegate.ts | 34 +++++++++++------- tests/integration/tests/cli/plugins.test.ts | 1 + .../with-delegate/enhanced-client.test.ts | 35 +++++++++++++++++++ 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/packages/runtime/src/enhancements/delegate.ts b/packages/runtime/src/enhancements/delegate.ts index 7032a965a..06a96b0c6 100644 --- a/packages/runtime/src/enhancements/delegate.ts +++ b/packages/runtime/src/enhancements/delegate.ts @@ -77,6 +77,11 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler { this.injectWhereHierarchy(model, args?.where); this.injectSelectIncludeHierarchy(model, args); + if (args.orderBy) { + // `orderBy` may contain fields from base types + args.orderBy = this.buildWhereHierarchy(this.model, args.orderBy); + } + if (this.options.logPrismaQuery) { this.logger.info(`[delegate] \`${method}\` ${this.getModelName(model)}: ${formatObject(args)}`); } @@ -126,19 +131,19 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler { }); } - private buildWhereHierarchy(where: any) { + private buildWhereHierarchy(model: string, where: any) { if (!where) { return undefined; } where = deepcopy(where); Object.entries(where).forEach(([field, value]) => { - const fieldInfo = resolveField(this.options.modelMeta, this.model, field); + const fieldInfo = resolveField(this.options.modelMeta, model, field); if (!fieldInfo?.inheritedFrom) { return; } - let base = this.getBaseModel(this.model); + let base = this.getBaseModel(model); let target = where; while (base) { @@ -173,12 +178,17 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler { for (const kind of ['select', 'include'] as const) { if (args[kind] && typeof args[kind] === 'object') { - for (const [field, value] of Object.entries(args[kind])) { - if (value !== undefined) { + for (const [field, value] of Object.entries(args[kind])) { + const fieldInfo = resolveField(this.options.modelMeta, model, field); + if (fieldInfo && value !== undefined) { + if (value?.orderBy) { + // `orderBy` may contain fields from base types + value.orderBy = this.buildWhereHierarchy(fieldInfo.type, value.orderBy); + } + if (this.injectBaseFieldSelect(model, field, value, args, kind)) { delete args[kind][field]; } else { - const fieldInfo = resolveField(this.options.modelMeta, model, field); if (fieldInfo && this.isDelegateOrDescendantOfDelegate(fieldInfo.type)) { let nextValue = value; if (nextValue === true) { @@ -847,15 +857,15 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler { args = deepcopy(args); if (args.cursor) { - args.cursor = this.buildWhereHierarchy(args.cursor); + args.cursor = this.buildWhereHierarchy(this.model, args.cursor); } if (args.orderBy) { - args.orderBy = this.buildWhereHierarchy(args.orderBy); + args.orderBy = this.buildWhereHierarchy(this.model, args.orderBy); } if (args.where) { - args.where = this.buildWhereHierarchy(args.where); + args.where = this.buildWhereHierarchy(this.model, args.where); } if (this.options.logPrismaQuery) { @@ -875,11 +885,11 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler { args = deepcopy(args); if (args?.cursor) { - args.cursor = this.buildWhereHierarchy(args.cursor); + args.cursor = this.buildWhereHierarchy(this.model, args.cursor); } if (args?.where) { - args.where = this.buildWhereHierarchy(args.where); + args.where = this.buildWhereHierarchy(this.model, args.where); } if (this.options.logPrismaQuery) { @@ -915,7 +925,7 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler { args = deepcopy(args); if (args.where) { - args.where = this.buildWhereHierarchy(args.where); + args.where = this.buildWhereHierarchy(this.model, args.where); } if (this.options.logPrismaQuery) { diff --git a/tests/integration/tests/cli/plugins.test.ts b/tests/integration/tests/cli/plugins.test.ts index 36d6dd1a9..4d28c8e2a 100644 --- a/tests/integration/tests/cli/plugins.test.ts +++ b/tests/integration/tests/cli/plugins.test.ts @@ -116,6 +116,7 @@ describe('CLI Plugins Tests', () => { strict: true, lib: ['esnext', 'dom'], esModuleInterop: true, + skipLibCheck: true, }, }) ); diff --git a/tests/integration/tests/enhancements/with-delegate/enhanced-client.test.ts b/tests/integration/tests/enhancements/with-delegate/enhanced-client.test.ts index b4f273be5..5a171aa8b 100644 --- a/tests/integration/tests/enhancements/with-delegate/enhanced-client.test.ts +++ b/tests/integration/tests/enhancements/with-delegate/enhanced-client.test.ts @@ -235,6 +235,41 @@ describe('Polymorphism Test', () => { expect(imgAsset.owner).toMatchObject(user); }); + it('order by base fields', async () => { + const { db, user } = await setup(); + + await expect( + db.video.findMany({ + orderBy: { viewCount: 'desc' }, + }) + ).resolves.toHaveLength(1); + + await expect( + db.ratedVideo.findMany({ + orderBy: { duration: 'asc' }, + }) + ).resolves.toHaveLength(1); + + await expect( + db.user.findMany({ + orderBy: { assets: { _count: 'desc' } }, + }) + ).resolves.toHaveLength(1); + + await expect( + db.user.findUnique({ + where: { id: user.id }, + include: { + ratedVideos: { + orderBy: { + viewCount: 'desc', + }, + }, + }, + }) + ).toResolveTruthy(); + }); + it('update simple', async () => { const { db, videoWithOwner: video } = await setup();