Skip to content

Commit 2aefd0e

Browse files
test(mappers): implement unit tests for all mappers
1 parent 6338668 commit 2aefd0e

24 files changed

+665
-72
lines changed

README.md

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -78,22 +78,9 @@ present on a property)
7878

7979
Generic information is never available due to some serialization limitations at the time of writing.
8080

81-
## A word on Collections (Array & Set)
82-
83-
###Array
84-
Javascript Arrays with a a items of type String, Number or Binary will be mapped to a S(et) type, by default all other types are mapped to L(ist) type.
85-
If an item of an Array has a complex type the type can be defined using the @TypedArray() Decorator.
86-
87-
###Set
88-
ES6 Set types will be marshalled to dynamoDb set type if the type of the set is supported, if the type is not supported it will be
89-
marshalled to an dynamoDB List.
90-
91-
When one of the following decorators is added, the value is marshalled to a List type.
92-
@SortedSet(itemType?: ModelClazz), @TypedSet(itemType?: ModelClazz)
93-
9481
## Model
9582

96-
###Custom TableName
83+
### Custom TableName
9784
Here is the rule how a table name is built `${kebabCase(modelName)}s` so for a model called Product the table will be named products, this is a default implementation.
9885

9986
There are two possibilities to change the name:
@@ -121,17 +108,51 @@ Complex Types (properties with these types need some decorators to work properly
121108
- Map
122109
- Array<complexType>
123110

111+
| TS Type | Dynamo Type |
112+
| ------------- |:-------------:|
113+
| String | S |
114+
| Number | N |
115+
| Boolean | BOOL |
116+
| moment.Moment | S (ISO-8601 formatted) |
117+
| null | NULL |
118+
| Array | L, (S,N,B)S |
119+
| ES6 Set | L, (S,N,B)S |
120+
| Object | M |
121+
|----|------|
122+
| Binary | Not Supported |
123+
| ES6 Map | Not Supported |
124+
| Date | Not Supported |
125+
126+
## Custom Attribute Mapping
127+
It is always possible to define a custom mapping strategy, just implement the [MapperForType](https://shiftcode.github.io/dynamo-easy/interfaces/_mapper_for_type_base_mapper_.mapperfortype.html) class.
128+
129+
## Collection Mapping (Array & Set)
130+
131+
### Array
132+
Javascript Arrays with a items of type String, Number or Binary will be mapped to a S(et) type, by default all other types are mapped to L(ist) type.
133+
If the items have a complex type it will be mapped to a L(ist).
134+
135+
### Set
136+
An instance of ES6 Set type will be mapped to a S(et) type if the type of the items is supported (String, Number, Binary), otherwise it is mapped to a L(ist).
137+
138+
When one of the following decorators is present, the value is always mapped to a L(ist).
139+
140+
- @SortedSet(itemType?: ModelClazz) - only L(ist) type preserves order
141+
- @TypedSet(itemType?: ModelClazz) - if the itemType is not one of String | Number | Binary
142+
- @TypedArray()
143+
124144
## Date
125-
Right now we only support (MomentJS)[http://momentjs.com/] Dates.
145+
Right now we only support [MomentJS](http://momentjs.com/) Dates.
126146

127147
If you want to explicitly mark a property to be a Date use the @Date() decorator. If we find a moment value we automatically map it to a String (using ISO-8601 format).
128148
When coming from db we do a regex test for ISO-8601 format and map it back to a moment object.
129149

130-
# Requests API
131-
To start making requests create an instance of [DynamoStore](https://shiftcode.github.io/dynamo-easy/classes/_dynamo_dynamo_store_.dynamostore.html) and execute the desired operation using the provided api.
132-
We support all the common dynamodb operations.
150+
## Enum
151+
Enum values are persisted as Numbers (index of enum).
133152

134-
The request api has support for the following operations:
153+
# Request API
154+
To start making requests create an instance of [DynamoStore](https://shiftcode.github.io/dynamo-easy/classes/_dynamo_dynamo_store_.dynamostore.html) and execute the desired operation using the provided api.
155+
We support all the common dynamodb operations:
135156

136157
- Put
137158
- Get
@@ -141,9 +162,6 @@ The request api has support for the following operations:
141162
- Query
142163
- MakeRequest (generic low level method for special scenarios)
143164

144-
For most of the api there is probably no explanation required, here are some topics we think
145-
need some more info.
146-
147165
There is always the possibility to access the Params object directly to add values which are not covered with our api.
148166

149167
# Authentication
@@ -154,8 +172,8 @@ The default implementation is a no-op function.
154172
## Session Validity Ensurer
155173
Here is an example of an implementation using amazon cognito
156174

157-
```
158-
function sessionValidityEnsurer(): Observable<>{
175+
```javascript
176+
function sessionValidityEnsurer(): Observable<boolean> {
159177
return Observable.of(this.isLoggedIn())
160178
.switchMap(isLoggedIn => {
161179
if (isLoggedIn) {
@@ -226,18 +244,17 @@ which publishes our code automatically on github and npm, plus generates automat
226244
We use 2 git hooks:
227245

228246
`precommit`
229-
- to format the code with Prettier :nail_care: before sending it to the git repo.
247+
- to format the code with Prettier :nail_care:
230248
- to check if the commit message follows a [conventional commit message](https://github.com/conventional-changelog/conventional-changelog)
231249

232-
233250
`prepush`
234-
- if the code can be built running npm run build
235-
- if all the tests pass
251+
- to check if the code can be built running `npm run build`
252+
- to check if all tests pass
236253

237254
## Credits
238-
[https://github.com/alexjoverm/typescript-library-starter](https://github.com/alexjoverm/typescript-library-starter) For the awesome project which helps to scaffold, develop and build a typescript library project
239-
[https://github.com/ryanfitz/vogels](https://github.com/ryanfitz/vogels) - To get an idea on how to build the chainable api
240-
[http://densebrain.github.io/typestore/](http://densebrain.github.io/typestore/) - Thats where the base idea on how to implement the model decorators came came from
255+
- [https://github.com/alexjoverm/typescript-library-starter](https://github.com/alexjoverm/typescript-library-starter) For the awesome project which helps to scaffold, develop and build a typescript library project
256+
- [https://github.com/ryanfitz/vogels](https://github.com/ryanfitz/vogels) - To get an idea on how to build the chainable api
257+
- [http://densebrain.github.io/typestore/](http://densebrain.github.io/typestore/) - Thats where the base idea on how to implement the model decorators came came from
241258

242259
## Contributors
243260
Made with :heart: by [@michaelwittwer](https://github.com/michaelwittwer) and all these wonderful contributors ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,12 @@
8787
}
8888
},
8989
"devDependencies": {
90-
"aws-sdk": "^2.100.0",
91-
"lodash": "^4.17.4",
92-
"moment": "^2.18.1",
93-
"rxjs": "^5.4.3",
9490
"@types/debug": "^0.0.30",
9591
"@types/jest": "^21.1.2",
9692
"@types/lodash": "^4.14.74",
9793
"@types/node": "^8.0.26",
9894
"@types/uuid": "^3.4.1",
95+
"aws-sdk": "^2.100.0",
9996
"colors": "^1.1.2",
10097
"commitizen": "^2.9.6",
10198
"coveralls": "^3.0.0",
@@ -104,7 +101,9 @@
104101
"husky": "^0.14.0",
105102
"jest": "^21.2.1",
106103
"lint-staged": "^4.0.4",
104+
"lodash": "^4.17.4",
107105
"lodash.camelcase": "^4.3.0",
106+
"moment": "^2.18.1",
108107
"prettier": "^1.6.1",
109108
"prompt": "^1.0.0",
110109
"replace-in-file": "^2.6.3",
@@ -115,6 +114,7 @@
115114
"rollup-plugin-node-builtins": "^2.1.2",
116115
"rollup-plugin-node-resolve": "^3.0.0",
117116
"rollup-plugin-sourcemaps": "^0.4.2",
117+
"rxjs": "^5.4.3",
118118
"semantic-release": "^8.0.3",
119119
"ts-jest": "^21.0.1",
120120
"ts-loader": "^2.3.4",

src/decorator/impl/collection/typed-array.decorator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { TypeInfo } from '../../metadata/property-metadata.model'
33
import { AttributeModelTypes, initOrUpdateProperty, KEY_PROPERTY } from '../property/property.decorator'
44

55
/**
6-
* Makes sure the property will be marshalled to a L(ist) type. The modelClass is required if the collection items
6+
* Makes sure the property will be mapped to a L(ist) type. The modelClass is required if the collection items
77
* have some property decorators, so we can retrieve this information using the model class.
88
*/
99
export function TypedArray<T>(modelClass?: ModelConstructor<T>): PropertyDecorator {

src/decorator/impl/enum/enum.decorator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { EnumType } from '../../../mapper/type/enum.type'
22
import { initOrUpdateProperty } from '../property/property.decorator'
33

4-
export function Enum(): PropertyDecorator {
4+
export function Enum<T>(enumType?: T): PropertyDecorator {
55
return (target: any, propertyKey: string) => {
66
initOrUpdateProperty({ typeInfo: { type: EnumType, isCustom: true } }, target, propertyKey)
77
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { PropertyMetadata } from '../../decorator/metadata/property-metadata.model'
2+
import { BooleanMapper } from './boolean.mapper'
3+
import { EnumMapper } from './enum.mapper'
4+
5+
describe('boolean mapper', () => {
6+
let mapper: BooleanMapper
7+
8+
beforeEach(() => {
9+
mapper = new BooleanMapper()
10+
})
11+
12+
describe('to db', () => {
13+
it('should work (true)', () => {
14+
const attributeValue = mapper.toDb(true)
15+
expect(attributeValue).toEqual({ BOOL: true })
16+
})
17+
18+
it('should work (false)', () => {
19+
const attributeValue = mapper.toDb(false)
20+
expect(attributeValue).toEqual({ BOOL: false })
21+
})
22+
23+
it('should throw (string is not a valid boolean value)', () => {
24+
expect(() => {
25+
mapper.toDb(<any>'true')
26+
}).toThrowError()
27+
})
28+
29+
it('should throw (string is not a valid boolean value)', () => {
30+
expect(() => {
31+
mapper.toDb(<any>1)
32+
}).toThrowError()
33+
})
34+
})
35+
36+
describe('from db', () => {
37+
it('should work (true)', () => {
38+
const enumValue = mapper.fromDb({ BOOL: true })
39+
expect(enumValue).toBe(true)
40+
})
41+
42+
it('should work (false)', () => {
43+
const enumValue = mapper.fromDb({ BOOL: false })
44+
expect(enumValue).toBe(false)
45+
})
46+
47+
it('should throw (S cannot be mapped to boolean)', () => {
48+
expect(() => {
49+
mapper.fromDb({ S: 'true' })
50+
}).toThrowError()
51+
})
52+
53+
it('should throw (N cannot be mapped to boolean)', () => {
54+
expect(() => {
55+
mapper.fromDb({ N: '1' })
56+
}).toThrowError()
57+
})
58+
})
59+
})

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@ import { MapperForType } from './base.mapper'
33

44
export class BooleanMapper implements MapperForType<boolean> {
55
fromDb(dbValue: any): boolean {
6+
if (dbValue.BOOL === undefined) {
7+
throw new Error('only attribute values with BOOL value can be mapped to a boolean')
8+
}
9+
610
return dbValue.BOOL === true
711
}
812

913
toDb(modelValue: boolean): AttributeValue {
14+
if (!(modelValue === true || modelValue === false)) {
15+
throw new Error('only boolean values are mapped to a BOOl attribute')
16+
}
17+
1018
return { BOOL: modelValue }
1119
}
1220
}

0 commit comments

Comments
 (0)