From d45a8faf1fdb474526ff5091251242b57bb09ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20Koskim=C3=A4ki?= Date: Sat, 15 Jun 2024 09:07:31 +0300 Subject: [PATCH] fixes #1036 --- src/util/object-utils.ts | 31 +++++++++++++++++----- test/node/src/camel-case.test.ts | 44 +++++++++++++++++++++++++++----- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/util/object-utils.ts b/src/util/object-utils.ts index 845f9ee87..4f7842cac 100644 --- a/src/util/object-utils.ts +++ b/src/util/object-utils.ts @@ -59,13 +59,20 @@ export function isArrayBufferOrView( } export function isPlainObject(obj: unknown): obj is Record { - return ( - isObject(obj) && - !Array.isArray(obj) && - !isDate(obj) && - !isBuffer(obj) && - !isArrayBufferOrView(obj) - ) + if (!isObject(obj) || getTag(obj) !== '[object Object]') { + return false + } + + if (Object.getPrototypeOf(obj) === null) { + return true + } + + let proto = obj + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto) + } + + return Object.getPrototypeOf(obj) === proto } export function getLast(arr: ArrayLike): T | undefined { @@ -169,3 +176,13 @@ function compareGenericObjects( return true } + +const toString = Object.prototype.toString + +function getTag(value: unknown): string { + if (value == null) { + return value === undefined ? '[object Undefined]' : '[object Null]' + } + + return toString.call(value) +} diff --git a/test/node/src/camel-case.test.ts b/test/node/src/camel-case.test.ts index 5ba4aeceb..923ce43fa 100644 --- a/test/node/src/camel-case.test.ts +++ b/test/node/src/camel-case.test.ts @@ -1,4 +1,11 @@ -import { CamelCasePlugin, Generated, Kysely, RawBuilder, sql } from '../../../' +import { + CamelCasePlugin, + Generated, + Kysely, + ParseJSONResultsPlugin, + RawBuilder, + sql, +} from '../../../' import { destroyTest, @@ -78,7 +85,7 @@ for (const dialect of DIALECTS) { // Can't run this test on SQLite because we can't access the same database // from the other Kysely instance. - if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql') { + if (dialect !== 'sqlite') { it('should have created the table and its columns in snake_case', async () => { const result = await sql`select * from camel_person`.execute( ctx.db, @@ -262,18 +269,41 @@ for (const dialect of DIALECTS) { }) }) - it('should respect maintainNestedObjectKeys', async () => { - const data = await camelDb - .withoutPlugins() - .withPlugin(new CamelCasePlugin({ maintainNestedObjectKeys: true })) + it('should map nested objects by default', async () => { + let db = camelDb.withoutPlugins() + + if (dialect === 'mssql' || dialect === 'sqlite') { + db = db.withPlugin(new ParseJSONResultsPlugin()) + } + + db = db.withPlugin(new CamelCasePlugin()) + + const data = await db .selectFrom('camelPerson') .selectAll() .executeTakeFirstOrThrow() + expect(data.preferences).to.eql({ + disableEmails: true, + }) + }) + + it('should respect maintainNestedObjectKeys', async () => { + let db = camelDb.withoutPlugins() + if (dialect === 'mssql' || dialect === 'sqlite') { - data.preferences = JSON.parse(data.preferences.toString()) + db = db.withPlugin(new ParseJSONResultsPlugin()) } + db = db.withPlugin( + new CamelCasePlugin({ maintainNestedObjectKeys: true }), + ) + + const data = await db + .selectFrom('camelPerson') + .selectAll() + .executeTakeFirstOrThrow() + expect(data.preferences).to.eql({ disable_emails: true, })