Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Upgrade to aws-sdk v3 #75

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
"./src"
],
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.47.0",
"@aws-sdk/lib-dynamodb": "^3.47.0",
"@ginger.io/jay-z": "0.0.14",
"aws-sdk": "^2.853.0",
"aws-xray-sdk": "^3.2.0",
"fast-json-stable-stringify": "^2.1.0",
"js-yaml": "^3.13.1",
Expand Down
9 changes: 5 additions & 4 deletions src/main/dynamo/Beyonce.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"
import { JayZ } from "@ginger.io/jay-z"
import { DynamoDB } from "aws-sdk"
import { captureAWSClient } from "aws-xray-sdk"
import { UpdateItemExpressionBuilder } from "./expressions/UpdateItemExpressionBuilder"
import { groupModelsByType } from "./groupModelsByType"
Expand Down Expand Up @@ -41,12 +42,12 @@ export interface ScanOptions {
* does auto mapping between JSON <=> DynamoDB Items
*/
export class Beyonce {
private client: DynamoDB.DocumentClient
private client: DynamoDBDocumentClient
private jayz?: JayZ
private consistentReads: boolean

constructor(private table: Table<string, string>, dynamo: DynamoDB, options: Options = {}) {
this.client = new DynamoDB.DocumentClient({ service: dynamo })
constructor(private table: Table<string, string>, dynamo: DynamoDBClient, options: Options = {}) {
this.client = DynamoDBDocumentClient.from(dynamo, { marshallOptions: { removeUndefinedValues: true } })
if (options.xRayTracingEnabled) {
// hack per: https://github.com/aws/aws-xray-sdk-node/issues/23#issuecomment-509745488
captureAWSClient((this.client as any).service)
Expand Down
17 changes: 8 additions & 9 deletions src/main/dynamo/QueryBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { DynamoDBDocumentClient, QueryCommand, QueryCommandInput } from "@aws-sdk/lib-dynamodb"
import { JayZ } from "@ginger.io/jay-z"
import { DynamoDB } from "aws-sdk"
import { DocumentClient } from "aws-sdk/clients/dynamodb"
import { ready } from "libsodium-wrappers"
import { QueryExpressionBuilder } from "./expressions/QueryExpressionBuilder"
import { groupModelsByType } from "./groupModelsByType"
Expand All @@ -12,15 +11,15 @@ import { Table } from "./Table"
import { GroupedModels, TaggedModel } from "./types"

interface TableQueryConfig<T extends TaggedModel> {
db: DynamoDB.DocumentClient
db: DynamoDBDocumentClient
table: Table
key: PartitionKey<T> | PartitionKeyAndSortKeyPrefix<T>
jayz?: JayZ
consistentRead?: boolean
}

interface GSIQueryConfig<T extends TaggedModel> {
db: DynamoDB.DocumentClient
db: DynamoDBDocumentClient
table: Table
gsiName: string
gsiKey: PartitionKey<T>
Expand All @@ -44,14 +43,14 @@ export class QueryBuilder<T extends TaggedModel> extends QueryExpressionBuilder<

async exec(): Promise<GroupedModels<T>> {
const query = this.createQueryInput({ lastEvaluatedKey: undefined })
const iterator = pagedIterator<DocumentClient.QueryInput, T>(
const iterator = pagedIterator<QueryCommandInput, T>(
{ lastEvaluatedKey: undefined },
({ lastEvaluatedKey, pageSize }) => ({
...query,
ExclusiveStartKey: lastEvaluatedKey,
Limit: pageSize
}),
(query) => this.config.db.query(query).promise(),
(query) => this.config.db.send(new QueryCommand(query)),
this.config.jayz
)

Expand All @@ -61,14 +60,14 @@ export class QueryBuilder<T extends TaggedModel> extends QueryExpressionBuilder<
async *iterator(options: IteratorOptions = {}): PaginatedIteratorResults<T> {
const iteratorOptions = toInternalIteratorOptions(options)
const query = this.createQueryInput(iteratorOptions)
const iterator = pagedIterator<DocumentClient.QueryInput, T>(
const iterator = pagedIterator<QueryCommandInput, T>(
iteratorOptions,
({ lastEvaluatedKey, pageSize }) => ({
...query,
ExclusiveStartKey: lastEvaluatedKey,
Limit: pageSize
}),
(query) => this.config.db.query(query).promise(),
(query) => this.config.db.send(new QueryCommand(query)),
this.config.jayz
)

Expand All @@ -88,7 +87,7 @@ export class QueryBuilder<T extends TaggedModel> extends QueryExpressionBuilder<
}
}

private createQueryInput(options: InternalIteratorOptions): DynamoDB.DocumentClient.QueryInput {
private createQueryInput(options: InternalIteratorOptions): QueryCommandInput {
if (isTableQuery(this.config)) {
const { table, consistentRead } = this.config
const keyCondition = this.buildKeyConditionForTable(this.config)
Expand Down
13 changes: 6 additions & 7 deletions src/main/dynamo/ScanBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { DynamoDBDocumentClient, ScanCommand, ScanCommandInput } from "@aws-sdk/lib-dynamodb"
import { JayZ } from "@ginger.io/jay-z"
import { DynamoDB } from "aws-sdk"
import { DocumentClient } from "aws-sdk/clients/dynamodb"
import { ready } from "libsodium-wrappers"
import { QueryExpressionBuilder } from "./expressions/QueryExpressionBuilder"
import { groupModelsByType } from "./groupModelsByType"
Expand All @@ -11,7 +10,7 @@ import { Table } from "./Table"
import { GroupedModels, TaggedModel } from "./types"

interface ScanConfig<T extends TaggedModel> {
db: DynamoDB.DocumentClient
db: DynamoDBDocumentClient
table: Table
jayz?: JayZ
consistentRead?: boolean
Expand All @@ -33,14 +32,14 @@ export class ScanBuilder<T extends TaggedModel> extends QueryExpressionBuilder<T

async exec(): Promise<GroupedModels<T>> {
const scanInput = this.createScanInput()
const iterator = pagedIterator<DocumentClient.ScanInput, T>(
const iterator = pagedIterator<ScanCommandInput, T>(
{ lastEvaluatedKey: undefined },
({ lastEvaluatedKey, pageSize }) => ({
...scanInput,
ExclusiveStartKey: lastEvaluatedKey,
Limit: pageSize
}),
(input) => this.config.db.scan(input).promise(),
(input) => this.config.db.send(new ScanCommand(input)),
this.config.jayz
)

Expand All @@ -50,14 +49,14 @@ export class ScanBuilder<T extends TaggedModel> extends QueryExpressionBuilder<T
async *iterator(options: IteratorOptions = {}): PaginatedIteratorResults<T> {
const iteratorOptions = toInternalIteratorOptions(options)
const scanInput = this.createScanInput(iteratorOptions)
const iterator = pagedIterator<DocumentClient.ScanInput, T>(
const iterator = pagedIterator<ScanCommandInput, T>(
iteratorOptions,
({ lastEvaluatedKey, pageSize }) => ({
...scanInput,
ExclusiveStartKey: lastEvaluatedKey,
Limit: pageSize
}),
(input) => this.config.db.scan(input).promise(),
(input) => this.config.db.send(new ScanCommand(input)),
this.config.jayz
)

Expand Down
6 changes: 3 additions & 3 deletions src/main/dynamo/Table.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DynamoDB } from "aws-sdk"
import { AttributeDefinition, BillingMode, CreateTableCommandInput } from "@aws-sdk/client-dynamodb"
import { GSI, GSIBuilder } from "./GSI"
import { Model, PartitionKeyBuilder } from "./Model"
import { Partition } from "./Partition"
Expand Down Expand Up @@ -53,15 +53,15 @@ export class Table<PK extends string = string, SK extends string = string> {
return this.modelTags
}

asCreateTableInput(billingMode: DynamoDB.Types.BillingMode): DynamoDB.Types.CreateTableInput {
asCreateTableInput(billingMode: BillingMode): CreateTableCommandInput {
const attributeSet = new Set([
this.partitionKeyName,
this.sortKeyName,
"model",
...this.gsis.flatMap((_) => [_.partitionKeyName, _.sortKeyName])
])

const attributeDefinitions: DynamoDB.Types.AttributeDefinitions = Array.from(attributeSet).map((attr) => ({
const attributeDefinitions: AttributeDefinition[] = Array.from(attributeSet).map((attr) => ({
AttributeName: attr,
AttributeType: "S"
}))
Expand Down
5 changes: 3 additions & 2 deletions src/main/dynamo/UnprocessedKeyCollector.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DynamoDB } from "aws-sdk"
import { NativeAttributeValue } from "@aws-sdk/util-dynamodb"
import { PartitionAndSortKey } from "./keys"
import { Table } from "./Table"
import { TaggedModel } from "./types"
Expand All @@ -10,6 +10,7 @@ import { TaggedModel } from "./types"
* This makes it easy for the caller to re-submit the operation (e.g. batchGet) by directly
* passing the keys returned by Beyonce
*/

export class UnprocessedKeyCollector<T extends PartitionAndSortKey<TaggedModel>> {
private dynamoKeyToBeyonceKey: { [key: string]: T } = {}
private unprocessedKeys: T[] = []
Expand All @@ -18,7 +19,7 @@ export class UnprocessedKeyCollector<T extends PartitionAndSortKey<TaggedModel>>
inputKeys.forEach((key) => (this.dynamoKeyToBeyonceKey[`${key.partitionKey}-${key.sortKey}`] = key))
}

add(unprocessedKey: DynamoDB.DocumentClient.Key): void {
add(unprocessedKey: { [key: string]: NativeAttributeValue }): void {
const { partitionKeyName, sortKeyName } = this.table
const beyonceKey = this.dynamoKeyToBeyonceKey[`${unprocessedKey[partitionKeyName]}-${unprocessedKey[sortKeyName]}`]
if (beyonceKey) {
Expand Down
10 changes: 5 additions & 5 deletions src/main/dynamo/expressions/DynamoDBExpression.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DynamoDB } from "aws-sdk"
import { NativeAttributeValue } from "@aws-sdk/util-dynamodb"

export type DynamoDBExpression = {
expression: DynamoDB.DocumentClient.ConditionExpression
attributeNames: DynamoDB.DocumentClient.ExpressionAttributeNameMap
attributeValues: DynamoDB.DocumentClient.ExpressionAttributeValueMap
export interface DynamoDBExpression {
expression: string
attributeNames: { [key: string]: string }
attributeValues: { [key: string]: NativeAttributeValue }
}
10 changes: 5 additions & 5 deletions src/main/dynamo/iterators/pagedIterator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NativeAttributeValue } from "@aws-sdk/util-dynamodb"
import { JayZ } from "@ginger.io/jay-z"
import { DynamoDB } from "aws-sdk"
import { DocumentClient } from "aws-sdk/clients/dynamodb"
import { CompositeError } from "../../CompositeError"
import { groupModelsByType } from "../groupModelsByType"
Expand All @@ -15,7 +15,7 @@ export type RawDynamoDBPage = {
export type PageResults<T extends TaggedModel> = {
items: T[]
errors: Error[]
lastEvaluatedKey?: DynamoDB.DocumentClient.Key
lastEvaluatedKey?: { [key: string]: NativeAttributeValue }
}

export async function groupAllPages<T extends TaggedModel>(
Expand Down Expand Up @@ -47,7 +47,7 @@ export async function* pagedIterator<T, U extends TaggedModel>(
const items: U[] = []
const errors: Error[] = []
try {
const response: DynamoDB.DocumentClient.QueryOutput = await executeOperation(pendingOperation)
const response = await executeOperation(pendingOperation)

if (response.LastEvaluatedKey !== undefined) {
lastEvaluatedKey = response.LastEvaluatedKey
Expand All @@ -72,13 +72,13 @@ export async function* pagedIterator<T, U extends TaggedModel>(
if (item) {
items.push(item)
} else if (error) {
errors.push(error)
errors.push(error as Error)
}
})

yield { items, lastEvaluatedKey, errors }
} catch (error) {
errors.push(error)
errors.push(error as Error)
yield { items: [], lastEvaluatedKey, errors }
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/dynamo/operations/BaseParams.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DynamoDB } from "aws-sdk"
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"
import { Table } from "../Table"

export interface BaseParams {
table: Table<string, string>
client: DynamoDB.DocumentClient
client: DynamoDBDocumentClient
}
15 changes: 8 additions & 7 deletions src/main/dynamo/operations/batchGetItems.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DynamoDB } from "aws-sdk"
import { BatchGetCommand, DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"
import { NativeAttributeValue } from "@aws-sdk/util-dynamodb"
import { PartitionAndSortKey } from "../keys"
import { Table } from "../Table"
import { TaggedModel } from "../types"
Expand All @@ -7,13 +8,13 @@ import { BaseParams } from "./BaseParams"

export interface BatchGetItemsParams<T extends PartitionAndSortKey<TaggedModel>> extends BaseParams {
table: Table<string, string>
client: DynamoDB.DocumentClient
client: DynamoDBDocumentClient
keys: T[]
consistentRead?: boolean
}

export interface BatchGetItemsResult<T extends PartitionAndSortKey<TaggedModel>> {
items: DynamoDB.DocumentClient.AttributeMap[]
items: { [key: string]: NativeAttributeValue }[]
unprocessedKeys: T[]
}

Expand All @@ -23,20 +24,20 @@ export async function batchGetItems<T extends PartitionAndSortKey<TaggedModel>>(
): Promise<BatchGetItemsResult<T>> {
const { table, client, consistentRead } = params
const { tableName } = table
const results = await client
.batchGet({
const results = await client.send(
new BatchGetCommand({
RequestItems: {
[tableName]: {
ConsistentRead: consistentRead,
Keys: mapToKeysAndRemoveDuplicate(params.keys, table)
}
}
})
.promise()
)

const unprocessedKeys = new UnprocessedKeyCollector(table, params.keys)
if (results.UnprocessedKeys && results.UnprocessedKeys[tableName]) {
results.UnprocessedKeys[tableName].Keys.forEach((key) => unprocessedKeys.add(key))
results.UnprocessedKeys[tableName].Keys?.forEach((key) => unprocessedKeys.add(key))
}

if (results.Responses && results.Responses[tableName]) {
Expand Down
12 changes: 6 additions & 6 deletions src/main/dynamo/operations/batchWriteItems.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DynamoDB } from "aws-sdk"
import { BatchWriteCommand, BatchWriteCommandInput } from "@aws-sdk/lib-dynamodb"
import { PartitionAndSortKey } from "../keys"
import { TaggedModel } from "../types"
import { UnprocessedKeyCollector } from "../UnprocessedKeyCollector"
Expand All @@ -20,14 +20,14 @@ export async function batchWriteItems<T extends TaggedModel>(
): Promise<BatchWriteItemResult<T>> {
const { table, client, putItems = [], deleteItems = [] } = params
const { tableName, partitionKeyName, sortKeyName } = table
const requests: DynamoDB.DocumentClient.WriteRequest[] = []
const requests: BatchWriteCommandInput["RequestItems"] = { [tableName]: [] }

putItems.forEach((item) => {
requests.push({ PutRequest: { Item: item } })
requests[tableName].push({ PutRequest: { Item: item } })
})

deleteItems.forEach((key) => {
requests.push({
requests[tableName].push({
DeleteRequest: {
Key: {
[partitionKeyName]: key.partitionKey,
Expand All @@ -37,15 +37,15 @@ export async function batchWriteItems<T extends TaggedModel>(
})
})

const results = await client.batchWrite({ RequestItems: { [tableName]: requests } }).promise()
const results = await client.send(new BatchWriteCommand({ RequestItems: requests }))
const unprocessedPuts: T[] = []
const unprocessedDeleteKeys = new UnprocessedKeyCollector(table, deleteItems)

if (results.UnprocessedItems && results.UnprocessedItems[tableName]) {
results.UnprocessedItems[tableName].forEach(({ PutRequest, DeleteRequest }) => {
if (PutRequest) {
unprocessedPuts.push(PutRequest.Item as T)
} else if (DeleteRequest) {
} else if (DeleteRequest && DeleteRequest.Key) {
unprocessedDeleteKeys.add(DeleteRequest.Key)
}
})
Expand Down
7 changes: 4 additions & 3 deletions src/main/dynamo/operations/deleteItem.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DeleteCommand } from "@aws-sdk/lib-dynamodb"
import { PartitionAndSortKey } from "../keys"
import { TaggedModel } from "../types"
import { BaseParams } from "./BaseParams"
Expand All @@ -8,13 +9,13 @@ export interface DeleteItemParams<T extends TaggedModel> extends BaseParams {

export async function deleteItem<T extends TaggedModel>(params: DeleteItemParams<T>): Promise<void> {
const { table, client, key } = params
await client
.delete({
await client.send(
new DeleteCommand({
TableName: table.tableName,
Key: {
[table.partitionKeyName]: key.partitionKey,
[table.sortKeyName]: key.sortKey
}
})
.promise()
)
}
Loading