Repository: jeremydaly/dynamodb-toolbox (ts)

Issue:
Entities need to list all attributes
If an entity does not list an attribute that exists in the database, `get` fails.

To make `await entityName.get(item)` work, in the definition of the entity `entityName`, it needs to list all attributes it may pull from the database for that particular item. Otherwise, it shows an `alias of undefined` error.

This is counterproductive for a single-table NoSQL design as often times, there is data that is there in the database, may get pulled by DynamoDB `get`, but the function doesn't need to be explicit about it.



Pull request:
Allow attributes not specified in entity to be passed through
Fixes #50 

Looking at the code I figured this was a bug, since there were guards for everything else.

PS. Thanks for this library, truly makes single table design approachable 🥇 

Patch:
diff --git a/lib/formatItem.js b/lib/formatItem.js
index 2138bde4..28791391 100644
--- a/lib/formatItem.js
+++ b/lib/formatItem.js
@@ -18,9 +18,9 @@ module.exports = (DocumentClient) => (attributes,linked,item,include=[]) => {
   const validateType = validateTypes(DocumentClient)
 
   return Object.keys(item).reduce((acc,field) => {
-
-    if (linked[field] || linked[attributes[field].alias]) {
-      Object.assign(acc, (linked[field] || linked[attributes[field].alias]).reduce((acc,f,i) => {
+    const link = linked[field] || attributes[field] && attributes[field].alias && linked[attributes[field].alias]
+    if (link) {
+      Object.assign(acc, link.reduce((acc,f,i) => {
         if (attributes[f].save || attributes[f].hidden || (include.length > 0 && !include.includes(f))) return acc    
         return Object.assign(acc,{
           [attributes[f].alias || f]: validateType(attributes[f],f,
@@ -38,7 +38,7 @@ module.exports = (DocumentClient) => (attributes,linked,item,include=[]) => {
     if (attributes[field] && attributes[field].type === 'set' && Array.isArray(item[field].values)) { item[field] = item[field].values } 
     return Object.assign(acc,{
       [(attributes[field] && attributes[field].alias) || field]: (
-        attributes[field].prefix || attributes[field].suffix
+        attributes[field] && (attributes[field].prefix || attributes[field].suffix)
           ? item[field]
             .replace(new RegExp(`^${escapeRegExp(attributes[field].prefix)}`),'')
             .replace(new RegExp(`${escapeRegExp(attributes[field].suffix)}$`),'')


Test patch:
diff --git a/__tests__/formatItem.unit.test.js b/__tests__/formatItem.unit.test.js
index 286fac30..d673d8cd 100644
--- a/__tests__/formatItem.unit.test.js
+++ b/__tests__/formatItem.unit.test.js
@@ -28,7 +28,13 @@ DefaultTable.entities = new Entity({
     list_alias2: { type: 'list', map: 'list2' },
     test: 'map',
     linked1: ['sk',0, { save: false }],
-    linked2: ['sk',1, { save: false }]
+    linked2: ['sk',1, { save: false }],
+    composite1: { type: 'string', alias: 'composite1_alias' },
+    linked3: ['composite1',0, { save: false }],
+    linked4: ['composite1',1, { save: false, alias: 'linked4_alias' }],
+    composite2_alias: { type: 'string', map: 'composite2' },
+    linked5: ['composite2_alias',0, { save: false, }],
+    linked6: ['composite2_alias',1, { save: false, alias: 'linked6_alias' }],
   }
 })
 
@@ -83,4 +89,19 @@ describe('formatItem', () => {
     expect(result).toEqual({ linked1: 'test1' })
   })
 
+  it('formats item with linked aliased composite field', () => {
+    let result = formatItem(DocumentClient)(DefaultTable.User.schema.attributes,DefaultTable.User.linked,{ composite1: 'test1#test2' })  
+    expect(result).toEqual({ composite1_alias: 'test1#test2', linked3: 'test1', linked4_alias: 'test2' })
+  })
+
+  it('formats item with linked mapped composite field', () => {
+    let result = formatItem(DocumentClient)(DefaultTable.User.schema.attributes,DefaultTable.User.linked,{ composite2: 'test1#test2'  })  
+    expect(result).toEqual({ composite2_alias: 'test1#test2', linked5: 'test1', linked6_alias: 'test2' })
+  })
+
+  it('passes through attribute not specified in entity', () => {
+    let result = formatItem(DocumentClient)(DefaultTable.User.schema.attributes,DefaultTable.User.linked,{ unspecified: 'value' })  
+    expect(result).toEqual({ unspecified: 'value' })
+  })
+
 })