From ebe9b00a9fdb09fdccfe4f2177b96cdb8d1419cc Mon Sep 17 00:00:00 2001 From: w35l3y Date: Sun, 1 Jun 2025 17:25:20 -0300 Subject: [PATCH] Querying json data now works in mysql Added prisma extension to convert "path" from array format (postgresql) to string format (mysql) --- .../prismaExtensionPgpathToMysql.js | 61 +++++++++++++++++++ src/api/server.module.ts | 12 +++- src/utils/extendsWithProxy.ts | 27 ++++++++ tsconfig.json | 1 + 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/api/extensions/prismaExtensionPgpathToMysql.js create mode 100644 src/utils/extendsWithProxy.ts diff --git a/src/api/extensions/prismaExtensionPgpathToMysql.js b/src/api/extensions/prismaExtensionPgpathToMysql.js new file mode 100644 index 00000000..dc76bcf0 --- /dev/null +++ b/src/api/extensions/prismaExtensionPgpathToMysql.js @@ -0,0 +1,61 @@ +import { Prisma } from '@prisma/client' +import { Logger } from '@config/logger.config'; + +const logger = new Logger('PGPATH2MYSQL'); + +function convertPgPathToMysql (path) { + if (!Array.isArray(path)) return path + let result = '$' + for (const item of path) { + if (/^\d+$/.test(item)) { + result += `[${item}]` + } else { + result += `.${item}` + } + } + return result +} + +function processWhere (obj) { + if (obj && typeof obj === 'object') { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + if (key === 'path') { + obj[key] = convertPgPathToMysql(obj[key]); + } else { + processWhere(obj[key]); + } + } + } + } +} + +// https://www.prisma.io/docs/orm/prisma-client/client-extensions/query#modify-all-operations-in-all-models-of-your-schema +// https://www.prisma.io/docs/orm/prisma-client/client-extensions/query#modify-a-specific-operation-in-a-specific-model + +const overriddenOperation = async ({ model, operation, args, query }) => { + if (args?.where) { + processWhere(args.where) + } + const result = await query(args) + logger.debug({ model, operation, args: JSON.stringify(args), result }) + return result +} + +export default Prisma.defineExtension({ + name: 'prisma-extension-pgpath-to-mysql', + query: { + $allModels: { + findFirst: overriddenOperation, + findMany: overriddenOperation, + updateMany: overriddenOperation, + count: overriddenOperation, + deleteMany: overriddenOperation, + + delete: overriddenOperation, + findUnique: overriddenOperation, + update: overriddenOperation, + upsert: overriddenOperation, + } + } +}) diff --git a/src/api/server.module.ts b/src/api/server.module.ts index 49fc5695..e29ddaf1 100644 --- a/src/api/server.module.ts +++ b/src/api/server.module.ts @@ -1,5 +1,5 @@ import { CacheEngine } from '@cache/cacheengine'; -import { Chatwoot, configService, ProviderSession } from '@config/env.config'; +import { Chatwoot, configService, ProviderSession, Database } from '@config/env.config'; import { eventEmitter } from '@config/event.config'; import { Logger } from '@config/logger.config'; @@ -40,6 +40,9 @@ import { ProxyService } from './services/proxy.service'; import { SettingsService } from './services/settings.service'; import { TemplateService } from './services/template.service'; +import pgPathToMysql from './extensions/prismaExtensionPgpathToMysql'; +import { extendsWithProxy } from '@utils/extendsWithProxy'; + const logger = new Logger('WA MODULE'); let chatwootCache: CacheService = null; @@ -55,7 +58,12 @@ if (configService.get('PROVIDER').ENABLED) { providerFiles = new ProviderFiles(configService); } -export const prismaRepository = new PrismaRepository(configService); +const provider = configService.get('DATABASE').PROVIDER; +let extendablePrismaRepository: PrismaRepository = new PrismaRepository(configService) +if (provider === "mysql") { + extendablePrismaRepository = extendsWithProxy(extendablePrismaRepository, pgPathToMysql); +} +export const prismaRepository = extendablePrismaRepository; export const waMonitor = new WAMonitoringService( eventEmitter, diff --git a/src/utils/extendsWithProxy.ts b/src/utils/extendsWithProxy.ts new file mode 100644 index 00000000..f21e6c46 --- /dev/null +++ b/src/utils/extendsWithProxy.ts @@ -0,0 +1,27 @@ +import { PrismaClient } from '@prisma/client'; + +type ExtensionArgs = Parameters[0]; + +export function extendsWithProxy( + instanciaBase: T, + extensao: ExtensionArgs +): T { + const instanciaEstendida = instanciaBase.$extends(extensao); + + const proxy = new Proxy(instanciaBase as unknown as object, { + get(target, prop, receiver) { + if (prop === 'toString') { + return () => '[Proxy toString]'; + } + if (prop === Symbol.toStringTag) { + return undefined; + } + return prop in instanciaEstendida ? Reflect.get(instanciaEstendida as any, prop, receiver) : Reflect.get(target, prop, receiver); + }, + has(target, prop) { + return prop in target || prop in (instanciaEstendida as any); + }, + }); + + return proxy as unknown as T; +} diff --git a/tsconfig.json b/tsconfig.json index af814134..28a3630a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,7 @@ "strictNullChecks": false, "incremental": true, "noImplicitAny": false, + "allowJs": true, "baseUrl": ".", "paths": { "@api/*": ["./src/api/*"],