From a933e98e98f366014e1a5af2c1444aaf330a09a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ad=C3=A1mek?= Date: Wed, 2 Nov 2022 19:56:29 +0100 Subject: [PATCH] feat(core): add context param to `Type.convertToDatabaseValue()` Closes #3567 --- packages/core/src/hydration/ObjectHydrator.ts | 4 ++-- packages/core/src/types/ArrayType.ts | 5 +++-- packages/core/src/types/BlobType.ts | 4 ++-- packages/core/src/types/EnumArrayType.ts | 5 +++-- packages/core/src/types/Type.ts | 8 +++++++- packages/core/src/unit-of-work/ChangeSetPersister.ts | 2 +- packages/core/src/utils/EntityComparator.ts | 8 ++++---- packages/core/src/utils/QueryHelper.ts | 2 +- packages/knex/src/query/QueryBuilderHelper.ts | 2 +- 9 files changed, 24 insertions(+), 16 deletions(-) diff --git a/packages/core/src/hydration/ObjectHydrator.ts b/packages/core/src/hydration/ObjectHydrator.ts index ffca7c289051..4e02d4e02072 100644 --- a/packages/core/src/hydration/ObjectHydrator.ts +++ b/packages/core/src/hydration/ObjectHydrator.ts @@ -85,7 +85,7 @@ export class ObjectHydrator extends Hydrator { ); } else if (prop.customType) { context.set(`convertToJSValue_${convertorKey}`, (val: any) => prop.customType.convertToJSValue(val, this.platform)); - context.set(`convertToDatabaseValue_${convertorKey}`, (val: any) => prop.customType.convertToDatabaseValue(val, this.platform)); + context.set(`convertToDatabaseValue_${convertorKey}`, (val: any) => prop.customType.convertToDatabaseValue(val, this.platform, { mode: 'hydration' })); ret.push( ` if (${preCond}typeof data${dataKey} !== 'undefined') {`, @@ -147,7 +147,7 @@ export class ObjectHydrator extends Hydrator { } if (prop.customType) { - context.set(`convertToDatabaseValue_${this.safeKey(prop.name)}`, (val: any) => prop.customType.convertToDatabaseValue(val, this.platform)); + context.set(`convertToDatabaseValue_${this.safeKey(prop.name)}`, (val: any) => prop.customType.convertToDatabaseValue(val, this.platform, { mode: 'hydration' })); ret.push(` if (data${dataKey} != null && convertCustomTypes) {`); ret.push(` data${dataKey} = convertToDatabaseValue_${this.safeKey(prop.name)}(entity${entityKey}.__helper.getPrimaryKey());`); // make sure the value is comparable diff --git a/packages/core/src/types/ArrayType.ts b/packages/core/src/types/ArrayType.ts index c614e6b41720..f547a22d8d11 100644 --- a/packages/core/src/types/ArrayType.ts +++ b/packages/core/src/types/ArrayType.ts @@ -1,3 +1,4 @@ +import type { TransformContext } from './Type'; import { Type } from './Type'; import { Utils } from '../utils'; import type { EntityProperty } from '../typings'; @@ -10,7 +11,7 @@ export class ArrayType extends Type extends Type { - convertToDatabaseValue(value: Buffer, platform: Platform): Buffer { + convertToDatabaseValue(value: Buffer): Buffer { return value; } - convertToJSValue(value: Buffer, platform: Platform): Buffer | null { + convertToJSValue(value: Buffer): Buffer | null { if (value as unknown instanceof Buffer || !value) { return value; } diff --git a/packages/core/src/types/EnumArrayType.ts b/packages/core/src/types/EnumArrayType.ts index 3ebe78a0d792..71b863b204ce 100644 --- a/packages/core/src/types/EnumArrayType.ts +++ b/packages/core/src/types/EnumArrayType.ts @@ -2,6 +2,7 @@ import { inspect } from 'util'; import { ArrayType } from './ArrayType'; import type { Platform } from '../platforms'; import { ValidationError } from '../errors'; +import type { TransformContext } from './Type'; function mapHydrator(items: T[] | undefined, hydrate: (i: string) => T): (i: string) => T { if (items && items.length > 0 && typeof items[0] === 'number') { @@ -19,7 +20,7 @@ export class EnumArrayType extends ArrayType super(mapHydrator(items, hydrate)); } - convertToDatabaseValue(value: T[] | null, platform: Platform, fromQuery?: boolean): string | null { + convertToDatabaseValue(value: T[] | null, platform: Platform, context: TransformContext | boolean): string | null { /* istanbul ignore else */ if (Array.isArray(value) && Array.isArray(this.items)) { const invalid = value.filter(v => !this.items!.includes(v)); @@ -29,7 +30,7 @@ export class EnumArrayType extends ArrayType } } - return super.convertToDatabaseValue(value, platform, fromQuery); + return super.convertToDatabaseValue(value, platform, context); } } diff --git a/packages/core/src/types/Type.ts b/packages/core/src/types/Type.ts index aede696d9b73..68157acfcd4e 100644 --- a/packages/core/src/types/Type.ts +++ b/packages/core/src/types/Type.ts @@ -1,6 +1,12 @@ import type { Platform } from '../platforms'; import type { Constructor, EntityMetadata, EntityProperty } from '../typings'; +export interface TransformContext { + fromQuery?: boolean; + key?: string; + mode?: 'hydration' | 'query' | 'discovery' | 'serialization'; +} + export abstract class Type { private static readonly types = new Map(); @@ -11,7 +17,7 @@ export abstract class Type { /** * Converts a value from its JS representation to its database representation of this type. */ - convertToDatabaseValue(value: JSType | DBType, platform: Platform, fromQuery?: boolean): DBType { + convertToDatabaseValue(value: JSType | DBType, platform: Platform, context?: TransformContext | boolean): DBType { return value as DBType; } diff --git a/packages/core/src/unit-of-work/ChangeSetPersister.ts b/packages/core/src/unit-of-work/ChangeSetPersister.ts index aafde01decbe..20adac9a4b2a 100644 --- a/packages/core/src/unit-of-work/ChangeSetPersister.ts +++ b/packages/core/src/unit-of-work/ChangeSetPersister.ts @@ -230,7 +230,7 @@ export class ChangeSetPersister { // some drivers might be returning bigint PKs as numbers when the number is small enough, // but we need to have it as string so comparison works in change set tracking, so instead // of using the raw value from db, we convert it back to the db value explicitly - value = prop.customType ? prop.customType.convertToDatabaseValue(insertId, this.platform) : value; + value = prop.customType ? prop.customType.convertToDatabaseValue(insertId, this.platform, { mode: 'serialization' }) : value; changeSet.payload[wrapped.__meta.primaryKeys[0]] = value; wrapped.__identifier!.setValue(value); } diff --git a/packages/core/src/utils/EntityComparator.ts b/packages/core/src/utils/EntityComparator.ts index 8c8bb85feed7..7c2ebcce6868 100644 --- a/packages/core/src/utils/EntityComparator.ts +++ b/packages/core/src/utils/EntityComparator.ts @@ -127,7 +127,7 @@ export class EntityComparator { } if (meta.properties[pk].customType) { - context.set(`convertToDatabaseValue_${pk}`, (val: any) => meta.properties[pk].customType.convertToDatabaseValue(val, this.platform)); + context.set(`convertToDatabaseValue_${pk}`, (val: any) => meta.properties[pk].customType.convertToDatabaseValue(val, this.platform, { mode: 'serialization' })); lines.push(` return convertToDatabaseValue_${pk}(entity${this.wrap(pk)});`); } else { lines.push(` return entity${this.wrap(pk)};`); @@ -398,7 +398,7 @@ export class EntityComparator { } if (shouldProcessCustomType(childProp)) { - context.set(`convertToDatabaseValue_${childProp.name}`, (val: any) => childProp.customType.convertToDatabaseValue(val, this.platform)); + context.set(`convertToDatabaseValue_${childProp.name}`, (val: any) => childProp.customType.convertToDatabaseValue(val, this.platform, { mode: 'serialization' })); if (['number', 'string', 'boolean'].includes(childProp.customType.compareAsType().toLowerCase())) { return `${padding} ret${childDataKey} = convertToDatabaseValue_${childProp.name}(entity${childEntityKey});`; @@ -441,7 +441,7 @@ export class EntityComparator { } if (prop.customType) { - context.set(`convertToDatabaseValue_${prop.name}`, (val: any) => prop.customType.convertToDatabaseValue(val, this.platform)); + context.set(`convertToDatabaseValue_${prop.name}`, (val: any) => prop.customType.convertToDatabaseValue(val, this.platform, { mode: 'serialization' })); if (['number', 'string', 'boolean'].includes(prop.customType.compareAsType().toLowerCase())) { return ret + ` ret${dataKey} = convertToDatabaseValue_${prop.name}(ret${dataKey});\n }\n`; @@ -454,7 +454,7 @@ export class EntityComparator { } if (prop.customType) { - context.set(`convertToDatabaseValue_${prop.name}`, (val: any) => prop.customType.convertToDatabaseValue(val, this.platform)); + context.set(`convertToDatabaseValue_${prop.name}`, (val: any) => prop.customType.convertToDatabaseValue(val, this.platform, { mode: 'serialization' })); if (['number', 'string', 'boolean'].includes(prop.customType.compareAsType().toLowerCase())) { return ret + ` ret${dataKey} = convertToDatabaseValue_${prop.name}(entity${entityKey});\n }\n`; diff --git a/packages/core/src/utils/QueryHelper.ts b/packages/core/src/utils/QueryHelper.ts index 75458919b0f0..3888173b330b 100644 --- a/packages/core/src/utils/QueryHelper.ts +++ b/packages/core/src/utils/QueryHelper.ts @@ -237,7 +237,7 @@ export class QueryHelper { return (cond as ObjectQuery[]).map(v => QueryHelper.processCustomType(prop, v, platform, key, fromQuery)) as unknown as ObjectQuery; } - return prop.customType.convertToDatabaseValue(cond, platform, fromQuery); + return prop.customType.convertToDatabaseValue(cond, platform, { fromQuery, key, mode: 'query' }); } private static processExpression(expr: string, value: T): Dictionary { diff --git a/packages/knex/src/query/QueryBuilderHelper.ts b/packages/knex/src/query/QueryBuilderHelper.ts index afe0dd9236ad..fd8221e88c57 100644 --- a/packages/knex/src/query/QueryBuilderHelper.ts +++ b/packages/knex/src/query/QueryBuilderHelper.ts @@ -764,7 +764,7 @@ export class QueryBuilderHelper { } if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) { - data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, true); + data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query' }); } if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[k])) {