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:
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",