Skip to content

Commit ba40c77

Browse files
committed
add transformation functions
1 parent 917ea4e commit ba40c77

File tree

9 files changed

+122
-24
lines changed

9 files changed

+122
-24
lines changed

__tests__/entity-creation.unit.test.js

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,99 @@ describe('Entity creation', ()=> {
471471
entity: 'TestEnt', pk: 'test', test: 'testx', num: 5
472472
})
473473

474-
475474
}) // creates attribute with prefix/suffix
476475

476+
477+
it('creates an attribute a transformation function', async () => {
478+
479+
// Create basic table
480+
const TestTable = new Table({
481+
name: 'test-table',
482+
partitionKey: 'pk',
483+
sortKey: 'sk',
484+
DocumentClient
485+
})
486+
487+
// Create basic entity
488+
const TestEntity = new Entity({
489+
name: 'TestEnt',
490+
attributes: {
491+
pk: { partitionKey: true, transform: (val) => val.toUpperCase(), default: 'pkDef' },
492+
test: {
493+
transform: (val,data) => {
494+
return val.toUpperCase()
495+
},
496+
default: () => 'defaultVal',
497+
prefix: 'pre-'
498+
},
499+
sk: { type: 'string', prefix: 'testprev-', sortKey: true, delimiter: '|' },
500+
testx: ['sk',0],
501+
testy: ['sk',1, {
502+
default: () => 'testDefaultX',
503+
transform: (val) => { return '__'+val.toUpperCase() }
504+
}]
505+
},
506+
table: TestTable,
507+
timestamps: false
508+
})
509+
510+
let result = TestEntity.putParams({
511+
testx: 1
512+
})
513+
514+
expect(result).toEqual({
515+
TableName: 'test-table',
516+
Item: {
517+
sk: 'testprev-1|__TESTDEFAULTX',
518+
pk: 'PKDEF',
519+
test: 'pre-DEFAULTVAL',
520+
testy: '__TESTDEFAULTX',
521+
_et: 'TestEnt',
522+
testx: '1'
523+
}
524+
})
525+
526+
let result2 = TestEntity.getParams({
527+
testx: 'test',
528+
testy: 'testx'
529+
})
530+
531+
expect(result2).toEqual({
532+
TableName: 'test-table',
533+
Key: { pk: 'PKDEF', sk: 'testprev-test|__TESTX' }
534+
})
535+
536+
let result3 = TestEntity.updateParams({
537+
testx: 'test',
538+
testy: 'testx',
539+
test: 'uppercase'
540+
})
541+
542+
expect(result3).toEqual({
543+
TableName: 'test-table',
544+
Key: { pk: 'PKDEF', sk: 'testprev-test|__TESTX' },
545+
UpdateExpression: 'SET #test = :test, #testy = :testy, #_et = if_not_exists(#_et,:_et), #testx = :testx',
546+
ExpressionAttributeNames: {
547+
'#test': 'test',
548+
'#testy': 'testy',
549+
'#_et': '_et',
550+
'#testx': 'testx'
551+
},
552+
ExpressionAttributeValues: {
553+
':test': 'pre-UPPERCASE',
554+
':testy': '__TESTX',
555+
':_et': 'TestEnt',
556+
':testx': 'test'
557+
}
558+
})
559+
560+
// console.log(result);
561+
// console.log(result2);
562+
// console.log(result3);
563+
564+
565+
566+
}) // creates attribute with transformations
567+
568+
477569
})

__tests__/entity.put.unit.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ describe('put',()=>{
8484
})
8585

