Skip to content

Commit

Permalink
feat(core): add context param to Type.convertToDatabaseValue()
Browse files Browse the repository at this point in the history
Closes #3567
  • Loading branch information
B4nan committed Nov 2, 2022
1 parent 53ff984 commit a933e98
Show file tree
Hide file tree
Showing 9 changed files with 24 additions and 16 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/hydration/ObjectHydrator.ts
Expand Up @@ -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') {`,
Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions 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';
Expand All @@ -10,7 +11,7 @@ export class ArrayType<T extends string | number = string> extends Type<T[] | nu
super();
}

convertToDatabaseValue(value: T[] | null, platform: Platform, fromQuery?: boolean): string | null {
convertToDatabaseValue(value: T[] | null, platform: Platform, context?: TransformContext | boolean): string | null {
if (!value) {
return value as null;
}
Expand All @@ -19,7 +20,7 @@ export class ArrayType<T extends string | number = string> extends Type<T[] | nu
return platform.marshallArray(value as string[]);
}

if (fromQuery) {
if (typeof context === 'boolean' ? context : context?.fromQuery) {
return value;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/types/BlobType.ts
Expand Up @@ -4,11 +4,11 @@ import type { EntityProperty } from '../typings';

export class BlobType extends Type<Buffer | null> {

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;
}
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/types/EnumArrayType.ts
Expand Up @@ -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<T>(items: T[] | undefined, hydrate: (i: string) => T): (i: string) => T {
if (items && items.length > 0 && typeof items[0] === 'number') {
Expand All @@ -19,7 +20,7 @@ export class EnumArrayType<T extends string | number = string> 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));
Expand All @@ -29,7 +30,7 @@ export class EnumArrayType<T extends string | number = string> extends ArrayType
}
}

return super.convertToDatabaseValue(value, platform, fromQuery);
return super.convertToDatabaseValue(value, platform, context);
}

}
8 changes: 7 additions & 1 deletion 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<JSType = string, DBType = JSType> {

private static readonly types = new Map();
Expand All @@ -11,7 +17,7 @@ export abstract class Type<JSType = string, DBType = JSType> {
/**
* 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;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/unit-of-work/ChangeSetPersister.ts
Expand Up @@ -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);
}
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/utils/EntityComparator.ts
Expand Up @@ -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)};`);
Expand Down Expand Up @@ -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});`;
Expand Down Expand Up @@ -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`;
Expand All @@ -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`;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/utils/QueryHelper.ts
Expand Up @@ -237,7 +237,7 @@ export class QueryHelper {
return (cond as ObjectQuery<T>[]).map(v => QueryHelper.processCustomType(prop, v, platform, key, fromQuery)) as unknown as ObjectQuery<T>;
}

return prop.customType.convertToDatabaseValue(cond, platform, fromQuery);
return prop.customType.convertToDatabaseValue(cond, platform, { fromQuery, key, mode: 'query' });
}

private static processExpression<T>(expr: string, value: T): Dictionary<T> {
Expand Down
2 changes: 1 addition & 1 deletion packages/knex/src/query/QueryBuilderHelper.ts
Expand Up @@ -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])) {
Expand Down

0 comments on commit a933e98

Please sign in to comment.