Skip to content

Commit 611b08e

Browse files
apply inverseTransform + typeHidden after repo sync
1 parent a86b7a8 commit 611b08e

9 files changed

+99
-18
lines changed

src/__tests__/entities/test-entity.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export = {
2727
test_string_set_type_coerce: { type: 'set', setType: 'string', coerce: true },
2828
test_number_set_type_coerce: { type: 'set', setType: 'number', coerce: true },
2929
test_binary: { type: 'binary' },
30-
simple_string: 'string'
30+
simple_string: 'string',
31+
inverse_transformed_simple_string: {
32+
type: "string",
33+
inverseTransform: (input: string) => input.toUpperCase(),
34+
},
3135
}
3236
}

src/__tests__/entity-creation.unit.test.ts

+43
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,49 @@ describe('Entity creation', ()=> {
314314
expect(result).toThrow(`Please provide a valid entity definition`)
315315
})
316316

317+
it("creates an attribute with a inverseTransformation function", async () => {
318+
// Create basic table
319+
const TestTable = new Table({
320+
name: "test-table",
321+
partitionKey: "pk",
322+
sortKey: "sk",
323+
DocumentClient,
324+
});
325+
326+
// Create basic entity
327+
const TestEntity = new Entity({
328+
name: "TestEnt",
329+
attributes: {
330+
pk: {
331+
partitionKey: true,
332+
inverseTransform: (val) => val.toUpperCase(),
333+
default: "pkDef",
334+
},
335+
test: {
336+
inverseTransform: (val, data) => {
337+
return val.toUpperCase();
338+
},
339+
default: () => "defaultVal",
340+
},
341+
sk: { type: "string", sortKey: true },
342+
testx: ["sk", 0],
343+
testy: [
344+
"sk",
345+
1,
346+
{
347+
default: () => "testDefaultX",
348+
inverseTransform: (val) => {
349+
return "__" + val.toUpperCase();
350+
},
351+
},
352+
],
353+
},
354+
table: TestTable,
355+
timestamps: false,
356+
})
357+
})
358+
359+
317360

318361
// it('creates entity w/ table', async () => {
319362

src/__tests__/entity.parse.unit.test.ts

