Repository: typedorm/typedorm (ts)

Issue:
Using attribute of type enums for a schema causes schema validation to fail
Using attribute of type enums for a schema causes schema validation to fail.

I.e When an attribute is defined with type other than scalar types, (Enum is non scalar) this causes schema validation to  fail.

Pull request description:
fix(core): add option to use enum on property in key schema
- allow using property of type enum when referencing it in key schema

closes #29 

Patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 3078d02..56f832e 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,7 +1,6 @@
 {
   "cSpell.words": [
     "KSUID",
-    "Metadatas",
     "bazel",
     "commitlint",
     "dynamodb",
diff --git a/README.md b/README.md
index a9f6def..283a32d 100644
--- a/README.md
+++ b/README.md
@@ -217,8 +217,6 @@ To find out more about how the data looks like when it is stored in dynamo have
 
 ### [Step by step guide](./docs/guide.md)
 
-### [Index](./docs/index.md)
-
 ## Current Limitations
 
 - TypDORM currently is not able to connect with dynamodb table using aws credentials and only works within environment like lambda/ec2 that already has access to dynamo table, but this will be resolved very soon.
diff --git a/docs/index.md b/docs/index.md
deleted file mode 100644
index 4967587..0000000
--- a/docs/index.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Index
-
-Document index that covers, list of pages available
-
-- [How it works](./how-it-works.md)
-- [Step by step guide](./guide.md)
-- [Entity inheritance](./entity-inheritance.md)
-- [Working with multiple connections](./multiple-connection.md)
diff --git a/docs/multiple-connection.md b/docs/multiple-connection.md
deleted file mode 100644
index 6523a38..0000000
--- a/docs/multiple-connection.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Working with multiple connections
-
-Let's assume that we have a two different tables running two different workloads, one is for `app1` workload and other is for `app2` workload. In order to be able to interact with these two tables, we have two user with different permissions, `app1User` and `app2User`.
-
-Now, we have a service that needs to be able to work with both tables in that are registered in separate connections. Working with different connections using different credentials, will look something like this.
-
-First we create two separate connections both using different credentials
-
-```Typescript
-// connection for app1
-createConnection({
-  name: 'app1'
-  table: app1Table,
-  entities: [...entities],
-  documentClient: {
-    credentials: {
-      accessKeyId: process.env.APP_1_ACCESS_KEY_ID,
-      secretAccessKey: process.env.APP_1_SECRET_ACCESS_KEY
-    }
-  }
-});
-
-// connection for app2
-createConnection({
-  name: 'app2'
-  table: app1Table,
-  entities: [...entities],
-  documentClient: {
-    credentials: {
-      accessKeyId: process.env.APP_2_ACCESS_KEY_ID,
-      secretAccessKey: process.env.APP_2_SECRET_ACCESS_KEY
-    }
-  }
-});
-```
-
-Now we can use multiple connections to different actions, that are using completely different credentials,
-
-```Typescript
-// update record in both table in different connections
-
-// create user in connection 1
-getEntityManager('app1').create(User, {...})
-
-// update record in connection2
-getEntityManager('app2').create(Record, {...})
-```
-
-TypeDORM makes it super easy to configure multiple connections with different connection options.
diff --git a/packages/common/decorators/attribute.decorator.ts b/packages/common/decorators/attribute.decorator.ts
index 047b054..c11f137 100644
--- a/packages/common/decorators/attribute.decorator.ts
+++ b/packages/common/decorators/attribute.decorator.ts
@@ -4,19 +4,30 @@ import {AttributeRawMetadataOptions} from '../metadata-storage';
 
 export interface AttributeOptions {
   /**
-   * item will be managed using transaction to ensure it's consistency
+   * Item will be managed using transaction to ensure it's consistency
    * @default false
    */
-  unique: boolean;
+  unique?: boolean;
+  /**
+   * Mark property as enum
+   * @required when property of type enum is referenced in key
+   * @default false
+   */
+  isEnum?: boolean;
 }
 
 export function Attribute(options?: AttributeOptions): PropertyDecorator {
   return (target, propertyKey): void => {
-    const type = Reflect.getMetadata('design:type', target, propertyKey);
+    let type = Reflect.getMetadata('design:type', target, propertyKey).name;
+
+    if (options?.isEnum) {
+      // default to "String" when attribute is marked as enum
+      type = 'String';
+    }
 
     const attributeProps = {
       name: propertyKey.toString(),
-      type: type.name,
+      type,
       unique: options?.unique,
     } as AttributeRawMetadataOptions;
 
diff --git a/packages/common/enums.ts b/packages/common/enums.ts
index 113d863..7f310bf 100644
--- a/packages/common/enums.ts
+++ b/packages/common/enums.ts
@@ -27,7 +27,3 @@ export enum AUTO_GENERATE_ATTRIBUTE_STRATEGY {
   EPOCH_DATE = 'EPOCH_DATE',
   ISO_DATE = 'ISO_DATE',
 }
-
-export enum INTERNAL_ENTITY_ATTRIBUTE {
-  ENTITY_NAME = '__en',
-}
diff --git a/packages/common/index.ts b/packages/common/index.ts
index d97d051..818f074 100644
--- a/packages/common/index.ts
+++ b/packages/common/index.ts
@@ -16,7 +16,6 @@ export * from './require-at-least-one-type';
 export * from './require-only-one-type';
 export * from './scalar-type';
 export * from './update-attributes-type';
-export * from './replace-type';
 
 // classes
 export * from './table';
diff --git a/packages/common/replace-type.ts b/packages/common/replace-type.ts
deleted file mode 100644
index f784556..0000000
--- a/packages/common/replace-type.ts
+++ /dev/null
@@ -1,1 +0,0 @@
-export type Replace<T, K extends keyof T, R> = Omit<T, K> & R;
diff --git a/packages/core/classes/connection/connection-options.ts b/packages/core/classes/connection/connection-options.ts
index f4c326c..889a956 100644
--- a/packages/core/classes/connection/connection-options.ts
+++ b/packages/core/classes/connection/connection-options.ts
@@ -1,29 +1,8 @@
 import {EntityTarget, Table} from '@typedorm/common';
-import {DocumentClient} from 'aws-sdk/clients/dynamodb';
 
-/**
- * @param table - Global table to use for all entities, entity scoped table takes precedence over this
- * @param name - Connection name
- * @param dynamoQueryItemsImplicitLimit - Implicit limit to apply when querying items, this will be overridden by explicit limit
- * @param entities - List of entity classes or global pattern string matching all entities
- * @param documentClient - Document client to use for this connection
- */
 export interface ConnectionOptions {
-  /**
-   * @default - none
-   */
   table?: Table;
-  /**
-   * @default - 'default'
-   */
   name?: string;
-  /**
-   * @default 3000
-   */
   dynamoQueryItemsImplicitLimit?: number;
   entities: EntityTarget<any>[] | string;
-  /**
-   * @default - new document client instance with default config will be instantiated
-   */
-  documentClient?: DocumentClient;
 }
diff --git a/packages/core/classes/connection/connection.ts b/packages/core/classes/connection/connection.ts
index eaa1e88..573ea55 100644
--- a/packages/core/classes/connection/connection.ts
+++ b/packages/core/classes/connection/connection.ts
@@ -3,7 +3,6 @@ import {
   EntityTarget,
   Table,
 } from '@typedorm/common';
-import {DocumentClient} from 'aws-sdk/clients/dynamodb';
 import {EntityManager} from '../manager/entity-manager';
 import {TransactionManager} from '../manager/transaction-manager';
 import {AttributeMetadata} from '../metadata/attribute-metadata';
@@ -21,7 +20,6 @@ export class Connection {
   readonly entityManager: EntityManager;
   readonly transactionManger: TransactionManager;
   readonly defaultConfig: {queryItemsImplicitLimit: number};
-  readonly documentClient: DocumentClient;
 
   private _entityMetadatas: Map<string, EntityMetadata>;
   private isConnected: boolean;
@@ -33,18 +31,12 @@ export class Connection {
     }
     this.name = name;
     this.entityManager = new EntityManager(this);
-    this.transactionManger = new TransactionManager(this);
+    this.transactionManger = new TransactionManager();
     this.defaultConfig = {
       queryItemsImplicitLimit:
         options.dynamoQueryItemsImplicitLimit ??
         DYNAMO_QUERY_ITEMS_IMPLICIT_LIMIT,
     };
-
-    if (options.documentClient) {
-      this.documentClient = options.documentClient;
-    } else {
-      this.documentClient = new DocumentClient();
-    }
     /**
      * This makes sure that we only ever build entity metadatas once per connection
      */
diff --git a/packages/core/classes/manager/base-manager.ts b/packages/core/classes/manager/base-manager.ts
new file mode 100644
index 0000000..3e9d276
--- /dev/null
+++ b/packages/core/classes/manager/base-manager.ts
@@ -0,0 +1,8 @@
+import {DynamoDB} from 'aws-sdk';
+
+export abstract class BaseManager {
+  protected _dc: DynamoDB.DocumentClient;
+  constructor() {
+    this._dc = new DynamoDB.DocumentClient();
+  }
+}
diff --git a/packages/core/classes/manager/entity-manager.ts b/packages/core/classes/manager/entity-manager.ts
index 2f57740..7c8a60b 100644
--- a/packages/core/classes/manager/entity-manager.ts
+++ b/packages/core/classes/manager/entity-manager.ts
@@ -16,6 +16,7 @@ import {
   ManagerToDynamoQueryItemsOptions,
 } from '../transformer/document-client-request-transformer';
 import {EntityTransformer} from '../transformer/entity-transformer';
+import {BaseManager} from './base-manager';
 import {getConstructorForInstance} from '../../helpers/get-constructor-for-instance';
 
 export interface EntityManagerUpdateOptions {
@@ -35,11 +36,12 @@ export interface EntityManagerQueryOptions
   cursor?: DocumentClient.Key;
 }
 
-export class EntityManager {
+export class EntityManager extends BaseManager {
   private _dcReqTransformer: DocumentClientRequestTransformer;
   private _entityTransformer: EntityTransformer;
 
   constructor(protected connection: Connection) {
+    super();
     this._dcReqTransformer = new DocumentClientRequestTransformer(connection);
     this._entityTransformer = new EntityTransformer(connection);
   }
@@ -53,7 +55,7 @@ export class EntityManager {
     const entityClass = getConstructorForInstance(entity);
 
     if (!Array.isArray(dynamoPutItemInput)) {
-      await this.connection.documentClient.put(dynamoPutItemInput).promise();
+      await this._dc.put(dynamoPutItemInput).promise();
       const itemToReturn = this._entityTransformer.fromDynamoEntity<Entity>(
         entityClass,
         dynamoPutItemInput.Item
@@ -94,9 +96,7 @@ export class EntityManager {
       primaryKeyAttributes
     );
 
-    const response = await this.connection.documentClient
-      .get(dynamoGetItem)
-      .promise();
+    const response = await this._dc.get(dynamoGetItem).promise();
 
     const entity = this._entityTransformer.fromDynamoEntity<Entity>(
       entityClass,
@@ -178,7 +178,7 @@ export class EntityManager {
         attrValue
       );
       return !!(
-        await this.connection.documentClient
+        await this._dc
           .get({
             Key: {...uniqueAttributePrimaryKey},
             TableName: metadata.table.name,
@@ -208,9 +208,7 @@ export class EntityManager {
       Entity
     >(entityClass, primaryKey, body, options);
 
-    const response = await this.connection.documentClient
-      .update(dynamoUpdateItem)
-      .promise();
+    const response = await this._dc.update(dynamoUpdateItem).promise();
 
     return this._entityTransformer.fromDynamoEntity<Entity>(
       entityClass,
@@ -232,7 +230,7 @@ export class EntityManager {
       Entity
     >(entityClass, primaryKey);
 
-    await this.connection.documentClient.delete(dynamoDeleteItem).promise();
+    await this._dc.delete(dynamoDeleteItem).promise();
 
     return {
       success: true,
@@ -291,10 +289,7 @@ export class EntityManager {
     cursor?: DocumentClient.Key;
     itemsFetched?: DocumentClient.ItemList;
   }): Promise<{items: DocumentClient.ItemList; cursor?: DocumentClient.Key}> {
-    const {
-      LastEvaluatedKey,
-      Items = [],
-    } = await this.connection.documentClient
+    const {LastEvaluatedKey, Items = []} = await this._dc
       .query({...queryInput, ExclusiveStartKey: cursor})
       .promise();
     itemsFetched = [...itemsFetched, ...Items];
diff --git a/packages/core/classes/manager/transaction-manager.ts b/packages/core/classes/manager/transaction-manager.ts
index 2355a07..59df1f8 100644
--- a/packages/core/classes/manager/transaction-manager.ts
+++ b/packages/core/classes/manager/transaction-manager.ts
@@ -1,13 +1,15 @@
 import {DocumentClient} from 'aws-sdk/clients/dynamodb';
 import {TRANSACTION_WRITE_ITEMS_LIMIT} from '@typedorm/common';
 import {WriteTransaction} from '../transaction/write-transaction';
-import {Connection} from '../connection/connection';
+import {BaseManager} from './base-manager';
 
 /**
  * Performs transactions using document client's writeTransaction
  */
-export class TransactionManager {
-  constructor(private connection: Connection) {}
+export class TransactionManager extends BaseManager {
+  constructor() {
+    super();
+  }
 
   async write(transaction: WriteTransaction) {
     if (transaction.items.length > TRANSACTION_WRITE_ITEMS_LIMIT) {
@@ -23,9 +25,7 @@ export class TransactionManager {
     // FIXME: current promise implementation of document client transact write does not provide a way
     // the transaction was failed, as a work around we do following.
     // refer to this issue for more details https://github.com/aws/aws-sdk-js/issues/2464
-    const transactionRequest = this.connection.documentClient.transactWrite(
-      transactionInput
-    );
+    const transactionRequest = this._dc.transactWrite(transactionInput);
 
     let cancellationReasons: any[];
     transactionRequest.on('extractError', response => {
diff --git a/packages/core/classes/metadata/entity-metadata.ts b/packages/core/classes/metadata/entity-metadata.ts
index ab7863c..264a47b 100644
--- a/packages/core/classes/metadata/entity-metadata.ts
+++ b/packages/core/classes/metadata/entity-metadata.ts
@@ -1,11 +1,4 @@
-import {
-  EntityTarget,
-  INDEX_TYPE,
-  INTERNAL_ENTITY_ATTRIBUTE,
-  IndexOptions,
-  IsCompositePrimaryKey,
-  Table,
-} from '@typedorm/common';
+import {EntityTarget, INDEX_TYPE} from '@typedorm/common';
 import {getInterpolatedKeys} from '../../helpers/get-interpolated-keys';
 import {validateKey} from '../../helpers/validate-key';
 import {Connection} from '../connection/connection';
@@ -14,10 +7,10 @@ import {
   Indexes,
   PrimaryKey,
 } from '@typedorm/common/metadata-storage';
+import {IndexOptions, IsCompositePrimaryKey, Table} from '@typedorm/common';
 import {AttributeMetadata} from './attribute-metadata';
 import {AutoGeneratedAttributeMetadata} from './auto-generated-attribute-metadata';
 import {BaseMetadata} from './base-metadata';
-import {InternalAttributeMetadata} from './internal-attribute-metadata';
 
 export type DynamoEntitySchemaPrimaryKey = {
   [key: string]: any;
@@ -55,7 +48,6 @@ export class EntityMetadata extends BaseMetadata {
   readonly table: Table;
   readonly target: EntityTarget<any>;
   readonly attributes: AttributeMetadataType[];
-  readonly internalAttributes: InternalAttributeMetadata[];
   readonly schema: DynamoEntitySchema;
   constructor({
     connection,
@@ -71,15 +63,6 @@ export class EntityMetadata extends BaseMetadata {
     this.target = target;
     this.attributes = attributes;
 
-    // auto persist internal attributes
-    this.internalAttributes = [
-      new InternalAttributeMetadata({
-        name: INTERNAL_ENTITY_ATTRIBUTE.ENTITY_NAME,
-        type: 'String',
-        value: this.name,
-      }),
-    ];
-
     if (table) {
       // if no entity level table is defined fallback to global connection table
       this.table = table;
@@ -87,9 +70,7 @@ export class EntityMetadata extends BaseMetadata {
       this.table = connection.table;
     }
 
-    // validate attributes and key type pair
     const attributesKeyTypePair = this.attributes.reduce((acc, attr) => {
-      this.validateAttributeMetadata(attr);
       acc[attr.name] = attr.type;
       return acc;
     }, {} as {[key: string]: string});
@@ -110,18 +91,6 @@ export class EntityMetadata extends BaseMetadata {
     };
   }
 
-  private validateAttributeMetadata(
-    attributeToValidate: AttributeMetadataType
-  ) {
-    this.internalAttributes.forEach(internalAttr => {
-      if (attributeToValidate.name === internalAttr.name) {
-        throw new Error(
-          `Attribute name "${attributeToValidate.name}" is reserved by TypeDORM and cannot be used.`
-        );
-      }
-    });
-  }
-
   private validatePrimaryKey(
     table: Table,
     primaryKey: PrimaryKey,
diff --git a/packages/core/classes/metadata/internal-attribute-metadata.ts b/packages/core/classes/metadata/internal-attribute-metadata.ts
deleted file mode 100644
index 86a9d4b..0000000
--- a/packages/core/classes/metadata/internal-attribute-metadata.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import {
-  AttributeMetadata,
-  AttributeMetadataOptions,
-} from './attribute-metadata';
-
-export interface InternalAttributeMetadataOptions
-  extends AttributeMetadataOptions {
-  value: any;
-}
-
-export class InternalAttributeMetadata extends AttributeMetadata {
-  readonly value: any;
-  constructor(options: InternalAttributeMetadataOptions) {
-    super(options);
-    this.value = options.value;
-  }
-}
diff --git a/packages/core/classes/transformer/document-client-request-transformer.ts b/packages/core/classes/transformer/document-client-request-transformer.ts
index cadc201..c1519f4 100644
--- a/packages/core/classes/transformer/document-client-request-transformer.ts
+++ b/packages/core/classes/transformer/document-client-request-transformer.ts
@@ -99,21 +99,13 @@ export class DocumentClientRequestTransformer extends BaseTransformer {
     options?: ManagerToDynamoPutItemOptions
   ): DocumentClient.PutItemInput | DocumentClient.PutItemInput[] {
     const entityClass = getConstructorForInstance(entity);
-    const {table, name, internalAttributes} = this.connection.getEntityByTarget(
-      entityClass
-    );
+    const {table, name} = this.connection.getEntityByTarget(entityClass);
 
     const uniqueAttributes = this.connection.getUniqueAttributesForEntity(
       entityClass
     ) as AttributeMetadata[];
 
     const dynamoEntity = this.toDynamoEntity(entity);
-
-    const entityInternalAttributes = internalAttributes.reduce((acc, attr) => {
-      acc[attr.name] = attr.value;
-      return acc;
-    }, {} as DocumentClient.PutItemInputAttributeMap);
-
     let uniqueAttributePutItems = [] as DocumentClient.PutItemInput[];
     // apply attribute not exist condition when creating unique
     const uniqueRecordConditionExpression = this._expressionBuilder.buildConditionExpression(
@@ -149,7 +141,6 @@ export class DocumentClientRequestTransformer extends BaseTransformer {
 
     let dynamoPutItem = {
       Item: {
-        ...entityInternalAttributes,
         ...dynamoEntity,
       },
       TableName: table.name,
diff --git a/packages/core/classes/transformer/entity-transformer.ts b/packages/core/classes/transformer/entity-transformer.ts
index 48efe97..3f864a0 100644
--- a/packages/core/classes/transformer/entity-transformer.ts
+++ b/packages/core/classes/transformer/entity-transformer.ts
@@ -20,9 +20,6 @@ export class EntityTransformer extends BaseTransformer {
     const entityMetadata = this.connection.getEntityByTarget(entityClass);
 
     const entityPrimaryKeys = Object.keys(entityMetadata.schema.primaryKey);
-    const entityInternalAttributeKeys = entityMetadata.internalAttributes.map(
-      attr => attr.name
-    );
 
     const entityMetadataSchemaIndexes = entityMetadata.schema.indexes ?? {};
     const entityIndexes = Object.keys(entityMetadataSchemaIndexes)
@@ -33,11 +30,7 @@ export class EntityTransformer extends BaseTransformer {
       .flat();
 
     return Object.keys(dynamoEntity).reduce((acc, key) => {
-      if (
-        entityPrimaryKeys.includes(key) ||
-        entityIndexes.includes(key) ||
-        entityInternalAttributeKeys.includes(key)
-      ) {
+      if (entityPrimaryKeys.includes(key) || entityIndexes.includes(key)) {
         return acc;
       }
       (acc as any)[key] = (dynamoEntity as any)[key];


Test patch:
diff --git a/packages/common/decorators/__test__/attribute.decorator.spec.ts b/packages/common/decorators/__test__/attribute.decorator.spec.ts
index f547ff2..e42c83e 100644
--- a/packages/common/decorators/__test__/attribute.decorator.spec.ts
+++ b/packages/common/decorators/__test__/attribute.decorator.spec.ts
@@ -30,3 +30,33 @@ test('adds raw metadata', () => {
     },
   ]);
 });
+
+/**
+ * Issue #29
+ */
+test('adds raw metadata for enum property', () => {
+  enum ROLE {
+    ADMIN = 'admin',
+    USER = 'user',
+  }
+  @Entity({
+    name: 'user',
+    primaryKey: {
+      partitionKey: 'USER#{{role}}',
+    },
+  })
+  class User {
+    @Attribute({
+      isEnum: true,
+    })
+    role: ROLE;
+  }
+  expect(
+    MetadataManager.metadataStorage.getRawAttributesForEntity(User)
+  ).toEqual([
+    {
+      name: 'role',
+      type: 'String',
+    },
+  ]);
+});
diff --git a/packages/core/classes/manager/__test__/entity-manager.spec.ts b/packages/core/classes/manager/__test__/entity-manager.spec.ts
index 9414fc1..d6c78ae 100644
--- a/packages/core/classes/manager/__test__/entity-manager.spec.ts
+++ b/packages/core/classes/manager/__test__/entity-manager.spec.ts
@@ -1,3 +1,18 @@
+// This needs to be at the top of the file before
+// any other modules are imported that might
+// bring in original instance of mock
+const dcMock = jest.fn().mockReturnValue({
+  put: jest.fn(),
+  get: jest.fn(),
+  update: jest.fn(),
+  delete: jest.fn(),
+  query: jest.fn(),
+});
+jest.mock('aws-sdk', () => ({
+  DynamoDB: {
+    DocumentClient: dcMock,
+  },
+}));
 jest.useFakeTimers('modern').setSystemTime(new Date(1606896235000));
 
 import {createTestConnection, resetTestConnection} from '@typedorm/testing';
@@ -13,19 +28,10 @@ import {
 } from '@typedorm/core/__mocks__/user-auto-generate-attributes';
 
 let manager: EntityManager;
-const dcMock = {
-  put: jest.fn(),
-  get: jest.fn(),
-  update: jest.fn(),
-  delete: jest.fn(),
-  query: jest.fn(),
-};
 beforeEach(() => {
   const connection = createTestConnection({
     entities: [User, UserUniqueEmail, UserAutoGenerateAttributes],
-    documentClient: dcMock,
   });
-
   manager = new EntityManager(connection);
 });
 
@@ -37,7 +43,7 @@ afterEach(() => {
  * @group create
  */
 test('creates entity', async () => {
-  dcMock.put.mockReturnValue({
+  dcMock().put.mockReturnValue({
     promise: () => ({}),
   });
 
@@ -47,15 +53,14 @@ test('creates entity', async () => {
   user.status = 'active';
 
   const userEntity = await manager.create(user);
-  expect(dcMock.put).toHaveBeenCalledTimes(1);
-  expect(dcMock.put).toHaveBeenCalledWith({
+  expect(dcMock().put).toHaveBeenCalledTimes(1);
+  expect(dcMock().put).toHaveBeenCalledWith({
     Item: {
       GSI1PK: 'USER#STATUS#active',
       GSI1SK: 'USER#Test User',
       PK: 'USER#1',
       SK: 'USER#1',
       id: '1',
-      __en: 'user',
       name: 'Test User',
       status: 'active',
     },
@@ -76,7 +81,7 @@ test('creates entity', async () => {
  * Issue: #11
  */
 test('creates entity and returns all attributes, including auto generated ones', async () => {
-  dcMock.put.mockReturnValue({
+  dcMock().put.mockReturnValue({
     promise: () => ({}),
   });
 
@@ -84,15 +89,14 @@ test('creates entity and returns all attributes, including auto generated ones',
   user.id = '1';
 
   const userEntity = await manager.create(user);
-  expect(dcMock.put).toHaveBeenCalledTimes(1);
-  expect(dcMock.put).toHaveBeenCalledWith({
+  expect(dcMock().put).toHaveBeenCalledTimes(1);
+  expect(dcMock().put).toHaveBeenCalledWith({
     Item: {
       GSI1PK: 'USER#UPDATED_AT#1606896235',
       GSI1SK: 'USER#1',
       PK: 'USER#1',
       SK: 'USER#1',
       id: '1',
-      __en: 'user',
       updatedAt: 1606896235,
     },
     TableName: 'test-table',
@@ -111,7 +115,7 @@ test('creates entity and returns all attributes, including auto generated ones',
  * @group findOne
  */
 test('finds one entity by given primary key', async () => {
-  dcMock.get.mockReturnValue({
+  dcMock().get.mockReturnValue({
     promise: () => ({
       Item: {
         PK: 'USER#1',
@@ -128,8 +132,8 @@ test('finds one entity by given primary key', async () => {
   const userEntity = await manager.findOne<UserPrimaryKey, User>(User, {
     id: '1',
   });
-  expect(dcMock.get).toHaveBeenCalledTimes(1);
-  expect(dcMock.get).toHaveBeenCalledWith({
+  expect(dcMock().get).toHaveBeenCalledTimes(1);
+  expect(dcMock().get).toHaveBeenCalledWith({
     Key: {
       PK: 'USER#1',
       SK: 'USER#1',
@@ -155,7 +159,7 @@ test('throws an error when trying to do a get request with non primary key attri
  * @group exists
  */
 test('checks if given item exists', async () => {
-  dcMock.get.mockReturnValue({
+  dcMock().get.mockReturnValue({
     promise: () => ({
       Item: {
         PK: 'USER#1',
@@ -176,7 +180,7 @@ test('checks if given item exists', async () => {
     }
   );
 
-  expect(dcMock.get).toHaveBeenCalledWith({
+  expect(dcMock().get).toHaveBeenCalledWith({
     Key: {
       PK: 'USER#1',
       SK: 'USER#1',
@@ -191,7 +195,7 @@ test('checks if item with given unique attribute exists', async () => {
     email: 'user@example.com',
   });
 
-  expect(dcMock.get).toHaveBeenCalledWith({
+  expect(dcMock().get).toHaveBeenCalledWith({
     Key: {
       PK: 'DRM_GEN_USERUNIQUEEMAIL.EMAIL#user@example.com',
       SK: 'DRM_GEN_USERUNIQUEEMAIL.EMAIL#user@example.com',
@@ -202,7 +206,7 @@ test('checks if item with given unique attribute exists', async () => {
 });
 
 test('throws an error if trying to perform exists check with non key or non unique attributes', async () => {
-  expect(dcMock.get).not.toHaveBeenCalled();
+  expect(dcMock().get).not.toHaveBeenCalled();
   await expect(
     async () =>
       await manager.exists<UserUniqueEmail>(UserUniqueEmail, {
@@ -225,7 +229,7 @@ test('throws an error if trying to perform exists check with partial primary key
  * @group update
  */
 test('updates item', async () => {
-  dcMock.update.mockReturnValue({
+  dcMock().update.mockReturnValue({
     promise: () => ({
       Attributes: {
         PK: 'USER#1',
@@ -247,7 +251,7 @@ test('updates item', async () => {
     }
   );
 
-  expect(dcMock.update).toHaveBeenCalledWith({
+  expect(dcMock().update).toHaveBeenCalledWith({
     ExpressionAttributeNames: {
       '#attr0': 'name',
       '#attr1': 'status',
@@ -275,7 +279,7 @@ test('updates item', async () => {
 test('updates item and attributes marked to be autoUpdated', async () => {
   jest.useFakeTimers('modern').setSystemTime(new Date('2020-01-01'));
 
-  dcMock.update.mockReturnValue({
+  dcMock().update.mockReturnValue({
     promise: () => ({
       Attributes: {
         PK: 'USER#1',
@@ -301,7 +305,7 @@ test('updates item and attributes marked to be autoUpdated', async () => {
     }
   );
 
-  expect(dcMock.update).toHaveBeenCalledWith({
+  expect(dcMock().update).toHaveBeenCalledWith({
     ExpressionAttributeNames: {
       '#attr0': 'updatedAt',
       '#attr1': 'GSI1PK',
@@ -325,7 +329,7 @@ test('updates item and attributes marked to be autoUpdated', async () => {
  * @group delete
  */
 test('deletes item by primary key', async () => {
-  dcMock.delete.mockReturnValue({
+  dcMock().delete.mockReturnValue({
     promise: jest.fn().mockReturnValue({
       Attributes: {},
     }),
@@ -335,7 +339,7 @@ test('deletes item by primary key', async () => {
     id: '1',
   });
 
-  expect(dcMock.delete).toHaveBeenCalledWith({
+  expect(dcMock().delete).toHaveBeenCalledWith({
     Key: {
       PK: 'USER#1',
       SK: 'USER#1',
@@ -359,7 +363,7 @@ test('throws an error when trying to delete item by non primary key attributes',
  * @group find
  */
 test('finds items matching given query params', async () => {
-  dcMock.query.mockReturnValue({
+  dcMock().query.mockReturnValue({
     promise: jest.fn().mockReturnValue({
       Items: [
         {
@@ -397,8 +401,8 @@ test('finds items matching given query params', async () => {
     }
   );
 
-  expect(dcMock.query).toHaveBeenCalledTimes(1);
-  expect(dcMock.query).toHaveBeenCalledWith({
+  expect(dcMock().query).toHaveBeenCalledTimes(1);
+  expect(dcMock().query).toHaveBeenCalledWith({
     ExpressionAttributeNames: {
       '#KY_CE_PK': 'PK',
       '#KY_CE_SK': 'SK',
@@ -430,7 +434,7 @@ test('finds items matching given query params', async () => {
 });
 
 test('finds item from given cursor position', async () => {
-  dcMock.query.mockReturnValue({
+  dcMock().query.mockReturnValue({
     promise: jest.fn().mockReturnValue({
       Items: [
         {
@@ -472,7 +476,7 @@ test('finds item from given cursor position', async () => {
     }
   );
 
-  expect(dcMock.query).toHaveBeenCalledWith({
+  expect(dcMock().query).toHaveBeenCalledWith({
     ExclusiveStartKey: {
       partitionKey: 'USER#1',
       sortKey: 'USER#1',
@@ -503,8 +507,8 @@ test('queries items until limit is met', async () => {
       SK: `USER#${index}`,
     });
   }
-  dcMock.query
-    .mockImplementationOnce(() => ({
+  dcMock()
+    .query.mockImplementationOnce(() => ({
       promise: jest.fn().mockReturnValue({
         Items: itemsToReturn,
         Count: itemsToReturn.length,
@@ -534,8 +538,8 @@ test('queries items until limit is met', async () => {
     }
   );
 
-  expect(dcMock.query).toHaveBeenCalledTimes(2);
-  expect(dcMock.query.mock.calls).toEqual([
+  expect(dcMock().query).toHaveBeenCalledTimes(2);
+  expect(dcMock().query.mock.calls).toEqual([
     [
       {
         ExpressionAttributeNames: {
diff --git a/packages/core/classes/manager/__test__/transaction-manager.spec.ts b/packages/core/classes/manager/__test__/transaction-manager-write.spec.ts
similarity index 87%
rename from packages/core/classes/manager/__test__/transaction-manager.spec.ts
rename to packages/core/classes/manager/__test__/transaction-manager-write.spec.ts
index 5c7e80b..b3a4b16 100644
--- a/packages/core/classes/manager/__test__/transaction-manager.spec.ts
+++ b/packages/core/classes/manager/__test__/transaction-manager-write.spec.ts
@@ -1,3 +1,15 @@
+// This needs to be at the top of the file before
+// any other modules are imported that might
+// bring in original instance of mock
+const dcMock = jest.fn().mockReturnValue({
+  transactWrite: jest.fn(),
+});
+jest.mock('aws-sdk', () => ({
+  DynamoDB: {
+    DocumentClient: dcMock,
+  },
+}));
+
 import {UserPrimaryKey} from '../../../__mocks__/user';
 import {createTestConnection, resetTestConnection} from '@typedorm/testing';
 import {Connection} from '../../connection/connection';
@@ -6,17 +18,12 @@ import {TransactionManager} from '../transaction-manager';
 import {User} from '../../../__mocks__/user';
 
 let manager: TransactionManager;
-const dcMock = {
-  transactWrite: jest.fn(),
-};
-
 let connection: Connection;
 beforeEach(() => {
   connection = createTestConnection({
     entities: [User],
-    documentClient: dcMock,
   });
-  manager = new TransactionManager(connection);
+  manager = new TransactionManager();
 });
 
 afterEach(() => {
@@ -27,7 +34,7 @@ afterEach(() => {
  * @group write
  */
 test('performs write transactions', async () => {
-  dcMock.transactWrite.mockReturnValue({
+  dcMock().transactWrite.mockReturnValue({
     promise: jest.fn().mockReturnValue({
       ConsumedCapacity: [{}],
       ItemCollectionMetrics: [{}],
@@ -76,8 +83,8 @@ test('performs write transactions', async () => {
 
   const response = await manager.write(transaction);
 
-  expect(dcMock.transactWrite).toHaveBeenCalledTimes(1);
-  expect(dcMock.transactWrite).toHaveBeenCalledWith({
+  expect(dcMock().transactWrite).toHaveBeenCalledTimes(1);
+  expect(dcMock().transactWrite).toHaveBeenCalledWith({
     TransactItems: [
       {
         Put: {
@@ -87,7 +94,6 @@ test('performs write transactions', async () => {
             PK: 'USER#1',
             SK: 'USER#1',
             id: '1',
-            __en: 'user',
             name: 'user name',
             status: 'inactive',
           },
@@ -125,7 +131,6 @@ test('performs write transactions', async () => {
             PK: 'USER#2',
             SK: 'USER#2',
             id: '2',
-            __en: 'user',
             name: 'user 2',
             status: 'inactive',
           },
diff --git a/packages/core/classes/transaction/__test__/write-transaction.spec.ts b/packages/core/classes/transaction/__test__/write-transaction.spec.ts
index 3897635..caee895 100644
--- a/packages/core/classes/transaction/__test__/write-transaction.spec.ts
+++ b/packages/core/classes/transaction/__test__/write-transaction.spec.ts
@@ -69,7 +69,6 @@ test('chains simple transaction requests', () => {
           GSI1SK: 'USER#user-name',
           PK: 'USER#123',
           SK: 'USER#123',
-          __en: 'user',
           id: '123',
           name: 'user-name',
           status: 'active',
@@ -136,7 +135,6 @@ test('chains complex transaction requests', () => {
           GSI1SK: 'USER#new user',
           email: 'user@example.com',
           id: '1',
-          __en: 'user',
           name: 'new user',
           status: 'inactive',
         },
diff --git a/packages/core/classes/transformer/__test__/dc-request-transformer.spec.ts b/packages/core/classes/transformer/__test__/dc-request-transformer.spec.ts
index 81663a0..d491b60 100644
--- a/packages/core/classes/transformer/__test__/dc-request-transformer.spec.ts
+++ b/packages/core/classes/transformer/__test__/dc-request-transformer.spec.ts
@@ -66,7 +66,6 @@ test('transforms put item requests', () => {
       SK: 'USER#1',
       id: '1',
       name: 'Tito',
-      __en: 'user',
       status: 'active',
     },
     ConditionExpression: 'attribute_not_exists(#CE_PK)',
@@ -117,7 +116,6 @@ test('transforms put item request with unique attributes', () => {
         SK: 'USER#1',
         email: 'user@example.com',
         id: '1',
-        __en: 'user',
       },
       TableName: 'test-table',
     },
diff --git a/packages/core/classes/transformer/__test__/entity-transformer.spec.ts b/packages/core/classes/transformer/__test__/entity-transformer.spec.ts
index b64ac9b..cb1ca9b 100644
--- a/packages/core/classes/transformer/__test__/entity-transformer.spec.ts
+++ b/packages/core/classes/transformer/__test__/entity-transformer.spec.ts
@@ -59,21 +59,6 @@ test('transforms inherited dynamo entity to entity model', () => {
   });
 });
 
-test('excludes internal attributes from transformed object', () => {
-  const dynamoEntity = {
-    PK: 'CUS#1',
-    SK: 'CUS#user@example.com',
-    id: '1',
-    name: 'Me',
-    __en: 'user',
-  };
-  const transformed = transformer.fromDynamoEntity(User, dynamoEntity);
-  expect(transformed).toEqual({
-    id: '1',
-    name: 'Me',
-  });
-});
-
 /**
  * @group toDynamoEntity
  */
diff --git a/packages/testing/BUILD b/packages/testing/BUILD
index 15fccd8..74df0d0 100644
--- a/packages/testing/BUILD
+++ b/packages/testing/BUILD
@@ -5,10 +5,7 @@ setup_ts_build(
     name = "library",
     module_name = "@typedorm/testing",
     deps = [
-        "//packages/core:library",
-        "//packages/common:library",
-        "@npm//aws-sdk",
-        "@npm//@types/jest"
+        "//packages/core:library"
     ],
     visibility = ["//packages:__subpackages__"]
 
diff --git a/packages/testing/index.ts b/packages/testing/index.ts
index cf42201..118ac9a 100644
--- a/packages/testing/index.ts
+++ b/packages/testing/index.ts
@@ -1,21 +1,9 @@
-import {Replace} from '@typedorm/common';
 import {createConnection, ConnectionOptions} from '@typedorm/core';
 import {ConnectionManager} from '@typedorm/core/classes/connection/connection-manager';
 import {Container} from '@typedorm/core/classes/container';
-import {DynamoDB} from 'aws-sdk';
 
-export function createTestConnection(
-  connectionOptions: Replace<
-    ConnectionOptions,
-    'documentClient',
-    {
-      documentClient?: {
-        [key in keyof DynamoDB.DocumentClient]?: jest.SpyInstance;
-      };
-    }
-  >
-) {
-  return createConnection(connectionOptions as ConnectionOptions);
+export function createTestConnection(connectionOptions: ConnectionOptions) {
+  return createConnection(connectionOptions);
 }
 
 export function resetTestConnection() {
diff --git a/packages/testing/package.json b/packages/testing/package.json
index fea233d..86fd853 100644
--- a/packages/testing/package.json
+++ b/packages/testing/package.json
@@ -11,8 +11,7 @@
   "peerDependencies": {
     "@typedorm/common": "0.0.0-PLACEHOLDER",
     "@typedorm/core": "0.0.0-PLACEHOLDER",
-    "reflect-metadata": "^0.1.13",
-    "aws-sdk": "^2.799.0"
+    "reflect-metadata": "^0.1.13"
   },
   "publishConfig": {
     "tag": "TAG-PLACEHOLDER",