Skip to content

Commit ea37f7a

Browse files
Merge pull request #171 from shiftcode/#162-write-request-exec
#162 write request exec
2 parents f9b8092 + ed8f118 commit ea37f7a

File tree

10 files changed

+106
-20
lines changed

10 files changed

+106
-20
lines changed

src/dynamo/batchget/batch-get.request.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ describe('batch get', () => {
3535
},
3636
])
3737
})
38+
39+
it('returnConsumedCapacity', () => {
40+
request.returnConsumedCapacity('TOTAL')
41+
expect(request.params.ReturnConsumedCapacity).toBe('TOTAL')
42+
})
3843
})
3944

4045
describe('forModel', () => {

src/dynamo/batchget/batch-get.request.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ export class BatchGetRequest {
2424
}
2525
}
2626

27+
returnConsumedCapacity(level: DynamoDB.ReturnConsumedCapacity): BatchGetRequest {
28+
this.params.ReturnConsumedCapacity = level
29+
return this
30+
}
31+
2732
/**
2833
* @param {ModelConstructor<T>} modelClazz
2934
* @param {Partial<T>[]} keys a partial of T that contains Partition key and SortKey (if necessary). Throws if missing.

src/dynamo/request/delete/delete.request.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ describe('delete request', () => {
3535
it('should throw for no sort key value', () => {
3636
expect(() => new DeleteRequest(<any>null, ComplexModel, 'myId')).toThrowError()
3737
})
38+
39+
it('returnValues', () => {
40+
const request = new DeleteRequest(<any>null, SimpleWithPartitionKeyModel, 'myId')
41+
const req = request.returnValues('ALL_OLD')
42+
expect(req.params.ReturnValues).toEqual('ALL_OLD')
43+
})
3844
})
3945

4046
describe('logger', () => {

src/dynamo/request/delete/delete.request.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,33 @@ import { promiseTap } from '../../../helper/promise-tap.function'
33
import { createLogger, Logger } from '../../../logger/logger'
44
import { createKeyAttributes } from '../../../mapper/mapper'
55
import { ModelConstructor } from '../../../model/model-constructor'
6+
import { Omit } from '../../../model/omit.type'
67
import { DynamoDbWrapper } from '../../dynamo-db-wrapper'
78
import { WriteRequest } from '../write.request'
89

10+
type DeleteRequestReturnT<T> = Omit<DeleteRequest<T>, 'exec'> & { exec(): Promise<T> }
11+
912
export class DeleteRequest<T> extends WriteRequest<T, DynamoDB.DeleteItemInput, DeleteRequest<T>> {
10-
private readonly logger: Logger
13+
protected readonly logger: Logger
1114

1215
constructor(dynamoDBWrapper: DynamoDbWrapper, modelClazz: ModelConstructor<T>, partitionKey: any, sortKey?: any) {
1316
super(dynamoDBWrapper, modelClazz)
1417
this.logger = createLogger('dynamo.request.DeleteRequest', modelClazz)
1518
this.params.Key = createKeyAttributes(this.metadata, partitionKey, sortKey)
1619
}
1720

21+
22+
returnValues(returnValues: 'ALL_OLD'): DeleteRequestReturnT<T>
23+
returnValues(returnValues: 'NONE'): DeleteRequest<T>
24+
returnValues(returnValues: 'ALL_OLD' | 'NONE'): DeleteRequest<T> | DeleteRequestReturnT<T> {
25+
this.params.ReturnValues = returnValues
26+
return this
27+
}
28+
1829
execFullResponse(): Promise<DynamoDB.DeleteItemOutput> {
1930
this.logger.debug('request', this.params)
2031
return this.dynamoDBWrapper.deleteItem(this.params)
2132
.then(promiseTap(response => this.logger.debug('response', response)))
2233
}
2334
}
35+

src/dynamo/request/put/put.request.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ describe('put request', () => {
2626
expect(params.ExpressionAttributeNames).toEqual({ '#id': 'id' })
2727
expect(params.ExpressionAttributeValues).toBeUndefined()
2828
})
29+
30+
it('returnValues', () => {
31+
const req = request.returnValues('ALL_OLD')
32+
expect(req.params.ReturnValues).toEqual('ALL_OLD')
33+
})
2934
})
3035

3136
describe('logger', () => {

src/dynamo/request/put/put.request.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ import { promiseTap } from '../../../helper/promise-tap.function'
33
import { createLogger, Logger } from '../../../logger/logger'
44
import { toDb } from '../../../mapper/mapper'
55
import { ModelConstructor } from '../../../model/model-constructor'
6+
import { Omit } from '../../../model/omit.type'
67
import { DynamoDbWrapper } from '../../dynamo-db-wrapper'
78
import { createIfNotExistsCondition } from '../../expression/create-if-not-exists-condition.function'
89
import { WriteRequest } from '../write.request'
910

11+
type PutRequestReturnT<T> = Omit<PutRequest<T>, 'exec'> & { exec(): Promise<DynamoDB.AttributeMap> }
12+
13+
1014
export class PutRequest<T> extends WriteRequest<T, DynamoDB.PutItemInput, PutRequest<T>> {
11-
private readonly logger: Logger
15+
protected readonly logger: Logger
1216

1317
constructor(dynamoDBWrapper: DynamoDbWrapper, modelClazz: ModelConstructor<T>, item: T) {
1418
super(dynamoDBWrapper, modelClazz)
@@ -27,6 +31,13 @@ export class PutRequest<T> extends WriteRequest<T, DynamoDB.PutItemInput, PutReq
2731
return this
2832
}
2933

34+
returnValues(returnValues: 'ALL_OLD'): PutRequestReturnT<T>
35+
returnValues(returnValues: 'NONE'): PutRequest<T>
36+
returnValues(returnValues: 'ALL_OLD' | 'NONE'): PutRequest<T> | PutRequestReturnT<T> {
37+
this.params.ReturnValues = returnValues
38+
return this
39+
}
40+
3041
execFullResponse(): Promise<DynamoDB.PutItemOutput> {
3142
this.logger.debug('request', this.params)
3243
return this.dynamoDBWrapper.putItem(this.params)

src/dynamo/request/update/update.request.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ describe('update request', () => {
4444
expect(() => new UpdateRequest(<any>null, SimpleWithCompositePartitionKeyModel, 'myId')).toThrow()
4545
})
4646

47+
it('returnValues', () => {
48+
const request = new UpdateRequest(<any>null, SimpleWithPartitionKeyModel, 'myId')
49+
const req = request.returnValues('UPDATED_OLD')
50+
expect(req.params.ReturnValues).toEqual('UPDATED_OLD')
51+
})
52+
4753
it('should add operations', () => {
4854
const now = new Date()
4955
const request = new UpdateRequest(<any>null, UpdateModel, 'myId')

src/dynamo/request/update/update.request.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@ import { promiseTap } from '../../../helper/promise-tap.function'
33
import { createLogger, Logger } from '../../../logger/logger'
44
import { createKeyAttributes } from '../../../mapper/mapper'
55
import { ModelConstructor } from '../../../model/model-constructor'
6+
import { Omit } from '../../../model/omit.type'
67
import { DynamoDbWrapper } from '../../dynamo-db-wrapper'
78
import { prepareAndAddUpdateExpressions } from '../../expression/prepare-and-add-update-expressions.function'
89
import { addUpdate } from '../../expression/request-expression-builder'
910
import { RequestUpdateFunction } from '../../expression/type/update-expression-definition-chain'
1011
import { UpdateExpressionDefinitionFunction } from '../../expression/type/update-expression-definition-function'
1112
import { WriteRequest } from '../write.request'
1213

14+
type UpdateRequestReturnT<T> = Omit<UpdateRequest<T>, 'exec'> & { exec(): Promise<T> }
15+
type UpdateRequestReturnPartialT<T> = Omit<UpdateRequest<T>, 'exec'> & { exec(): Promise<Partial<T>> }
16+
1317
export class UpdateRequest<T> extends WriteRequest<T, DynamoDB.UpdateItemInput, UpdateRequest<T>> {
14-
private readonly logger: Logger
18+
protected readonly logger: Logger
1519

1620
constructor(dynamoDBWrapper: DynamoDbWrapper, modelClazz: ModelConstructor<T>, partitionKey: any, sortKey?: any) {
1721
super(dynamoDBWrapper, modelClazz)
@@ -28,6 +32,14 @@ export class UpdateRequest<T> extends WriteRequest<T, DynamoDB.UpdateItemInput,
2832
return this
2933
}
3034

35+
returnValues(returnValues: 'ALL_OLD' | 'ALL_NEW'): UpdateRequestReturnT<T>
36+
returnValues(returnValues: 'UPDATED_OLD' | 'UPDATED_NEW'): UpdateRequestReturnPartialT<T>
37+
returnValues(returnValues: 'NONE'): UpdateRequest<T>
38+
returnValues(returnValues: 'ALL_OLD' | 'ALL_NEW' | 'UPDATED_OLD' | 'UPDATED_NEW' | 'NONE'): UpdateRequest<T> | UpdateRequestReturnT<T> | UpdateRequestReturnPartialT<T> {
39+
this.params.ReturnValues = returnValues
40+
return this
41+
}
42+
3143
execFullResponse(): Promise<DynamoDB.UpdateItemOutput> {
3244
this.logger.debug('request', this.params)
3345
return this.dynamoDBWrapper.updateItem(this.params)

src/dynamo/request/write.request.spec.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { SimpleWithPartitionKeyModel } from '../../../test/models'
2+
import { createLogger, Logger } from '../../logger/logger'
3+
import { Attributes } from '../../mapper/type/attribute.type'
24
import { ModelConstructor } from '../../model/model-constructor'
35
import { attribute } from '../expression/logical-operator/attribute.function'
46
import { or } from '../expression/logical-operator/public.api'
57
import { WriteRequest } from './write.request'
68

79
describe('write request', () => {
810
class TestWriteRequest<T> extends WriteRequest<T, any, TestWriteRequest<T>> {
11+
protected readonly logger: Logger
912
readonly params: any = {}
1013

1114
constructor(modelClazz: ModelConstructor<T>) {
1215
super(<any>null, modelClazz)
16+
this.logger = createLogger('dynamo.request.PutRequest', modelClazz)
1317
}
1418

1519
execFullResponse() {
@@ -19,7 +23,7 @@ describe('write request', () => {
1923

2024
let req: TestWriteRequest<SimpleWithPartitionKeyModel>
2125

22-
describe('exec', () => {
26+
describe('exec [ReturnValues = NONE]', () => {
2327
let execFullResponseSpy: jasmine.Spy
2428
beforeEach(() => {
2529
req = new TestWriteRequest(SimpleWithPartitionKeyModel)
@@ -37,6 +41,18 @@ describe('write request', () => {
3741
})
3842
})
3943

44+
describe('exec [ReturnValues = ALL_OLD]', () => {
45+
beforeEach(() => {
46+
req = new TestWriteRequest(SimpleWithPartitionKeyModel)
47+
const returnValues: Attributes<SimpleWithPartitionKeyModel> = { id: { S: 'myId' }, age: { N: '20' } }
48+
spyOn(req, 'execFullResponse').and.returnValue(Promise.resolve({ Attributes: returnValues }))
49+
})
50+
it('should map result if Attributes return values(s)', async () => {
51+
const result = await req.exec()
52+
expect(result).toEqual({ id: 'myId', age: 20 })
53+
})
54+
})
55+
4056
describe('params', () => {
4157
beforeEach(() => {
4258
req = new TestWriteRequest(SimpleWithPartitionKeyModel)
@@ -47,11 +63,6 @@ describe('write request', () => {
4763
expect(req.params.ReturnItemCollectionMetrics).toBe('SIZE')
4864
})
4965

50-
it('should set returnValues', () => {
51-
req.returnValues('ALL_OLD')
52-
expect(req.params.ReturnValues).toBe('ALL_OLD')
53-
})
54-
5566
it('[onlyIfAttribute] should set condition', () => {
5667
req.onlyIfAttribute('age').gt(20)
5768
expect(req.params.ConditionExpression).toEqual('#age > :age')

src/dynamo/request/write.request.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import * as DynamoDB from 'aws-sdk/clients/dynamodb'
2+
import { promiseTap } from '../../helper/promise-tap.function'
3+
import { Logger } from '../../logger/logger'
4+
import { fromDb } from '../../mapper/mapper'
5+
import { Attributes } from '../../mapper/type/attribute.type'
26
import { ModelConstructor } from '../../model/model-constructor'
37
import { DynamoDbWrapper } from '../dynamo-db-wrapper'
48
import { and } from '../expression/logical-operator/public.api'
@@ -14,6 +18,10 @@ import { StandardRequest } from './standard.request'
1418
export abstract class WriteRequest<T,
1519
I extends DynamoDB.DeleteItemInput | DynamoDB.PutItemInput | DynamoDB.UpdateItemInput,
1620
R extends WriteRequest<T, I, R>> extends StandardRequest<T, I, R> {
21+
22+
protected abstract readonly logger: Logger
23+
24+
1725
protected constructor(dynamoDBWrapper: DynamoDbWrapper, modelClazz: ModelConstructor<T>) {
1826
super(dynamoDBWrapper, modelClazz)
1927
}
@@ -28,7 +36,6 @@ export abstract class WriteRequest<T,
2836
}
2937

3038
/**
31-
* @param conditionDefFns
3239
* @example writeRequest.onlyIf( attribute('age').eq(23) )
3340
* @example writeRequest.onlyIf( or( attribute('age').lt(18), attribute('age').gt(65) ) )
3441
*/
@@ -38,17 +45,23 @@ export abstract class WriteRequest<T,
3845
return <any>this
3946
}
4047

