Skip to content

Commit ac4e476

Browse files
Merge pull request #326 from shiftcode/next
Next
2 parents 29134f9 + 8860141 commit ac4e476

21 files changed

+116
-78
lines changed

.releaserc.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1-
# run semantic-release on master branch
2-
branch: master
1+
branches:
2+
- name: master
3+
- name: next
4+
channel: next
5+
prerelease: next

package-lock.json

-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-5
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
"@commitlint/prompt-cli": "^8.1.0",
6060
"@types/jest": "^25.2.1",
6161
"@types/node": "8.10.40",
62-
"@types/uuid": "^3.4.5",
6362
"aws-sdk": "^2.401.0",
6463
"colors": "^1.3.3",
6564
"coveralls": "^3.0.6",
@@ -79,13 +78,11 @@
7978
"tsutils": "^3.17.1",
8079
"typedoc": "0.14.0",
8180
"typedoc-plugin-external-module-name": "^2.1.0",
82-
"typescript": ">=2.9.1",
83-
"uuid": "^3.3.2"
81+
"typescript": ">=2.9.1"
8482
},
8583
"peerDependencies": {
8684
"aws-sdk": "^2.401.0",
8785
"reflect-metadata": "^0.1.12",
88-
"tslib": "^1.10.0",
89-
"uuid": "^3.3.2"
86+
"tslib": "^1.10.0"
9087
}
9188
}

src/decorator/decorators.spec.ts

