Skip to content

Commit

Permalink
fix(orm): correctly resolve reference class schemas
Browse files Browse the repository at this point in the history
Also make sure persistence error has more information.
  • Loading branch information
marcj committed Jun 6, 2024
1 parent f478e96 commit e193325
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 7 deletions.
4 changes: 2 additions & 2 deletions packages/mongo/src/persistence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { InsertCommand } from './client/command/insert.js';
import { UpdateCommand } from './client/command/update.js';
import { DeleteCommand } from './client/command/delete.js';
import { FindAndModifyCommand } from './client/command/findAndModify.js';
import { empty } from '@deepkit/core';
import { empty, formatError } from '@deepkit/core';
import { FindCommand } from './client/command/find.js';
import { MongoConnection } from './client/connection.js';
import { getPartialSerializeFunction, ReflectionClass } from '@deepkit/type';
Expand Down Expand Up @@ -139,7 +139,7 @@ export class MongoPersistence extends DatabasePersistence {
error = new DatabaseInsertError(
classSchema,
items as OrmEntity[],
`Could not insert ${classSchema.getClassName()} into database`,
`Could not insert ${classSchema.getClassName()} into database: ${formatError(error)}`,
{ cause: error },
);
throw this.handleSpecificError(error);
Expand Down
5 changes: 5 additions & 0 deletions packages/orm/src/database-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,13 @@ export class DatabaseEntityRegistry {
throw new Error(`Only TypeClass|TypeObjectLiteral expected, but got kind ${type.kind}`);
}

// exact matches or nominal type match have priority
for (const entity of this.entities) {
if (entity.type === type) return entity;
if (entity.type.id === type.id) return entity;
}

for (const entity of this.entities) {
if (type.kind === ReflectionKind.class && entity.type.kind === ReflectionKind.class) {
if (type.classType === entity.type.classType) {
//if both don't use generic, return directly
Expand Down
22 changes: 21 additions & 1 deletion packages/orm/src/database-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ import { DatabaseValidationError, OrmEntity } from './type.js';
import { AbstractClassType, ClassType, CustomError, forwardTypeArguments } from '@deepkit/core';
import {
getPrimaryKeyExtractor,
getTypeJitContainer,
isReferenceInstance,
markAsHydrated,
PrimaryKeyFields,
ReceiveType,
ReflectionClass,
ReflectionKind,
stringifyType,
Type,
typeSettings,
UnpopulatedCheck,
validate,
Expand All @@ -41,6 +45,20 @@ import { Stopwatch } from '@deepkit/stopwatch';
import { EventDispatcher, EventDispatcherInterface, EventToken } from '@deepkit/event';
import { DatabasePluginRegistry } from './plugin/plugin.js';

function resolveReferenceToEntity(type: Type, entityRegistry: DatabaseEntityRegistry): ReflectionClass<any> {
if (type.kind === ReflectionKind.class) {
return getTypeJitContainer(type).resolveReferenceEntity ||= ReflectionClass.from(type.classType);
}

// object literals have no reference to the nominal type,
// so we look it up in the EntityRegistry
if (type.kind === ReflectionKind.objectLiteral) {
return getTypeJitContainer(type).resolveReferenceEntity ||= entityRegistry.get(type);
}

throw new Error(`Could not resolve reference to entity for ${stringifyType(type)}`);
}

let SESSION_IDS = 0;

export class DatabaseSessionRound<ADAPTER extends DatabaseAdapter> {
Expand Down Expand Up @@ -102,7 +120,9 @@ export class DatabaseSessionRound<ADAPTER extends DatabaseAdapter> {
if (reference.isBackReference()) continue;
const v = item[reference.getNameAsString() as keyof T] as any;
if (v == undefined) continue;
if (!isReferenceInstance(v)) result.push([reference.getResolvedReflectionClass(), v]);
if (!isReferenceInstance(v)) {
result.push([resolveReferenceToEntity(reference.type, this.session.entityRegistry), v]);
}
}

return result;
Expand Down
25 changes: 21 additions & 4 deletions packages/type/src/reflection/reflection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,11 @@ export function resolveReceiveType(type?: Packed | Type | ClassType | AbstractCl
if (!('__type' in type)) {
if ((type as any).__cached_type) return (type as any).__cached_type;
// disabled reflection for this class, so we return shallow TypeClass
return (type as any).__cached_type = { kind: ReflectionKind.class, classType: type as any, types: [] } as any;
return (type as any).__cached_type = {
kind: ReflectionKind.class,
classType: type as any,
types: [],
} as any;
}
return resolveRuntimeType(type) as Type;
}
Expand Down Expand Up @@ -262,7 +266,11 @@ export function visit(type: Type, visitor: (type: Type, path: string) => false |
case ReflectionKind.class:
case ReflectionKind.intersection:
case ReflectionKind.templateLiteral:
for (const member of type.types) stack.push({ type: member, depth: entry.depth + 1, path: extendPath(entry.path, member) });
for (const member of type.types) stack.push({
type: member,
depth: entry.depth + 1,
path: extendPath(entry.path, member),
});
break;
case ReflectionKind.string:
case ReflectionKind.number:
Expand All @@ -276,7 +284,11 @@ export function visit(type: Type, visitor: (type: Type, path: string) => false |
case ReflectionKind.method:
case ReflectionKind.methodSignature:
stack.push({ type: type.return, depth: entry.depth + 1, path: entry.path });
for (const member of type.parameters) stack.push({ type: member, depth: entry.depth + 1, path: extendPath(entry.path, member) });
for (const member of type.parameters) stack.push({
type: member,
depth: entry.depth + 1,
path: extendPath(entry.path, member),
});
break;
case ReflectionKind.propertySignature:
case ReflectionKind.property:
Expand Down Expand Up @@ -425,7 +437,12 @@ export class ReflectionFunction {

if (!('__type' in fn)) {
//functions without any types have no __type attached
return new ReflectionFunction({ kind: ReflectionKind.function, function: fn, return: { kind: ReflectionKind.any }, parameters: [] });
return new ReflectionFunction({
kind: ReflectionKind.function,
function: fn,
return: { kind: ReflectionKind.any },
parameters: [],
});
}

const type = reflect(fn);
Expand Down

0 comments on commit e193325

Please sign in to comment.