Skip to content

Commit

Permalink
Merge pull request #245 from shiftcode/#243-projection-expression
Browse files Browse the repository at this point in the history
#243 projection expression
  • Loading branch information
michaelwittwer authored Oct 11, 2019
2 parents 083f9b7 + 926c21b commit b079514
Show file tree
Hide file tree
Showing 33 changed files with 349 additions and 203 deletions.
41 changes: 30 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/dynamo/expression/request-expression-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,18 +151,18 @@ export function doAddCondition<T, R extends ConditionalParamsHost>(
/**
* @hidden
*/
export function addPartitionKeyCondition<R extends StandardRequest<any, any, any>>(
export function addPartitionKeyCondition<R extends StandardRequest<any, any, any, any>>(
keyName: keyof any,
keyValue: any,
request: R,
): R
export function addPartitionKeyCondition<T, R extends StandardRequest<T, any, any>>(
export function addPartitionKeyCondition<T, R extends StandardRequest<T, any, any, any>>(
keyName: keyof T,
keyValue: any,
request: R,
metadata: Metadata<T>,
): R
export function addPartitionKeyCondition<T, R extends StandardRequest<T, any, any>>(
export function addPartitionKeyCondition<T, R extends StandardRequest<T, any, any, any>>(
keyName: keyof T,
keyValue: any,
request: R,
Expand Down
2 changes: 1 addition & 1 deletion src/dynamo/request/base.request.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ModelConstructor } from '../../model/model-constructor'
import { BaseRequest } from './base.request'

describe('base request', () => {
class TestRequest<T> extends BaseRequest<T, any, BaseRequest<T, any, any>> {
class TestRequest<T> extends BaseRequest<T, T, any, BaseRequest<T, T, any, any>> {
constructor(modelClazz: ModelConstructor<T>) {
super(<any>null, modelClazz)
}
Expand Down
5 changes: 3 additions & 2 deletions src/dynamo/request/base.request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getTableName } from '../get-table-name.function'
*/
export abstract class BaseRequest<
T,
T2,
I extends
| DynamoDB.DeleteItemInput
| DynamoDB.GetItemInput
Expand All @@ -26,7 +27,7 @@ export abstract class BaseRequest<
| DynamoDB.BatchWriteItemInput
| DynamoDB.TransactGetItemsInput
| DynamoDB.TransactWriteItemsInput,
R extends BaseRequest<T, I, any>
R extends BaseRequest<T, T2, I, any>
> {
readonly dynamoDBWrapper: DynamoDbWrapper

Expand Down Expand Up @@ -86,5 +87,5 @@ export abstract class BaseRequest<
/**
* execute request and return the parsed item(s) or void if none were requested.
*/
abstract exec(): Promise<T | T[] | void | null>
abstract exec(): Promise<T2 | T2[] | void | null>
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,25 @@ describe('batch get', () => {
})
})

it('ConsistentRead', () => {
it('consistent read', () => {
const request = new BatchGetSingleTableRequest<any>(<any>null, SimpleWithPartitionKeyModel, [{ id: 'myId' }])
request.consistentRead()
expect(request.params.RequestItems).toBeDefined()
expect(request.params.RequestItems[getTableName(SimpleWithPartitionKeyModel)]).toBeDefined()
expect(request.params.RequestItems[getTableName(SimpleWithPartitionKeyModel)].ConsistentRead).toBeTruthy()
})

it('projection expression', () => {
const request = new BatchGetSingleTableRequest<any>(<any>null, SimpleWithPartitionKeyModel, [{ id: 'myId' }])
request.projectionExpression('age')
expect(request.params.RequestItems).toBeDefined()
const params = request.params.RequestItems[getTableName(SimpleWithPartitionKeyModel)]
expect(params).toBeDefined()
expect(params.ProjectionExpression).toBe('#age')
expect(params.ExpressionAttributeNames).toEqual({
'#age': 'age',
})
})
})

describe('should return appropriate value', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ import { batchGetItemsFetchAll } from '../../batchget/batch-get-utils'
import { BATCH_GET_DEFAULT_TIME_SLOT, BATCH_GET_MAX_REQUEST_ITEM_COUNT } from '../../batchget/batch-get.const'
import { DynamoDbWrapper } from '../../dynamo-db-wrapper'
import { BaseRequest } from '../base.request'
import { addProjectionExpressionParam } from '../helper/add-projection-expression-param.function'
import { BatchGetSingleTableResponse } from './batch-get-single-table.response'

/**
* Request class for BatchGetItem operation which supports a single model class only.
*/
export class BatchGetSingleTableRequest<T> extends BaseRequest<
export class BatchGetSingleTableRequest<T, T2 = T> extends BaseRequest<
T,
T2,
DynamoDB.BatchGetItemInput,
BatchGetSingleTableRequest<T>
BatchGetSingleTableRequest<T, T2>
> {
private readonly logger: Logger

Expand All @@ -39,11 +41,23 @@ export class BatchGetSingleTableRequest<T> extends BaseRequest<
}
}

consistentRead(value: boolean = true): BatchGetSingleTableRequest<T> {
/**
* Determines the read consistency model: If set to true, then the operation uses strongly consistent reads; otherwise, the operation uses eventually consistent reads.
*/
consistentRead(value: boolean = true): this {
this.params.RequestItems[this.tableName].ConsistentRead = value
return this
}

/**
* Specifies the list of model attributes to be returned from the table instead of returning the entire document
* @param attributesToGet List of model attributes to be returned
*/
projectionExpression(...attributesToGet: Array<keyof T | string>): BatchGetSingleTableRequest<T, Partial<T>> {
addProjectionExpressionParam(attributesToGet, this.params.RequestItems[this.tableName], this.metadata)
return this
}

/**
* fetch all entries and return the raw response (without parsing the attributes to js objects)
* @param backoffTimer when unprocessed keys are returned the next value of backoffTimer is used to determine how many time slots to wait before doing the next request
Expand All @@ -64,7 +78,7 @@ export class BatchGetSingleTableRequest<T> extends BaseRequest<
execFullResponse(
backoffTimer = randomExponentialBackoffTimer,
throttleTimeSlot = BATCH_GET_DEFAULT_TIME_SLOT,
): Promise<BatchGetSingleTableResponse<T>> {
): Promise<BatchGetSingleTableResponse<T2>> {
return this.fetch(backoffTimer, throttleTimeSlot)
.then(this.mapResponse)
.then(promiseTap(response => this.logger.debug('mapped items', response.Items)))
Expand All @@ -75,18 +89,18 @@ export class BatchGetSingleTableRequest<T> extends BaseRequest<
* @param backoffTimer when unprocessed keys are returned the next value of backoffTimer is used to determine how many time slots to wait before doing the next request
* @param throttleTimeSlot the duration of a time slot in ms
*/
exec(backoffTimer = randomExponentialBackoffTimer, throttleTimeSlot = BATCH_GET_DEFAULT_TIME_SLOT): Promise<T[]> {
exec(backoffTimer = randomExponentialBackoffTimer, throttleTimeSlot = BATCH_GET_DEFAULT_TIME_SLOT): Promise<T2[]> {
return this.fetch(backoffTimer, throttleTimeSlot)
.then(this.mapResponse)
.then(r => r.Items)
.then(promiseTap(items => this.logger.debug('mapped items', items)))
}

private mapResponse = (response: DynamoDB.BatchGetItemOutput) => {
let items: T[] = []
let items: T2[] = []
if (response.Responses && Object.keys(response.Responses).length && response.Responses[this.tableName]) {
const mapped: T[] = response.Responses[this.tableName].map(attributeMap =>
fromDb(<Attributes<T>>attributeMap, this.modelClazz),
const mapped: T2[] = response.Responses[this.tableName].map(attributeMap =>
fromDb(<Attributes<T2>>attributeMap, <any>this.modelClazz),
)
items = mapped
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as DynamoDB from 'aws-sdk/clients/dynamodb'
*/
export interface BatchGetSingleTableResponse<T> {
/**
* A map of table name to a list of items. Each object in Responses consists of a table name, along with a map of attribute data consisting of the data type and attribute value.
* A map of table name to a list of items. Each object in Responses consists of a table name, along with a map of attribute data consisting of the data type and attribute value, as specified by ProjectionExpression.
*/
Items: T[]
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import { BaseRequest } from '../base.request'
/**
* Request class for BatchWriteItem operation which supports a single model class only.
*/
export class BatchWriteSingleTableRequest<T> extends BaseRequest<
export class BatchWriteSingleTableRequest<T, T2 = T> extends BaseRequest<
T,
T2,
DynamoDB.BatchWriteItemInput,
BatchWriteSingleTableRequest<T>
BatchWriteSingleTableRequest<T, T2>
> {
private readonly logger: Logger
private toKey = createToKeyFn(this.modelClazz)
Expand All @@ -33,19 +34,20 @@ export class BatchWriteSingleTableRequest<T> extends BaseRequest<
/**
* return item collection metrics.
*/
returnItemCollectionMetrics(value: DynamoDB.ReturnItemCollectionMetrics) {
returnItemCollectionMetrics(value: DynamoDB.ReturnItemCollectionMetrics): this {
this.params.ReturnItemCollectionMetrics = value
return this
}

delete(items: Array<Partial<T>>): BatchWriteSingleTableRequest<T> {
delete(items: Array<Partial<T>>): this {
if (this.params.RequestItems[this.tableName].length + items.length > BATCH_WRITE_MAX_REQUEST_ITEM_COUNT) {
throw new Error(`batch write takes at max ${BATCH_WRITE_MAX_REQUEST_ITEM_COUNT} items`)
}
this.params.RequestItems[this.tableName].push(...items.map(this.createDeleteRequest))
return this
}

put(items: T[]): BatchWriteSingleTableRequest<T> {
put(items: T[]): this {
if (this.params.RequestItems[this.tableName].length + items.length > BATCH_WRITE_MAX_REQUEST_ITEM_COUNT) {
throw new Error(`batch write takes at max ${BATCH_WRITE_MAX_REQUEST_ITEM_COUNT} items`)
}
Expand Down
Binary file modified src/dynamo/request/class-diagram.monopic
Binary file not shown.
Loading

0 comments on commit b079514

Please sign in to comment.