Repository: typedorm/typedorm
Pull request #214 (2022-03-12 10:46:35)

fix 203
- fix: fixes: #203 check instanceof entity more robustly
- chore: add unit test for common-types



Patch:
diff --git a/packages/common/src/helpers/common-types.ts b/packages/common/src/helpers/common-types.ts
index cb0db1c..4875ba1 100644
--- a/packages/common/src/helpers/common-types.ts
+++ b/packages/common/src/helpers/common-types.ts
@@ -2,9 +2,9 @@ import {ObjectType} from './object-type';
 
 export type EntityTarget<Entity> = ObjectType<Entity>;
 
-export type EntityInstance = {
+export interface EntityInstance extends Object {
   constructor: Function;
-};
+}
 
 export type EntityAttributes<Entity> = {
   [key in keyof Entity]: Entity[key];
@@ -13,4 +13,5 @@ export type EntityAttributes<Entity> = {
 export const IsEntityInstance = (
   variable: unknown
 ): variable is EntityInstance =>
-  (variable as EntityInstance).constructor instanceof Function;
+  (variable as EntityInstance).constructor instanceof Function &&
+  (variable as EntityInstance).constructor.name !== 'Object';
diff --git a/packages/core/src/classes/manager/entity-manager.ts b/packages/core/src/classes/manager/entity-manager.ts
index e74f417..68bdefc 100644
--- a/packages/core/src/classes/manager/entity-manager.ts
+++ b/packages/core/src/classes/manager/entity-manager.ts
@@ -1,7 +1,9 @@
 import {DynamoDB} from 'aws-sdk';
 import {
   EntityAttributes,
+  EntityInstance,
   EntityTarget,
+  IsEntityInstance,
   MANAGER_NAME,
   QUERY_ORDER,
   STATS_TYPE,
@@ -139,10 +141,17 @@ export class EntityManager {
    * @param entity Entity to add to table as a new record
    */
   async create<Entity>(
-    entity: Entity,
+    entity: EntityInstance,
     options?: EntityManagerCreateOptions<Entity>,
     metadataOptions?: MetadataOptions
   ): Promise<Entity> {
+    if (!IsEntityInstance(entity)) {
+      throw new Error(
+        `Provided entity ${JSON.stringify(
+          entity
+        )} must be an instance of an entity class`
+      );
+    }
     const requestId = getUniqueRequestId(metadataOptions?.requestId);
 
     const dynamoPutItemInput = this._dcReqTransformer.toDynamoPutItem(
diff --git a/packages/core/src/helpers/get-constructor-for-instance.ts b/packages/core/src/helpers/get-constructor-for-instance.ts
index 46df1f2..4d7207a 100644
--- a/packages/core/src/helpers/get-constructor-for-instance.ts
+++ b/packages/core/src/helpers/get-constructor-for-instance.ts
@@ -1,11 +1,8 @@
-import {EntityInstance, EntityTarget, IsEntityInstance} from '@typedorm/common';
+import {EntityTarget, IsEntityInstance} from '@typedorm/common';
 
-export function getConstructorForInstance<Entity>(entity: Entity) {
-  let entityClass: EntityTarget<Entity>;
-  if (IsEntityInstance(entity)) {
-    entityClass = (entity as EntityInstance).constructor;
-  } else {
+export function getConstructorForInstance<Entity>(entity: unknown) {
+  if (!IsEntityInstance(entity)) {
     throw new Error('Cannot transform Class to dynamo object');
   }
-  return entityClass;
+  return entity.constructor as EntityTarget<Entity>;
 }


Test patch:
diff --git a/packages/common/src/helpers/__test__/common-types.spec.ts b/packages/common/src/helpers/__test__/common-types.spec.ts
new file mode 100644
index 0000000..93ece23
--- /dev/null
+++ b/packages/common/src/helpers/__test__/common-types.spec.ts
@@ -0,0 +1,18 @@
+import {IsEntityInstance} from '../common-types';
+
+describe('IsEntityInstance', () => {
+  test('should return true when input is an instance of a class', () => {
+    class Foo {}
+    expect(IsEntityInstance(Foo)).toBe(true);
+  });
+
+  test('should return false when input is an object not constructed via a class', () => {
+    expect(IsEntityInstance({name: 'foo'})).toBe(false);
+  });
+
+  test('should return true when input is an instance of a class named "Object" (dont do this!)', () => {
+    //@ts-expect-error just in case anyone should be foolish enough to name a class "Object"...
+    class Object {}
+    expect(IsEntityInstance(Object)).toBe(true);
+  });
+});
diff --git a/packages/core/src/classes/manager/__test__/entity-manager.spec.ts b/packages/core/src/classes/manager/__test__/entity-manager.spec.ts
index d3829fe..045d998 100644
--- a/packages/core/src/classes/manager/__test__/entity-manager.spec.ts
+++ b/packages/core/src/classes/manager/__test__/entity-manager.spec.ts
@@ -14,6 +14,7 @@ import {
 import {Connection} from '../../connection/connection';
 import {
   CONSUMED_CAPACITY_TYPE,
+  EntityInstance,
   InvalidDynamicUpdateAttributeValueError,
 } from '@typedorm/common';
 import {
@@ -212,6 +213,34 @@ test('creates entity and returns all attributes, including auto generated ones',
   });
 });
 
+/**
+ * Issue: #203
+ */
+test('will throw when called with a POJO rather than an instance of a entity class', async () => {
+  dcMock.put.mockReturnValue({
+    promise: () => ({}),
+  });
+
+  const user = new User();
+  user.id = '1';
+  user.name = 'Test User';
+  user.status = 'active';
+
+  await manager.create<User>(user); // works
+
+  const userProperties = {
+    id: '2',
+    age: 2,
+    name: 'two',
+    status: 'broken',
+    addresses: ['address a'],
+  };
+
+  await expect(
+    manager.create<User>(userProperties as EntityInstance)
+  ).rejects.toBeInstanceOf(Error);
+});
+
 /**
  * @group findOne
  */