+10-7
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ const TestTable = new Table({
2222
describe('parse',()=>{
2323

2424
it('parses single item', ()=>{
25-
let item = TestEntity.parse({ pk: '[email protected]', sk: 'email', test_string: 'test', _et: 'TestEntity' })
25+
let item = TestEntity.parse({ pk: '[email protected]', sk: 'email', test_string: 'test', inverse_transformed_simple_string: "transformed", _et: 'TestEntity' })
2626
expect(item).toEqual({
2727
2828
test_type: 'email',
2929
test_string: 'test',
30+
inverse_transformed_simple_string: "TRANSFORMED",
3031
entity: 'TestEntity'
3132
})
3233
})
@@ -41,19 +42,21 @@ describe('parse',()=>{
4142

4243
it('parses multiple items', ()=>{
4344
let items = TestEntity.parse([
44-
{ pk: '[email protected]', sk: 'email', test_string: 'test' },
45-
{ pk: '[email protected]', sk: 'email2', test_string: 'test2' }
45+
{ pk: '[email protected]', sk: 'email', test_string: 'test', inverse_transformed_simple_string: "transformed", },
46+
{ pk: '[email protected]', sk: 'email2', test_string: 'test2', inverse_transformed_simple_string: "transformed", }
4647
])
4748
expect(items).toEqual([
4849
{
4950
5051
test_type: 'email',
51-
test_string: 'test'
52+
test_string: 'test',
53+
inverse_transformed_simple_string: "TRANSFORMED",
5254
},
5355
{
5456
5557
test_type: 'email2',
56-
test_string: 'test2'
58+
test_string: 'test2',
59+
inverse_transformed_simple_string: "TRANSFORMED",
5760
}
5861
])
5962
})
@@ -76,11 +79,11 @@ describe('parse',()=>{
7679
})
7780

7881
it('parses composite field', ()=>{
79-
let item = SimpleEntity.parse({ pk: '[email protected]', sk: 'active#email', test_composite: 'test' })
82+
let item = SimpleEntity.parse({ pk: '[email protected]', sk: 'active#email', test_composite: 'test' })
8083
expect(item).toEqual({
8184
8285
test_composite: 'test',
83-
test_composite2: 'email'
86+
test_composite2: 'email',
8487
})
8588
})
8689

src/__tests__/parseEntity.unit.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const entity: EntityConstructor = {
1313
modified: '_modified',
1414
modifiedAlias: 'modifiedAlias',
1515
typeAlias: 'typeAlias',
16+
typeHidden: true,
1617
attributes: {
1718
pk: { partitionKey: true },
1819
sk: { sortKey: true },
@@ -38,6 +39,7 @@ describe('parseEntity', () => {
3839
expect(ent.autoExecute).toBe(true)
3940
expect(ent.autoParse).toBe(true)
4041
expect(ent._etAlias).toBe('typeAlias')
42+
expect(ent.typeHidden).toBe(true)
4143
})
4244

4345
it('fails on extra config option', async () => {

src/classes/Entity.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ export interface EntityConstructor {
4141
autoExecute?: boolean
4242
autoParse?: boolean
4343
table?: Table
44+
typeHidden?: boolean;
4445
}
4546

4647
export interface EntityAttributeConfig {
4748
type?: DynamoDBTypes
4849
default?: any | ((data: object) => any)
4950
dependsOn?: string | string[]
5051
transform?: (value: any, data: {}) => any
52+
inverseTransform?: (value: any, data: {}) => any
5153
coerce?: boolean
5254
save?: boolean
5355
onUpdate?: boolean
@@ -139,6 +141,7 @@ class Entity<
139141
public defaults: any
140142
public linked: any
141143
public required: any
144+
public typeHidden!: boolean
142145

143146
// Declare constructor (entity config)
144147
constructor(entity: EntityConstructor) {
@@ -172,7 +175,7 @@ class Entity<
172175

173176
// If an entity tracking field is enabled, add the attributes, alias and the default
174177
if (table.Table.entityField) {
175-
this.schema.attributes[table.Table.entityField] = { type: 'string', alias: this._etAlias, default: this.name } as EntityAttributeConfig
178+
this.schema.attributes[table.Table.entityField] = { type: 'string', hidden: this.typeHidden, alias: this._etAlias, default: this.name } as EntityAttributeConfig
176179
this.defaults[table.Table.entityField] = this.name
177180
this.schema.attributes[this._etAlias] = { type: 'string', map: table.Table.entityField, default: this.name } as EntityAttributeConfig
178181
this.defaults[this._etAlias] = this.name

src/classes/Table.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class Table {
191191
// Validate and sets the document client (extend with options.convertEmptyValues because it's not typed)
192192
set DocumentClient(docClient: (DocumentClient & { options?: { convertEmptyValues: boolean}}) | undefined) {
193193
// If a valid document client
194-
if (docClient && docClient.get && docClient.put && docClient.delete && docClient.update) {
194+
if (docClient) {
195195
// Automatically set convertEmptyValues to true, unless false
196196
if (docClient.options!.convertEmptyValues !== false)
197197
docClient.options!.convertEmptyValues = true

src/lib/formatItem.ts

+25-8
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,32 @@ export default (DocumentClient: DocumentClient) => (attributes: { [key:string] :
3737

3838
if ((attributes[field] && attributes[field].hidden) || (include.length > 0 && !include.includes(field))) return acc
3939
// Extract values from sets
40-
if (attributes[field] && attributes[field].type === 'set' && Array.isArray(item[field].values)) { item[field] = item[field].values }
41-
return Object.assign(acc,{
42-
[(attributes[field] && attributes[field].alias) || field]: (
43-
attributes[field] && (attributes[field].prefix || attributes[field].suffix)
40+
if (attributes[field] && attributes[field].type === 'set' && Array.isArray(item[field].values)) { item[field] = item[field].values }
41+
42+
const fieldValue =
43+
attributes[field] &&
44+
(attributes[field].prefix || attributes[field].suffix)
4445
? item[field]
45-
.replace(new RegExp(`^${escapeRegExp(attributes[field].prefix!)}`),'')
46-
.replace(new RegExp(`${escapeRegExp(attributes[field].suffix!)}$`),'')
47-
: item[field]
48-
)
46+
.replace(
47+
new RegExp(`^${escapeRegExp(attributes[field].prefix!)}`),
48+
""
49+
)
50+
.replace(
51+
new RegExp(`${escapeRegExp(attributes[field].suffix!)}$`),
52+
""
53+
)
54+
: item[field];
55+
56+
const transformedValue =
57+
attributes[field] && attributes[field].inverseTransform
58+
? (
59+
attributes[field] as Required<EntityAttributeConfig>
60+
).inverseTransform(fieldValue, item)
61+
: fieldValue
62+
63+
return Object.assign(acc, {
64+
[(attributes[field] && attributes[field].alias) || field]:
65+
transformedValue,
4966
})
5067
},{})
5168
}

src/lib/parseEntity.ts

+5
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export function parseEntity(entity: EntityConstructor) {
4141
modified,
4242
modifiedAlias,
4343
typeAlias,
44+
typeHidden,
4445
attributes,
4546
autoExecute,
4647
autoParse,
@@ -87,6 +88,9 @@ export function parseEntity(entity: EntityConstructor) {
8788
&& typeAlias.trim().length > 0 ? typeAlias.trim()
8889
: 'entity'
8990

91+
// Define 'typeHidden'
92+
typeHidden = typeof typeHidden === "boolean" ? typeHidden : false;
93+
9094
// Sanity check the attributes
9195
attributes = typeof attributes === 'object' && !Array.isArray(attributes) ?
9296
attributes : error(`Please provide a valid 'attributes' object`)
@@ -115,6 +119,7 @@ export function parseEntity(entity: EntityConstructor) {
115119
linked: track.linked,
116120
autoExecute,
117121
autoParse,
122+
typeHidden,
118123
_etAlias: typeAlias
119124
},
120125
table ? { table } : {}

src/lib/parseMapping.ts

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export default (field: string, config: EntityAttributeConfig, track: TrackingInf
2525
case 'transform':
2626
if (typeof config[prop] !== 'function') error(`'${prop}' must be a function`)
2727
break
28+
case "inverseTransform":
29+
if (typeof config[prop] !== "function")
30+
error(`'${prop}' must be a function`);
31+
break;
2832
case 'coerce':
2933
case 'onUpdate':
3034
case 'hidden':

0 commit comments

Comments
 (0)