From cc5f99315f3defaa6b3ff1f61b218b43701b9161 Mon Sep 17 00:00:00 2001 From: Michael Hayes Date: Sun, 8 Sep 2024 22:21:21 -0700 Subject: [PATCH] [drizzle] Fix cursors using aliased columsn --- .changeset/chatty-crabs-mate.md | 5 +++ .../src/drizzle-field-builder.ts | 2 +- packages/plugin-drizzle/src/schema-builder.ts | 5 +-- packages/plugin-drizzle/src/utils/config.ts | 28 +++++++++++++++ packages/plugin-drizzle/src/utils/cursors.ts | 26 ++++++++------ .../plugin-drizzle/tests/example/db/schema.ts | 36 ++++++++----------- 6 files changed, 67 insertions(+), 35 deletions(-) create mode 100644 .changeset/chatty-crabs-mate.md diff --git a/.changeset/chatty-crabs-mate.md b/.changeset/chatty-crabs-mate.md new file mode 100644 index 000000000..2fca95228 --- /dev/null +++ b/.changeset/chatty-crabs-mate.md @@ -0,0 +1,5 @@ +--- +"@pothos/plugin-drizzle": patch +--- + +Fix cursors using aliased coluns diff --git a/packages/plugin-drizzle/src/drizzle-field-builder.ts b/packages/plugin-drizzle/src/drizzle-field-builder.ts index e7e1f94db..3dc414115 100644 --- a/packages/plugin-drizzle/src/drizzle-field-builder.ts +++ b/packages/plugin-drizzle/src/drizzle-field-builder.ts @@ -277,7 +277,7 @@ export class DrizzleObjectFieldBuilder< (parent as Record)[name], args, select.limit, - getCursorFormatter(cursorColumns), + getCursorFormatter(cursorColumns, schemaConfig), ); }, }, diff --git a/packages/plugin-drizzle/src/schema-builder.ts b/packages/plugin-drizzle/src/schema-builder.ts index de20f3eb7..6e8f685e2 100644 --- a/packages/plugin-drizzle/src/schema-builder.ts +++ b/packages/plugin-drizzle/src/schema-builder.ts @@ -97,11 +97,12 @@ schemaBuilderProto.drizzleNode = function drizzleNode( Column >, ) { - const tableConfig = getSchemaConfig(this).schema![table]; + const schemaConfig = getSchemaConfig(this); + const tableConfig = schemaConfig.schema![table]; const idColumn = typeof column === 'function' ? column(tableConfig.columns) : column; const idColumns = Array.isArray(idColumn) ? idColumn : [idColumn]; const interfaceRef = this.nodeInterfaceRef?.(); - const resolve = getIDSerializer(idColumns); + const resolve = getIDSerializer(idColumns, schemaConfig); const idParser = getIDParser(idColumns); const typeName = variant ?? name ?? table; const nodeRef = new DrizzleNodeRef(typeName, table, { diff --git a/packages/plugin-drizzle/src/utils/config.ts b/packages/plugin-drizzle/src/utils/config.ts index 5b741a2e0..7f08f0c50 100644 --- a/packages/plugin-drizzle/src/utils/config.ts +++ b/packages/plugin-drizzle/src/utils/config.ts @@ -1,15 +1,18 @@ import { type SchemaTypes, createContextCache } from '@pothos/core'; import { + type Column, type RelationalSchemaConfig, type TableRelationalConfig, type TablesRelationalConfig, createTableRelationsHelpers, extractTablesRelationalConfig, + getTableName, } from 'drizzle-orm'; import type { DrizzleClient } from '../types'; export interface PothosDrizzleSchemaConfig extends RelationalSchemaConfig { dbToSchema: Record; + columnToTsName: (column: Column) => string; } const configCache = createContextCache( (builder: PothosSchemaTypes.SchemaBuilder): PothosDrizzleSchemaConfig => { @@ -37,8 +40,33 @@ const configCache = createContextCache( {}, ); + const columnMappings = Object.values(dbToSchema).reduce>>( + (acc, table) => { + acc[table.dbName] = Object.entries(table.columns).reduce>( + (acc, [name, column]) => { + acc[column.name] = name; + return acc; + }, + {}, + ); + return acc; + }, + {}, + ); + return { dbToSchema, + columnToTsName: (column) => { + const tableName = getTableName(column.table); + const table = columnMappings[tableName]; + const columnName = table?.[column.name]; + + if (!columnName) { + throw new Error(`Could not find column mapping for ${tableName}.${column.name}`); + } + + return columnName; + }, ...config, }; }, diff --git a/packages/plugin-drizzle/src/utils/cursors.ts b/packages/plugin-drizzle/src/utils/cursors.ts index 56a3c67a7..2ec3f616f 100644 --- a/packages/plugin-drizzle/src/utils/cursors.ts +++ b/packages/plugin-drizzle/src/utils/cursors.ts @@ -39,8 +39,12 @@ export function formatCursorChunk(value: unknown) { } } -export function formatDrizzleCursor(record: Record, fields: Column[]) { - return getCursorFormatter(fields)(record); +export function formatDrizzleCursor( + record: Record, + fields: Column[], + config: PothosDrizzleSchemaConfig, +) { + return getCursorFormatter(fields, config)(record); } export function formatIDChunk(value: unknown) { @@ -58,40 +62,40 @@ export function formatIDChunk(value: unknown) { } } -export function getIDSerializer(fields: Column[]) { +export function getIDSerializer(fields: Column[], config: PothosDrizzleSchemaConfig) { if (fields.length === 0) { throw new PothosValidationError('Column serializer must have at least one field'); } return (value: Record) => { if (fields.length > 1) { - return `${JSON.stringify(fields.map((col) => value[col.name]))}`; + return `${JSON.stringify(fields.map((col) => value[config.columnToTsName(col)]))}`; } - return `${formatIDChunk(value[fields[0].name])}`; + return `${formatIDChunk(value[config.columnToTsName(fields[0])])}`; }; } -export function getColumnSerializer(fields: Column[]) { +export function getColumnSerializer(fields: Column[], config: PothosDrizzleSchemaConfig) { if (fields.length === 0) { throw new PothosValidationError('Column serializer must have at least one field'); } return (value: Record) => { if (fields.length > 1) { - return `J:${JSON.stringify(fields.map((col) => value[col.name]))}`; + return `J:${JSON.stringify(fields.map((col) => value[config.columnToTsName(col)]))}`; } - return `${formatCursorChunk(value[fields[0].name])}`; + return `${formatCursorChunk(value[config.columnToTsName(fields[0])])}`; }; } -export function getCursorFormatter(fields: Column[]) { +export function getCursorFormatter(fields: Column[], config: PothosDrizzleSchemaConfig) { if (fields.length === 0) { throw new PothosValidationError('Cursor must have at least one field'); } - const serializer = getColumnSerializer(fields); + const serializer = getColumnSerializer(fields, config); return (value: Record) => { return encodeBase64(`DC:${serializer(value)}`); @@ -489,7 +493,7 @@ export async function resolveDrizzleCursorConnection( : q.orderBy : table.primaryKey, }); - formatter = getCursorFormatter(cursorColumns); + formatter = getCursorFormatter(cursorColumns, config); const where = typeof q.where === 'function' ? q.where(table.columns, getOperators()) : q.where; diff --git a/packages/plugin-drizzle/tests/example/db/schema.ts b/packages/plugin-drizzle/tests/example/db/schema.ts index 5a8a33cf6..f9d3565c3 100644 --- a/packages/plugin-drizzle/tests/example/db/schema.ts +++ b/packages/plugin-drizzle/tests/example/db/schema.ts @@ -36,27 +36,21 @@ export const profile = sqliteTable('profile', { bio: text('bio'), }); -export const posts = sqliteTable( - 'posts', - { - id: integer('id'), - slug: text('slug').unique(), - title: text('title').notNull(), - content: text('content').notNull(), - published: integer('published').notNull().default(0), - authorId: integer('author_id') - .notNull() - .references(() => users.id, { - onDelete: 'cascade', - }), - categoryId: integer('category_id').references(() => categories.id), - createdAt: text('createdAt').notNull().default(sql`(current_timestamp)`), - updatedAt: text('createdAt').notNull().default(sql`(current_timestamp)`), - }, - (table) => ({ - pk: primaryKey({ columns: [table.id] }), - }), -); +export const posts = sqliteTable('posts', { + id: integer('id').primaryKey({ autoIncrement: true }), + slug: text('slug').unique(), + title: text('title').notNull(), + content: text('content').notNull(), + published: integer('published').notNull().default(0), + authorId: integer('author_id') + .notNull() + .references(() => users.id, { + onDelete: 'cascade', + }), + categoryId: integer('category_id').references(() => categories.id), + createdAt: text('createdAt').notNull().default(sql`(current_timestamp)`), + updatedAt: text('createdAt').notNull().default(sql`(current_timestamp)`), +}); export const postLikes = sqliteTable( 'post_likes',