8686
it('creates item with aliases',() => {
87-
let { Item } = TestEntity.putParams({ email: 'test-pk', sort: 'test-sk', count: 5 })
87+
let { Item } = TestEntity.putParams({ email: 'test-pk', sort: 'test-sk', count: 0 })
8888

8989
expect(Item.pk).toBe('test-pk')
9090
expect(Item.sk).toBe('test-sk')
91-
expect(Item.test_number).toBe(5)
91+
expect(Item.test_number).toBe(0)
9292
expect(Item._et).toBe('TestEntity')
9393
expect(Item.test_string).toBe('test string')
9494
expect(Item).toHaveProperty('_ct')

__tests__/entity.update.unit.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const TestEntity2 = new Entity({
5050
attributes: {
5151
email: { type: 'string', partitionKey: true },
5252
sort: { type: 'string', map: 'sk' },
53-
test: { type: 'string' },
53+
test: { type: 'string', prefix: 'test---' },
5454
test_composite: ['sort',0, { save: true }],
5555
test_composite2: ['sort',1],
5656
test_undefined: { default: () => undefined }
@@ -552,7 +552,7 @@ describe('update',()=>{
552552

553553
expect(UpdateExpression).toBe('SET #field = :field, #test = :test REMOVE #field_remove = :field_remove ADD #field_add :field_add DELETE #field_delete')
554554
expect(ExpressionAttributeNames).toEqual({ '#test': 'test', '#field': 'field' })
555-
expect(ExpressionAttributeValues).toEqual({ ':test': 'test', ':field': 'my value' })
555+
expect(ExpressionAttributeValues).toEqual({ ':test': 'test---test', ':field': 'my value' })
556556
expect(ConditionExpression).toBe('#field > 0')
557557
})
558558

classes/Entity.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const parseConditions = require('../lib/expressionBuilder')
1818
const parseProjections = require('../lib/projectionBuilder')
1919

2020
// Import error handlers
21-
const { error } = require('../lib/utils')
21+
const { error, transformAttr } = require('../lib/utils')
2222

2323
// Declare Entity class
2424
class Entity {
@@ -616,7 +616,7 @@ class Entity {
616616
names[`#${field}`] = field
617617
// else add to SET
618618
} else {
619-
let value = validateType(mapping,field,data[field],data)
619+
let value = transformAttr(mapping,validateType(mapping,field,data[field],data),data)
620620

621621
// It's possible that defaults can purposely return undefined values
622622
// if (hasValue(value)) {
@@ -787,7 +787,7 @@ class Entity {
787787
&& (!mapping.link || (mapping.link && mapping.save === true))
788788
&& (!_table._removeNulls || (_table._removeNulls && value !== null))
789789
? Object.assign(acc, {
790-
[field]: value
790+
[field]: transformAttr(mapping,value,data)
791791
}) : acc
792792
},{})
793793
},
@@ -804,7 +804,6 @@ class Entity {
804804
} // end putParams
805805

806806

807-
808807
// Query pass-through (default entity)
809808
query(pk,options={},params={}) {
810809
options.entity = this.name
@@ -821,4 +820,4 @@ class Entity {
821820
} // end Entity
822821

823822
// Export the Entity class
824-
module.exports = Entity
823+
module.exports = Entity

lib/getKey.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
const validateTypes = require('./validateTypes')
10-
const { error } = require('./utils')
10+
const { error, transformAttr } = require('./utils')
1111

1212
// Get partitionKey/sortKey
1313
module.exports = (DocumentClient) => (data,schema,partitionKey,sortKey) => {
@@ -26,7 +26,7 @@ module.exports = (DocumentClient) => (data,schema,partitionKey,sortKey) => {
2626
error(`'${sortKey}'${schema[sortKey].alias ? ` or '${schema[sortKey].alias}'` : ''} is required`)
2727

2828
return Object.assign(
29-
{ [partitionKey]: validateType(schema[partitionKey],partitionKey,pk,data) },
30-
sortKey !== null ? { [sortKey]: validateType(schema[sortKey],sortKey,sk,data) } : {}
29+
{ [partitionKey]: transformAttr(schema[partitionKey],validateType(schema[partitionKey],partitionKey,pk,data),data) },
30+
sortKey !== null ? { [sortKey]: transformAttr(schema[sortKey],validateType(schema[sortKey],sortKey,sk,data),data) } : {}
3131
) // end assign
3232
} // end get keys

lib/normalizeData.js

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
const validateTypes = require('./validateTypes')
10-
const { error } = require('./utils')
10+
const { error, transformAttr } = require('./utils')
1111

1212
// Normalize Data
1313
module.exports = (DocumentClient) => (schema,linked,data,filter=false) => {
@@ -16,20 +16,15 @@ module.exports = (DocumentClient) => (schema,linked,data,filter=false) => {
1616
const validateType = validateTypes(DocumentClient)
1717

1818
let _data = Object.keys(data).reduce((acc,field) => {
19-
19+
2020
return Object.assign(acc,
21-
schema[field] ? { [schema[field].map || field] : (
22-
schema[field].prefix || schema[field].suffix
23-
? `${schema[field].prefix || ''}${data[field]}${schema[field].suffix || ''}`
24-
: data[field]
25-
)}
21+
schema[field] ? { [schema[field].map || field] : data[field] }
2622
: filter ? {} // this will filter out non-mapped fields
2723
: field === '$remove' ? { $remove: data[field] } // support for removes
2824
: error(`Field '${field}' does not have a mapping or alias`)
2925
)
3026
},{})
3127

32-
// TODO: rework linked fields?
3328
// Process linked
3429
let composites = Object.keys(linked).reduce((acc,attr) => {
3530

@@ -39,8 +34,8 @@ module.exports = (DocumentClient) => (schema,linked,data,filter=false) => {
3934
if (_data[field] !== undefined) return acc // if value exists, let override
4035
let values = linked[attr].map(f => {
4136
if (_data[f] === undefined) { return null }
42-
return validateType(schema[f],f,_data[f],_data)
43-
}).filter(x => x !== null)
37+
return transformAttr(schema[f],validateType(schema[f],f,_data[f],_data),_data)
38+
}).filter(x => x !== null)
4439

4540
// TODO: add required fields
4641
// if (values.length > 0 && values.length !== linked[field].length) {
@@ -49,7 +44,8 @@ module.exports = (DocumentClient) => (schema,linked,data,filter=false) => {
4944

5045
if (values.length === linked[attr].length) {
5146
return Object.assign(acc, {
52-
[field]: `${schema[attr].prefix || ''}${values.join(schema[attr].delimiter || '#')}${schema[attr].suffix || ''}`
47+
//[field]: `${schema[attr].prefix || ''}${values.join(schema[attr].delimiter || '#')}${schema[attr].suffix || ''}`
48+
[field]: values.join(schema[attr].delimiter || '#')
5349
})
5450
} else {
5551
return acc

lib/parseMapping.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = (field,config,track) => {
1616
switch(prop) {
1717
case 'type':
1818
case 'default':
19+
case 'transform':
1920
break
2021
case 'coerce':
2122
case 'onUpdate':

lib/utils.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,12 @@ module.exports.keyTypeError = field => {
3737
error(`Invalid or missing type for '${field}'. `
3838
+ `Valid types for partitionKey and sortKey are 'string','number' and 'binary'`)
3939
}
40+
41+
42+
// Tranform atribute values
43+
module.exports.transformAttr = (mapping,value,data) => {
44+
value = mapping.transform ? mapping.transform(value,data) : value
45+
return mapping.prefix || mapping.suffix ?
46+
`${mapping.prefix || ''}${value}${mapping.suffix || ''}`
47+
: value
48+
}

lib/validateTypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const { toBool, hasValue, error } = require('./utils')
1212
module.exports = (DocumentClient) => (mapping,field,input,data={}) => {
1313

1414
// Evaluate function expressions
15+
// TODO: should this happen here?
1516
let value = typeof input === 'function' ? input(data) : input
1617

1718
// return if undefined or null

0 commit comments

Comments
 (0)