-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ describe('Decorators should add correct metadata', () => {
117117
expect(prop!.nameDb).toBe('id')
118118
expect(prop!.key).toBeDefined()
119119
expect(prop!.key!.type).toBe('HASH')
120-
expect(prop!.key!.uuid).toBeFalsy()
121120
expect(prop!.transient).toBeFalsy()
122121
expect(prop!.typeInfo).toBeDefined()
123122
expect(prop!.typeInfo!.type).toBe(String)
@@ -130,7 +129,6 @@ describe('Decorators should add correct metadata', () => {
130129
expect(prop!.nameDb).toBe('creationDate')
131130
expect(prop!.key).toBeDefined()
132131
expect(prop!.key!.type).toBe('RANGE')
133-
expect(prop!.key!.uuid).toBeFalsy()
134132
expect(prop!.transient).toBeFalsy()
135133
expect(prop!.typeInfo).toBeDefined()
136134
})

src/decorator/impl/key/partition-key-uuid.decorator.ts

-12
This file was deleted.

src/decorator/impl/property/property-data.model.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import { MapperForType } from '../../../mapper/for-type/base.mapper'
66
/**
77
* Option interface for @Property decorator
88
*/
9-
export interface PropertyData {
10-
// the name of property how it is named in dynamoDB
9+
export interface PropertyData<T> {
10+
/**
11+
* the name of property how it is named in dynamoDB
12+
*/
1113
name: string
12-
mapper: MapperForType<any, any>
14+
mapper: MapperForType<T, any>
15+
defaultValueProvider: () => T
1316
}

src/decorator/impl/property/property.decorator.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import { PropertyMetadata } from '../../metadata/property-metadata.model'
55
import { initOrUpdateProperty } from './init-or-update-property.function'
66
import { PropertyData } from './property-data.model'
77

8-
export function Property(opts: Partial<PropertyData> = {}): PropertyDecorator {
8+
export function Property<T>(opts: Partial<PropertyData<T>> = {}): PropertyDecorator {
99
return (target: object, propertyKey: string | symbol) => {
1010
if (typeof propertyKey === 'string') {
1111
const propertyOptions: Partial<PropertyMetadata<any>> = {
1212
name: propertyKey,
1313
nameDb: opts.name || propertyKey,
14+
defaultValueProvider: opts.defaultValueProvider,
1415
}
1516

1617
if ('mapper' in opts && !!opts.mapper) {

src/decorator/impl/public-api.ts

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export * from './index/lsi-sort-key.decorator'
1414
export * from './index/index-type.enum'
1515
// key
1616
export * from './key/partition-key.decorator'
17-
export * from './key/partition-key-uuid.decorator'
1817
export * from './key/sort-key.decorator'
1918

2019
// model

src/decorator/metadata/metadata.spec.ts

+10-9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
INDEX_ACTIVE_CREATED_AT,
77
INDEX_COUNT,
88
ModelWithABunchOfIndexes,
9-
ModelWithAutogeneratedId,
9+
ModelWithDefaultValue,
1010
ModelWithGSI,
1111
ModelWithLSI,
1212
ModelWithoutPartitionKeyModel,
@@ -22,7 +22,7 @@ describe('metadata', () => {
2222
let metaDataLsi: Metadata<ModelWithLSI>
2323
let metaDataGsi: Metadata<ModelWithGSI>
2424
let metaDataIndexes: Metadata<ModelWithABunchOfIndexes>
25-
let metaDataUuid: Metadata<ModelWithAutogeneratedId>
25+
let metaDataDefaultValue: Metadata<ModelWithDefaultValue>
2626
let metaDataComplex: Metadata<ComplexModel>
2727

2828
beforeEach(() => {
@@ -32,7 +32,7 @@ describe('metadata', () => {
3232
metaDataLsi = new Metadata(ModelWithLSI)
3333
metaDataGsi = new Metadata(ModelWithGSI)
3434
metaDataIndexes = new Metadata(ModelWithABunchOfIndexes)
35-
metaDataUuid = new Metadata(ModelWithAutogeneratedId)
35+
metaDataDefaultValue = new Metadata(ModelWithDefaultValue)
3636
metaDataComplex = new Metadata(ComplexModel)
3737
})
3838

@@ -61,12 +61,13 @@ describe('metadata', () => {
6161
expect(nestedObjDateMeta).toBeUndefined()
6262
})
6363

64-
it('getKeysWithUUID', () => {
65-
const uuid = metaDataUuid.getKeysWithUUID()
66-
expect(uuid.length).toBe(1)
67-
expect(uuid[0].key).toBeDefined()
68-
expect(uuid[0].key!.uuid).toBeTruthy()
69-
expect(uuid[0].name).toBe('id')
64+
it('getPropertiesWithDefaultValueProvider', () => {
65+
const props = metaDataDefaultValue.getPropertiesWithDefaultValueProvider()
66+
expect(props.length).toBe(1)
67+
expect(props[0].key).toBeDefined()
68+
expect(props[0].name).toBe('id')
69+
expect(props[0].defaultValueProvider).toBeDefined()
70+
expect(props[0].defaultValueProvider!()).toBeDefined()
7071
})
7172

7273
it('getPartitionKey', () => {

src/decorator/metadata/metadata.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ export class Metadata<T> {
7070

7171
/**
7272
*
73-
* @returns {Array<PropertyMetadata<any>>} Returns all the properties property the @PartitionKeyUUID decorator is present, returns an empty array by default
73+
* @returns {Array<PropertyMetadata<any>>} Returns all the properties a defaultValueProvider, returns an empty array by default
7474
*/
75-
getKeysWithUUID(): Array<PropertyMetadata<any>> {
76-
return filterBy(this.modelOptions, (p) => !!(p.key && p.key.uuid), [])
75+
getPropertiesWithDefaultValueProvider(): Array<PropertyMetadata<any>> {
76+
return filterBy(this.modelOptions, p => !!p.defaultValueProvider, [])
7777
}
7878

7979
/**

src/decorator/metadata/property-metadata.model.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ export interface TypeInfo {
1313

1414
export interface Key {
1515
type: DynamoDB.KeyType
16-
uuid?: boolean
1716
}
1817

1918
export interface PropertyMetadata<T, R extends Attribute = Attribute> {
@@ -49,6 +48,8 @@ export interface PropertyMetadata<T, R extends Attribute = Attribute> {
4948

5049
// index?: IModelAttributeIndex
5150
transient?: boolean
51+
52+
defaultValueProvider?: () => any
5253
}
5354

5455
/**

src/dynamo/expression/logical-operator/attribute.function.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
* @Model()
1717
* class Person{
1818
*
19-
* @PartitionKeyUUID()
19+
* @PartitionKey()
2020
* id: string
2121
* age: number
2222
* }

src/dynamo/expression/logical-operator/update.function.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
* @Model()
1616
* class Person {
1717
*
18-
* @PartitionKeyUUID()
18+
* @PartitionKey()
1919
* id: string
2020
* age: number
2121
* }

src/mapper/for-type/string.mapper.spec.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe('string mapper', () => {
99

1010
it('should work (empty string)', () => {
1111
const attributeValue = StringMapper.toDb('')
12-
expect(attributeValue).toBe(null)
12+
expect(attributeValue).toStrictEqual({ S: '' })
1313
})
1414

1515
it('should work (null)', () => {
@@ -28,6 +28,10 @@ describe('string mapper', () => {
2828
const stringValue = StringMapper.fromDb({ S: 'myStringValue' })
2929
expect(stringValue).toBe('myStringValue')
3030
})
31+
it('should allow empty string values', () => {
32+
const stringValue = StringMapper.fromDb({ S: '' })
33+
expect(stringValue).toBe('')
34+
})
3135
it('should throw if not a string attribute', () => {
3236
expect(() => StringMapper.fromDb(<any>{ N: '8' })).toThrow()
3337
})

src/mapper/for-type/string.mapper.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ import { StringAttribute } from '../type/attribute.type'
55
import { MapperForType } from './base.mapper'
66

77
function stringFromDb(attributeValue: StringAttribute): string {
8-
if (attributeValue.S) {
8+
if (attributeValue.S || attributeValue.S === '') {
99
return attributeValue.S
1010
} else {
1111
throw new Error(`there is no S(tring) value defined on given attribute value: ${JSON.stringify(attributeValue)}`)
1212
}
1313
}
1414

1515
function stringToDb(modelValue: string): StringAttribute | null {
16-
// an empty string is not a valid value for string attribute
17-
if (modelValue === '' || modelValue === null || modelValue === undefined) {
16+
// an empty string is valid for a string attribute
17+
if (modelValue === null || modelValue === undefined) {
1818
return null
1919
} else {
2020
return { S: modelValue }

src/mapper/mapper.spec.ts

+20-11
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
Employee,
1515
Gift,
1616
Id,
17-
ModelWithAutogeneratedId,
17+
ModelWithDefaultValue,
1818
ModelWithCustomMapperModel,
1919
ModelWithDateAsHashKey,
2020
ModelWithDateAsIndexHashKey,
@@ -32,6 +32,8 @@ import {
3232
SimpleWithRenamedPartitionKeyModel,
3333
StringType,
3434
Type,
35+
ModelWithCustomMapperAndDefaultValue,
36+
MyProp,
3537
} from '../../test/models'
3638
import { IdMapper } from '../../test/models/model-with-custom-mapper.model'
3739
import { ModelWithEmptyValues } from '../../test/models/model-with-empty-values'
@@ -67,7 +69,7 @@ describe('Mapper', () => {
6769

6870
it('string (empty)', () => {
6971
const attrValue = <StringAttribute>toDbOne('')!
70-
expect(attrValue).toBeNull()
72+
expect(attrValue.S).toStrictEqual('')
7173
})
7274

7375
it('number', () => {
@@ -684,15 +686,21 @@ describe('Mapper', () => {
684686
})
685687
})
686688

687-
describe('model with autogenerated id', () => {
688-
it('should create an uuid', () => {
689-
const toDbVal = toDb(new ModelWithAutogeneratedId(), ModelWithAutogeneratedId)
689+
describe('model with autogenerated value for id', () => {
690+
it('should create an id', () => {
691+
const toDbVal = toDb(new ModelWithDefaultValue(), ModelWithDefaultValue)
690692
expect(toDbVal.id).toBeDefined()
691693
expect(keyOf(toDbVal.id)).toBe('S')
692-
// https://stackoverflow.com/questions/7905929/how-to-test-valid-uuid-guid
693-
expect((<StringAttribute>toDbVal.id).S).toMatch(
694-
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
695-
)
694+
expect((<StringAttribute>toDbVal.id).S).toMatch(/^generated-id-\d{1,3}$/)
695+
})
696+
})
697+
698+
describe('model with default value provider AND custom mapper', () => {
699+
it('should create correct value', () => {
700+
const toDbVal = toDb(new ModelWithCustomMapperAndDefaultValue(), ModelWithCustomMapperAndDefaultValue)
701+
expect(toDbVal.myProp).toBeDefined()
702+
expect(keyOf(toDbVal.myProp)).toBe('S')
703+
expect((<StringAttribute>toDbVal.myProp).S).toBe(MyProp.default().toString())
696704
})
697705
})
698706

@@ -801,7 +809,7 @@ describe('Mapper', () => {
801809
// OK
802810
id: 'myId',
803811

804-
// x -> empty strings are not valid
812+
// x -> empty strings are valid
805813
name: '',
806814

807815
// x -> empty set is not valid
@@ -824,7 +832,8 @@ describe('Mapper', () => {
824832
expect(toDbValue.id).toBeDefined()
825833
expect(keyOf(toDbValue.id)).toBe('S')
826834

827-
expect(toDbValue.name).toBeUndefined()
835+
expect(toDbValue.name).toBeDefined()
836+
expect(keyOf(toDbValue.name)).toBe('S')
828837

829838
expect(toDbValue.roles).toBeUndefined()
830839

src/mapper/mapper.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/**
22
* @module mapper
33
*/
4-
import { v4 as uuidv4 } from 'uuid'
54
import { hasSortKey, Metadata } from '../decorator/metadata/metadata'
65
import { metadataForModel } from '../decorator/metadata/metadata-for-model.function'
76
import { hasType, Key, PropertyMetadata } from '../decorator/metadata/property-metadata.model'
@@ -42,12 +41,14 @@ export function toDb<T>(item: T, modelConstructor?: ModelConstructor<T>): Attrib
4241
const metadata: Metadata<T> = metadataForModel(modelConstructor)
4342

4443
/*
45-
* initialize possible properties with auto generated uuid
44+
* initialize possible properties with default value providers
4645
*/
4746
if (metadata) {
48-
metadata.getKeysWithUUID().forEach((propertyMetadata) => {
49-
if (!Reflect.get(<any>item, propertyMetadata.name)) {
50-
Reflect.set(<any>item, propertyMetadata.name, uuidv4())
47+
metadata.getPropertiesWithDefaultValueProvider().forEach(propertyMetadata => {
48+
const currentVal = Reflect.get(<any>item, propertyMetadata.name)
49+
if (currentVal === undefined || currentVal === null) {
50+
// tslint:disable-next-line:no-non-null-assertion
51+
Reflect.set(<any>item, propertyMetadata.name, propertyMetadata.defaultValueProvider!())
5152
}
5253
})
5354
}

test/models/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
export * from './complex.model'
22
export * from './custom-table-name.model'
33
export * from './employee.model'
4-
export * from './model-with-autogenerated-id.model'
4+
export * from './model-with-default-value.model'
55
export * from './model-with-custom-mapper.model'
66
export * from './model-with-custom-mapper-for-sort-key.model'
7+
export * from './model-with-custom-mapper-and-default-value.model'
78
export * from './model-with-date.model'
89
export * from './model-with-enum.model'
910
export * from './model-with-indexes.model'

test/models/model-with-autogenerated-id.model.ts

-7
This file was deleted.

0 commit comments

Comments
 (0)