Repository: typedorm/typedorm (ts)

Issue:
Document client request transformer create does no respect default values
Document client request transformer does not respect attributes with default values when parsing indexes.

Pull request:
Correctly parse default values
# Bug fixes 
- correctly parse default attribute values

closes #135 

Patch:
diff --git a/packages/core/__mocks__/user-default-value.ts b/packages/core/__mocks__/user-default-value.ts
new file mode 100644
index 0000000..541ffce
--- /dev/null
+++ b/packages/core/__mocks__/user-default-value.ts
@@ -0,0 +1,27 @@
+import {Attribute, Entity, INDEX_TYPE} from '@typedorm/common';
+import {table} from './table';
+
+@Entity({
+  table,
+  name: 'user-with-default-values',
+  primaryKey: {
+    partitionKey: 'USER#{{id}}',
+    sortKey: 'USER#{{id}}',
+  },
+  indexes: {
+    GSI1: {
+      partitionKey: 'USER#STATUS#{{status}}',
+      sortKey: 'USER#{{status}}',
+      type: INDEX_TYPE.GSI,
+    },
+  },
+})
+export class UserWithDefaultValues {
+  @Attribute()
+  id: string;
+
+  @Attribute({
+    default: 'active',
+  })
+  status: string;
+}
diff --git a/packages/core/src/classes/transformer/base-transformer.ts b/packages/core/src/classes/transformer/base-transformer.ts
index 46d4a44..2b2a57d 100644
--- a/packages/core/src/classes/transformer/base-transformer.ts
+++ b/packages/core/src/classes/transformer/base-transformer.ts
@@ -1,3 +1,4 @@
+import {AttributeMetadata} from './../metadata/attribute-metadata';
 import {
   DynamoEntityIndexesSchema,
   DynamoEntityIndexSchema,
@@ -41,14 +42,28 @@ export abstract class BaseTransformer {
    * @param entity Entity to transform to DynamoDB entity type
    */
   toDynamoEntity<Entity>(entity: Entity) {
+    const explicitAttributes = {...entity};
     const entityClass = getConstructorForInstance(entity);
     // retrieve metadata and parse it to schema
     const entityMetadata = this.connection.getEntityByTarget(entityClass);
 
-    //  auto populate generated values
     this.connection.getAttributesForEntity(entityClass).forEach(attr => {
+      //  auto populate generated values
       if (IsAutoGeneratedAttributeMetadata(attr)) {
-        entity = Object.assign({...entity, [attr.name]: attr.value});
+        entity = Object.assign(entity, {[attr.name]: attr.value});
+      }
+
+      const attributeDefaultValue = (attr as AttributeMetadata)?.default;
+
+      // include attribute with default value
+      if (
+        attributeDefaultValue &&
+        typeof attributeDefaultValue === 'function'
+      ) {
+        const attrDefaultValue = attributeDefaultValue(entity);
+        entity = Object.assign(entity, {
+          [attr.name]: attrDefaultValue,
+        });
       }
     });
 
@@ -102,7 +117,7 @@ export abstract class BaseTransformer {
       ...parsedIndexes,
     };
 
-    return {...entity, ...formattedSchema};
+    return {...entity, ...formattedSchema, ...explicitAttributes};
   }
 
   /**
diff --git a/packages/core/src/classes/transformer/document-client-request-transformer.ts b/packages/core/src/classes/transformer/document-client-request-transformer.ts
index d031878..b267059 100644
--- a/packages/core/src/classes/transformer/document-client-request-transformer.ts
+++ b/packages/core/src/classes/transformer/document-client-request-transformer.ts
@@ -110,12 +110,9 @@ export class DocumentClientRequestTransformer extends BaseTransformer {
     | DynamoDB.DocumentClient.PutItemInput
     | DynamoDB.DocumentClient.TransactWriteItemList {
     const entityClass = getConstructorForInstance(entity);
-    const {
-      table,
-      internalAttributes,
-      name,
-      attributes,
-    } = this.connection.getEntityByTarget(entityClass);
+    const {table, internalAttributes, name} = this.connection.getEntityByTarget(
+      entityClass
+    );
 
     this.connection.logger.logTransform({
       requestId: metadataOptions?.requestId,
@@ -131,6 +128,7 @@ export class DocumentClientRequestTransformer extends BaseTransformer {
       entityClass
     ) as AttributeMetadata[];
 
+    // include attributes with default values in
     const dynamoEntity = this.toDynamoEntity(entity);
 
     const entityInternalAttributes = internalAttributes.reduce((acc, attr) => {
@@ -138,24 +136,9 @@ export class DocumentClientRequestTransformer extends BaseTransformer {
       return acc;
     }, {} as DynamoDB.DocumentClient.PutItemInputAttributeMap);
 
-    const attributesWithDefaultValues = attributes
-      .filter(
-        attr =>
-          !!(
-            (attr as AttributeMetadata)?.default &&
-            typeof (attr as AttributeMetadata)?.default === 'function'
-          )
-      )
-      .reduce((acc, attr) => {
-        const defaultValueProvider: any = (attr as AttributeMetadata).default;
-        acc[attr.name] = defaultValueProvider(dynamoEntity);
-        return acc;
-      }, {} as DynamoDB.DocumentClient.PutItemInputAttributeMap);
-
     let dynamoPutItem = {
       Item: {
         ...entityInternalAttributes,
-        ...attributesWithDefaultValues,
         ...dynamoEntity,
       },
       TableName: table.name,


Test patch:
diff --git a/packages/core/src/classes/connection/__test__/connection-metadata-builder.spec.ts b/packages/core/src/classes/connection/__test__/connection-metadata-builder.spec.ts
index c060e67..cdf8764 100644
--- a/packages/core/src/classes/connection/__test__/connection-metadata-builder.spec.ts
+++ b/packages/core/src/classes/connection/__test__/connection-metadata-builder.spec.ts
@@ -35,5 +35,5 @@ test('builds entity metadata with path match', () => {
     path.resolve(__dirname, '../../../../__mocks__/**/*.ts')
   );
 
-  expect(entities.length).toEqual(7);
+  expect(entities.length).toEqual(8);
 });