41-
/*
42-
* The ReturnValues parameter is used by several DynamoDB operations; however,
43-
* DeleteItem/PutItem/UpdateItem does not recognize any values other than NONE or ALL_OLD.
48+
/**
49+
* @returns { void } if no ReturnValues are requested, { T } if the requested ReturnValues are ALL_OLD|ALL_NEW or {Partial<T>} if the requested ReturnValues are UPDATED_OLD|UPDATED_NEW
4450
*/
45-
returnValues(returnValues: 'NONE' | 'ALL_OLD'): R {
46-
this.params.ReturnValues = returnValues
47-
return <R>(<any>this)
48-
}
49-
50-
exec(): Promise<void> {
51+
exec(): Promise<T | undefined> {
5152
return this.execFullResponse()
52-
.then(response => {return})
53+
.then(response => {
54+
if ('Attributes' in response && typeof response.Attributes === 'object' && response.Attributes !== null) {
55+
return fromDb(<Attributes<T>>response.Attributes, this.modelClazz)
56+
}
57+
return
58+
})
59+
.then(promiseTap(item => {
60+
if (item) {
61+
this.logger.debug('mapped item', item)
62+
} else {
63+
this.logger.debug('no return values to map')
64+
}
65+
}))
5366
}
5467
}

0 commit comments

Comments
 (0)