diff --git a/packages/core/src/classes/transformer/__test__/entity-transformer.spec.ts b/packages/core/src/classes/transformer/__test__/entity-transformer.spec.ts
index c621545..467de3a 100644
--- a/packages/core/src/classes/transformer/__test__/entity-transformer.spec.ts
+++ b/packages/core/src/classes/transformer/__test__/entity-transformer.spec.ts
@@ -14,6 +14,7 @@ import {EntityTransformer} from '../entity-transformer';
 import {UserSparseIndexes} from '../../../../__mocks__/user-sparse-indexes';
 import {table} from '@typedorm/core/__mocks__/table';
 import {UserAttrAlias} from '@typedorm/core/__mocks__/user-with-attribute-alias';
+import {UserWithDefaultValues} from '@typedorm/core/__mocks__/user-default-value';
 
 let transformer: EntityTransformer;
 beforeEach(() => {
@@ -24,6 +25,7 @@ beforeEach(() => {
       UserAutoGenerateAttributes,
       UserSparseIndexes,
       UserAttrAlias,
+      UserWithDefaultValues,
     ],
   });
   transformer = new EntityTransformer(connection);
@@ -343,6 +345,24 @@ test('transforms complex model model to dynamo entity', () => {
   });
 });
 
+/**
+ * Issue #135
+ */
+test('transforms put item requests with attributes containing default values', () => {
+  const user = new UserWithDefaultValues();
+  user.id = '1';
+
+  const putItem = transformer.toDynamoEntity(user);
+  expect(putItem).toEqual({
+    GSI1PK: 'USER#STATUS#active',
+    GSI1SK: 'USER#active',
+    PK: 'USER#1',
+    SK: 'USER#1',
+    id: '1',
+    status: 'active',
+  });
+});
+
 /**
  * @group getAffectedIndexesForAttributes
  */