From 8a1adb597762dc576a216af361926d148e9fb71c Mon Sep 17 00:00:00 2001 From: Olli Warro Date: Tue, 19 Mar 2024 14:19:26 +0200 Subject: [PATCH 1/5] add support for delete item command --- src/nodes/deleteNode.ts | 12 ++ ...eItemQueryBuilder.integration.test.ts.snap | 8 + ...deleteItemQueryBuilder.integration.test.ts | 50 +++++ src/queryBuilders/deleteItemQueryBuilder.ts | 195 ++++++++++++++++++ src/queryBuilders/putItemQueryBuilder.ts | 2 +- src/queryCompiler/queryCompiler.ts | 52 ++++- src/queryCreator.ts | 27 +++ 7 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 src/nodes/deleteNode.ts create mode 100644 src/queryBuilders/__snapshots__/deleteItemQueryBuilder.integration.test.ts.snap create mode 100644 src/queryBuilders/deleteItemQueryBuilder.integration.test.ts create mode 100644 src/queryBuilders/deleteItemQueryBuilder.ts diff --git a/src/nodes/deleteNode.ts b/src/nodes/deleteNode.ts new file mode 100644 index 0000000..8c9dd51 --- /dev/null +++ b/src/nodes/deleteNode.ts @@ -0,0 +1,12 @@ +import { ExpressionNode } from "./expressionNode"; +import { KeysNode } from "./keysNode"; +import { ReturnValuesNode } from "./returnValuesNode"; +import { TableNode } from "./tableNode"; + +export type DeleteNode = { + readonly kind: "DeleteNode"; + readonly table: TableNode; + readonly conditionExpression: ExpressionNode; + readonly returnValues?: ReturnValuesNode; + readonly keys?: KeysNode; +}; diff --git a/src/queryBuilders/__snapshots__/deleteItemQueryBuilder.integration.test.ts.snap b/src/queryBuilders/__snapshots__/deleteItemQueryBuilder.integration.test.ts.snap new file mode 100644 index 0000000..7ca8efa --- /dev/null +++ b/src/queryBuilders/__snapshots__/deleteItemQueryBuilder.integration.test.ts.snap @@ -0,0 +1,8 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`DeleteItemQueryBuilder > handles a simple delete query 1`] = ` +{ + "dataTimestamp": 2, + "userId": "1", +} +`; diff --git a/src/queryBuilders/deleteItemQueryBuilder.integration.test.ts b/src/queryBuilders/deleteItemQueryBuilder.integration.test.ts new file mode 100644 index 0000000..39a6c8b --- /dev/null +++ b/src/queryBuilders/deleteItemQueryBuilder.integration.test.ts @@ -0,0 +1,50 @@ +import { DDB } from "../../test/testFixture"; +import { getDDBClientFor, startDDBTestContainer } from "../../test/testUtil"; +import { Tsynamo } from "./../index"; + +describe("DeleteItemQueryBuilder", () => { + let tsynamoClient: Tsynamo; + + beforeAll(async () => { + const testContainer = await startDDBTestContainer(); + + tsynamoClient = new Tsynamo({ + ddbClient: await getDDBClientFor(testContainer), + }); + }); + + it("handles a simple delete query", async () => { + await tsynamoClient + .putItem("myTable") + .item({ + userId: "1", + dataTimestamp: 2, + }) + .execute(); + + const itemBeforeDeletion = await tsynamoClient + .getItemFrom("myTable") + .keys({ userId: "1", dataTimestamp: 2 }) + .execute(); + + expect(itemBeforeDeletion).toBeDefined(); + + const deleteResponse = await tsynamoClient + .deleteItem("myTable") + .keys({ + userId: "1", + dataTimestamp: 2, + }) + .returnValues("ALL_OLD") + .execute(); + + expect(deleteResponse).toMatchSnapshot(); + + const itemAfterDeletion = await tsynamoClient + .getItemFrom("myTable") + .keys({ userId: "1", dataTimestamp: 2 }) + .execute(); + + expect(itemAfterDeletion).toBeUndefined(); + }); +}); diff --git a/src/queryBuilders/deleteItemQueryBuilder.ts b/src/queryBuilders/deleteItemQueryBuilder.ts new file mode 100644 index 0000000..8060711 --- /dev/null +++ b/src/queryBuilders/deleteItemQueryBuilder.ts @@ -0,0 +1,195 @@ +import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; +import { DeleteNode } from "../nodes/deleteNode"; +import { ReturnValuesOptions } from "../nodes/returnValuesNode"; +import { QueryCompiler } from "../queryCompiler"; +import { + ExecuteOutput, + ObjectKeyPaths, + PickPk, + PickSkRequired, +} from "../typeHelpers"; +import { preventAwait } from "../util/preventAwait"; +import { + AttributeBeginsWithExprArg, + AttributeBetweenExprArg, + AttributeContainsExprArg, + AttributeFuncExprArg, + BuilderExprArg, + ComparatorExprArg, + ExprArgs, + ExpressionBuilder, + NotExprArg, +} from "./expressionBuilder"; + +export interface DeleteItemQueryBuilderInterface< + DDB, + Table extends keyof DDB, + O +> { + // conditionExpression + conditionExpression>( + ...args: ComparatorExprArg + ): DeleteItemQueryBuilderInterface; + + conditionExpression>( + ...args: AttributeFuncExprArg + ): DeleteItemQueryBuilderInterface; + + conditionExpression>( + ...args: AttributeBeginsWithExprArg + ): DeleteItemQueryBuilderInterface; + + conditionExpression>( + ...args: AttributeContainsExprArg + ): DeleteItemQueryBuilderInterface; + + conditionExpression>( + ...args: AttributeBetweenExprArg + ): DeleteItemQueryBuilderInterface; + + conditionExpression>( + ...args: NotExprArg + ): DeleteItemQueryBuilderInterface; + + conditionExpression>( + ...args: BuilderExprArg + ): DeleteItemQueryBuilderInterface; + + // orConditionExpression + orConditionExpression>( + ...args: ComparatorExprArg + ): DeleteItemQueryBuilderInterface; + + orConditionExpression>( + ...args: AttributeFuncExprArg + ): DeleteItemQueryBuilderInterface; + + orConditionExpression>( + ...args: AttributeBeginsWithExprArg + ): DeleteItemQueryBuilderInterface; + + orConditionExpression>( + ...args: AttributeContainsExprArg + ): DeleteItemQueryBuilderInterface; + + orConditionExpression>( + ...args: AttributeBetweenExprArg + ): DeleteItemQueryBuilderInterface; + + orConditionExpression>( + ...args: NotExprArg + ): DeleteItemQueryBuilderInterface; + + orConditionExpression>( + ...args: BuilderExprArg + ): DeleteItemQueryBuilderInterface; + + returnValues( + option: Extract + ): DeleteItemQueryBuilderInterface; + + keys & PickSkRequired>( + pk: Keys + ): DeleteItemQueryBuilderInterface; + + execute(): Promise[] | undefined>; +} + +/** + * @todo support ConditionExpression + */ +export class DeleteItemQueryBuilder< + DDB, + Table extends keyof DDB, + O extends DDB[Table] +> implements DeleteItemQueryBuilderInterface +{ + readonly #props: DeleteItemQueryBuilderProps; + + constructor(props: DeleteItemQueryBuilderProps) { + this.#props = props; + } + + conditionExpression>( + ...args: ExprArgs + ): DeleteItemQueryBuilderInterface { + const eB = new ExpressionBuilder({ + node: { ...this.#props.node.conditionExpression }, + }); + + const expressionNode = eB.expression(...args)._getNode(); + + return new DeleteItemQueryBuilder({ + ...this.#props, + node: { + ...this.#props.node, + conditionExpression: expressionNode, + }, + }); + } + + orConditionExpression>( + ...args: ExprArgs + ): DeleteItemQueryBuilderInterface { + const eB = new ExpressionBuilder({ + node: { ...this.#props.node.conditionExpression }, + }); + + const expressionNode = eB.orExpression(...args)._getNode(); + + return new DeleteItemQueryBuilder({ + ...this.#props, + node: { + ...this.#props.node, + conditionExpression: expressionNode, + }, + }); + } + + returnValues( + option: Extract + ): DeleteItemQueryBuilderInterface { + return new DeleteItemQueryBuilder({ + ...this.#props, + node: { + ...this.#props.node, + returnValues: { + kind: "ReturnValuesNode", + option, + }, + }, + }); + } + + keys & PickSkRequired>( + keys: Keys + ) { + return new DeleteItemQueryBuilder({ + ...this.#props, + node: { + ...this.#props.node, + keys: { + kind: "KeysNode", + keys, + }, + }, + }); + } + + execute = async (): Promise[] | undefined> => { + const deleteCommand = this.#props.queryCompiler.compile(this.#props.node); + const data = await this.#props.ddbClient.send(deleteCommand); + return data.Attributes as any; + }; +} + +preventAwait( + DeleteItemQueryBuilder, + "Don't await DeleteItemQueryBuilder instances directly. To execute the query you need to call the `execute` method" +); + +interface DeleteItemQueryBuilderProps { + readonly node: DeleteNode; + readonly ddbClient: DynamoDBDocumentClient; + readonly queryCompiler: QueryCompiler; +} diff --git a/src/queryBuilders/putItemQueryBuilder.ts b/src/queryBuilders/putItemQueryBuilder.ts index ce4cbd0..d826439 100644 --- a/src/queryBuilders/putItemQueryBuilder.ts +++ b/src/queryBuilders/putItemQueryBuilder.ts @@ -153,7 +153,7 @@ export class PutItemQueryBuilder< } returnValues( - option: ReturnValuesOptions + option: Extract ): PutItemQueryBuilderInterface { return new PutItemQueryBuilder({ ...this.#props, diff --git a/src/queryCompiler/queryCompiler.ts b/src/queryCompiler/queryCompiler.ts index 7df9777..108b936 100644 --- a/src/queryCompiler/queryCompiler.ts +++ b/src/queryCompiler/queryCompiler.ts @@ -1,4 +1,9 @@ -import { GetCommand, PutCommand, QueryCommand } from "@aws-sdk/lib-dynamodb"; +import { + DeleteCommand, + GetCommand, + PutCommand, + QueryCommand, +} from "@aws-sdk/lib-dynamodb"; import { ExpressionJoinTypeNode } from "../nodes/expressionJoinTypeNode"; import { ExpressionNode } from "../nodes/expressionNode"; import { GetNode } from "../nodes/getNode"; @@ -11,12 +16,14 @@ import { } from "./compilerUtil"; import { AttributesNode } from "../nodes/attributesNode"; import { PutNode } from "../nodes/putNode"; +import { DeleteNode } from "../nodes/deleteNode"; export class QueryCompiler { compile(rootNode: QueryNode): QueryCommand; compile(rootNode: GetNode): GetCommand; compile(rootNode: PutNode): PutCommand; - compile(rootNode: QueryNode | GetNode | PutNode) { + compile(rootNode: DeleteNode): DeleteCommand; + compile(rootNode: QueryNode | GetNode | PutNode | DeleteNode) { switch (rootNode.kind) { case "GetNode": return this.compileGetNode(rootNode); @@ -24,6 +31,8 @@ export class QueryCompiler { return this.compileQueryNode(rootNode); case "PutNode": return this.compilePutNode(rootNode); + case "DeleteNode": + return this.compileDeleteNode(rootNode); } } @@ -140,6 +149,45 @@ export class QueryCompiler { }); } + compileDeleteNode(deleteNode: DeleteNode) { + const { + table: tableNode, + returnValues: returnValuesNode, + keys: keysNode, + conditionExpression: conditionExpressionNode, + } = deleteNode; + + const attributeNames = new Map(); + const filterExpressionAttributeValues = new Map(); + + const compiledConditionExpression = this.compileExpression( + conditionExpressionNode, + filterExpressionAttributeValues, + attributeNames + ); + + return new DeleteCommand({ + TableName: tableNode.table, + Key: keysNode?.keys, + ReturnValues: returnValuesNode?.option, + ConditionExpression: compiledConditionExpression + ? compiledConditionExpression + : undefined, + ExpressionAttributeValues: + filterExpressionAttributeValues.size > 0 + ? { + ...Object.fromEntries(filterExpressionAttributeValues), + } + : undefined, + ExpressionAttributeNames: + attributeNames.size > 0 + ? { + ...Object.fromEntries(attributeNames), + } + : undefined, + }); + } + compileAttributeNamesNode(node?: AttributesNode) { const ProjectionExpression = node?.attributes .map((att) => getExpressionAttributeNameFrom(att)) diff --git a/src/queryCreator.ts b/src/queryCreator.ts index d2a532c..4244a8f 100644 --- a/src/queryCreator.ts +++ b/src/queryCreator.ts @@ -1,4 +1,5 @@ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; +import { DeleteItemQueryBuilder } from "./queryBuilders/deleteItemQueryBuilder"; import { GetQueryBuilder } from "./queryBuilders/getItemQueryBuilder"; import { PutItemQueryBuilder, @@ -92,6 +93,32 @@ export class QueryCreator { queryCompiler: this.#props.queryCompiler, }); } + + /** + * + * @param table Table to perform the put item command to + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/DeleteItemCommand/ + */ + deleteItem( + table: Table + ): DeleteItemQueryBuilder { + return new DeleteItemQueryBuilder({ + node: { + kind: "DeleteNode", + table: { + kind: "TableNode", + table, + }, + conditionExpression: { + kind: "ExpressionNode", + expressions: [], + }, + }, + ddbClient: this.#props.ddbClient, + queryCompiler: this.#props.queryCompiler, + }); + } } export interface QueryCreatorProps { From 46b406ba591f2c08906c962dd442a2ebbf37ebc5 Mon Sep 17 00:00:00 2001 From: Olli Warro Date: Tue, 19 Mar 2024 14:27:56 +0200 Subject: [PATCH 2/5] add another test --- README.md | 2 +- ...deleteItemQueryBuilder.integration.test.ts | 43 ++++++++++++++++++- src/queryBuilders/deleteItemQueryBuilder.ts | 2 +- .../getItemQueryBuilder.integration.test.ts | 16 +++---- .../putItemQueryBuilder.integration.test.ts | 4 +- src/queryBuilders/putItemQueryBuilder.ts | 3 -- src/queryCreator.ts | 2 +- 7 files changed, 54 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f631ecd..f5ca6b3 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ const tsynamoClient = new Tsynamo({ ```ts await tsynamoClient - .getItemFrom("UserEvents") + .getItem("UserEvents") .keys({ userId: "123", eventId: 222, diff --git a/src/queryBuilders/deleteItemQueryBuilder.integration.test.ts b/src/queryBuilders/deleteItemQueryBuilder.integration.test.ts index 39a6c8b..916cfa1 100644 --- a/src/queryBuilders/deleteItemQueryBuilder.integration.test.ts +++ b/src/queryBuilders/deleteItemQueryBuilder.integration.test.ts @@ -23,7 +23,7 @@ describe("DeleteItemQueryBuilder", () => { .execute(); const itemBeforeDeletion = await tsynamoClient - .getItemFrom("myTable") + .getItem("myTable") .keys({ userId: "1", dataTimestamp: 2 }) .execute(); @@ -41,10 +41,49 @@ describe("DeleteItemQueryBuilder", () => { expect(deleteResponse).toMatchSnapshot(); const itemAfterDeletion = await tsynamoClient - .getItemFrom("myTable") + .getItem("myTable") .keys({ userId: "1", dataTimestamp: 2 }) .execute(); expect(itemAfterDeletion).toBeUndefined(); }); + + it("handles a delete query with a ConditionExpression", async () => { + await tsynamoClient + .putItem("myTable") + .item({ + userId: "1", + dataTimestamp: 2, + tags: ["meow"], + someBoolean: true, + }) + .execute(); + + expect( + tsynamoClient + .deleteItem("myTable") + .keys({ + userId: "1", + dataTimestamp: 2, + }) + .conditionExpression("NOT", (qb) => { + return qb.expression("tags", "contains", "meow"); + }) + .execute() + ).rejects.toMatchInlineSnapshot( + `[ConditionalCheckFailedException: The conditional request failed]` + ); + + const res = await tsynamoClient + .deleteItem("myTable") + .keys({ userId: "1", dataTimestamp: 2 }) + .conditionExpression("NOT", (qb) => { + return qb.expression("tags", "contains", "meow"); + }) + .orConditionExpression("someBoolean", "attribute_exists") + .returnValues("ALL_OLD") + .execute(); + + expect(res).toBeDefined(); + }); }); diff --git a/src/queryBuilders/deleteItemQueryBuilder.ts b/src/queryBuilders/deleteItemQueryBuilder.ts index 8060711..01c0a09 100644 --- a/src/queryBuilders/deleteItemQueryBuilder.ts +++ b/src/queryBuilders/deleteItemQueryBuilder.ts @@ -96,7 +96,7 @@ export interface DeleteItemQueryBuilderInterface< } /** - * @todo support ConditionExpression + * @todo support ReturnValuesOnConditionCheckFailure */ export class DeleteItemQueryBuilder< DDB, diff --git a/src/queryBuilders/getItemQueryBuilder.integration.test.ts b/src/queryBuilders/getItemQueryBuilder.integration.test.ts index b64551c..4590c61 100644 --- a/src/queryBuilders/getItemQueryBuilder.integration.test.ts +++ b/src/queryBuilders/getItemQueryBuilder.integration.test.ts @@ -15,7 +15,7 @@ describe("GetItemQueryBuilder", () => { it("handles a basic get item command", async () => { const data = await tsynamoClient - .getItemFrom("myTable") + .getItem("myTable") .keys({ userId: TEST_DATA[1].userId, dataTimestamp: TEST_DATA[1].dataTimestamp, @@ -27,7 +27,7 @@ describe("GetItemQueryBuilder", () => { it("handles selecting specific attributes", async () => { const data = await tsynamoClient - .getItemFrom("myTable") + .getItem("myTable") .keys({ userId: TEST_DATA[0].userId, dataTimestamp: TEST_DATA[0].dataTimestamp, @@ -43,7 +43,7 @@ describe("GetItemQueryBuilder", () => { it("handles selecting nested attributes", async () => { const data = await tsynamoClient - .getItemFrom("myTable") + .getItem("myTable") .keys({ userId: TEST_DATA[4].userId, dataTimestamp: TEST_DATA[4].dataTimestamp, @@ -59,7 +59,7 @@ describe("GetItemQueryBuilder", () => { it("handles selecting deeply nested attributes", async () => { const data = await tsynamoClient - .getItemFrom("myTable") + .getItem("myTable") .keys({ userId: TEST_DATA[8].userId, dataTimestamp: TEST_DATA[8].dataTimestamp, @@ -77,7 +77,7 @@ describe("GetItemQueryBuilder", () => { it("handles selecting attributes from arrays", async () => { const data = await tsynamoClient - .getItemFrom("myOtherTable") + .getItem("myOtherTable") .keys({ userId: TEST_DATA[6].userId, stringTimestamp: "123", @@ -92,7 +92,7 @@ describe("GetItemQueryBuilder", () => { }); it("handles selecting attributes from tuples", async () => { const data = await tsynamoClient - .getItemFrom("myOtherTable") + .getItem("myOtherTable") .keys({ userId: TEST_DATA[6].userId, stringTimestamp: "123", @@ -106,7 +106,7 @@ describe("GetItemQueryBuilder", () => { }); it("handles selecting multiple attributes from tuples", async () => { const data = await tsynamoClient - .getItemFrom("myOtherTable") + .getItem("myOtherTable") .keys({ userId: TEST_DATA[6].userId, stringTimestamp: "123", @@ -123,7 +123,7 @@ describe("GetItemQueryBuilder", () => { expect( async () => await tsynamoClient - .getItemFrom("myTable") + .getItem("myTable") .keys({ userId: TEST_DATA[0].userId, dataTimestamp: TEST_DATA[0].dataTimestamp, diff --git a/src/queryBuilders/putItemQueryBuilder.integration.test.ts b/src/queryBuilders/putItemQueryBuilder.integration.test.ts index 43a4d84..cf06626 100644 --- a/src/queryBuilders/putItemQueryBuilder.integration.test.ts +++ b/src/queryBuilders/putItemQueryBuilder.integration.test.ts @@ -21,7 +21,7 @@ describe("PutItemQueryBuilder", () => { it("handles a simple put query", async () => { let result = await tsynamoClient - .getItemFrom("myTable") + .getItem("myTable") .keys({ userId: itemToPut.userId, dataTimestamp: itemToPut.dataTimestamp, @@ -33,7 +33,7 @@ describe("PutItemQueryBuilder", () => { await tsynamoClient.putItem("myTable").item(itemToPut).execute(); result = await tsynamoClient - .getItemFrom("myTable") + .getItem("myTable") .keys({ userId: "333", dataTimestamp: 222, diff --git a/src/queryBuilders/putItemQueryBuilder.ts b/src/queryBuilders/putItemQueryBuilder.ts index d826439..b638264 100644 --- a/src/queryBuilders/putItemQueryBuilder.ts +++ b/src/queryBuilders/putItemQueryBuilder.ts @@ -86,9 +86,6 @@ export interface PutItemQueryBuilderInterface { execute(): Promise[] | undefined>; } -/** - * @todo support ConditionExpression - */ export class PutItemQueryBuilder< DDB, Table extends keyof DDB, diff --git a/src/queryCreator.ts b/src/queryCreator.ts index 4244a8f..e105f81 100644 --- a/src/queryCreator.ts +++ b/src/queryCreator.ts @@ -25,7 +25,7 @@ export class QueryCreator { * @see https://docs.aws.amazon.com/cli/latest/reference/dynamodb/get-item.html * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/GetItemCommand/ */ - getItemFrom
( + getItem
( table: Table ): GetQueryBuilder { return new GetQueryBuilder({ From 91dbda2c0dae963bdcc487dbf4430c928bb02c9a Mon Sep 17 00:00:00 2001 From: Olli Warro Date: Tue, 19 Mar 2024 14:43:04 +0200 Subject: [PATCH 3/5] add support for ReturnValuesOnConditionCheckFailure --- package.json | 2 +- src/nodes/deleteNode.ts | 3 ++- src/nodes/returnValuesNode.ts | 5 +++++ src/queryBuilders/deleteItemQueryBuilder.ts | 19 +++++++++++++++++++ src/queryCompiler/queryCompiler.ts | 4 ++++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 74d1490..13c689b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tsynamo", "author": "woltsu", - "version": "0.0.6", + "version": "0.0.7", "description": "Typed query builder for DynamoDB", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/nodes/deleteNode.ts b/src/nodes/deleteNode.ts index 8c9dd51..4ccf687 100644 --- a/src/nodes/deleteNode.ts +++ b/src/nodes/deleteNode.ts @@ -1,6 +1,6 @@ import { ExpressionNode } from "./expressionNode"; import { KeysNode } from "./keysNode"; -import { ReturnValuesNode } from "./returnValuesNode"; +import { ReturnOldValuesNode, ReturnValuesNode } from "./returnValuesNode"; import { TableNode } from "./tableNode"; export type DeleteNode = { @@ -8,5 +8,6 @@ export type DeleteNode = { readonly table: TableNode; readonly conditionExpression: ExpressionNode; readonly returnValues?: ReturnValuesNode; + readonly returnValuesOnConditionCheckFailure?: ReturnOldValuesNode; readonly keys?: KeysNode; }; diff --git a/src/nodes/returnValuesNode.ts b/src/nodes/returnValuesNode.ts index d413ccd..5759f12 100644 --- a/src/nodes/returnValuesNode.ts +++ b/src/nodes/returnValuesNode.ts @@ -9,3 +9,8 @@ export type ReturnValuesNode = { readonly kind: "ReturnValuesNode"; readonly option: ReturnValuesOptions; }; + +export type ReturnOldValuesNode = { + readonly kind: "ReturnValuesNode"; + readonly option: Extract; +}; diff --git a/src/queryBuilders/deleteItemQueryBuilder.ts b/src/queryBuilders/deleteItemQueryBuilder.ts index 01c0a09..b8d5400 100644 --- a/src/queryBuilders/deleteItemQueryBuilder.ts +++ b/src/queryBuilders/deleteItemQueryBuilder.ts @@ -88,6 +88,10 @@ export interface DeleteItemQueryBuilderInterface< option: Extract ): DeleteItemQueryBuilderInterface; + returnValuesOnConditionCheckFailure( + option: Extract + ): DeleteItemQueryBuilderInterface; + keys & PickSkRequired>( pk: Keys ): DeleteItemQueryBuilderInterface; @@ -161,6 +165,21 @@ export class DeleteItemQueryBuilder< }); } + returnValuesOnConditionCheckFailure( + option: Extract + ): DeleteItemQueryBuilderInterface { + return new DeleteItemQueryBuilder({ + ...this.#props, + node: { + ...this.#props.node, + returnValuesOnConditionCheckFailure: { + kind: "ReturnValuesNode", + option, + }, + }, + }); + } + keys & PickSkRequired>( keys: Keys ) { diff --git a/src/queryCompiler/queryCompiler.ts b/src/queryCompiler/queryCompiler.ts index 108b936..83c919c 100644 --- a/src/queryCompiler/queryCompiler.ts +++ b/src/queryCompiler/queryCompiler.ts @@ -153,6 +153,8 @@ export class QueryCompiler { const { table: tableNode, returnValues: returnValuesNode, + returnValuesOnConditionCheckFailure: + returnValuesOnConditionCheckFailureNode, keys: keysNode, conditionExpression: conditionExpressionNode, } = deleteNode; @@ -170,6 +172,8 @@ export class QueryCompiler { TableName: tableNode.table, Key: keysNode?.keys, ReturnValues: returnValuesNode?.option, + ReturnValuesOnConditionCheckFailure: + returnValuesOnConditionCheckFailureNode?.option, ConditionExpression: compiledConditionExpression ? compiledConditionExpression : undefined, From 961053d4d0505025a3175df5c05a5e19c702120f Mon Sep 17 00:00:00 2001 From: Olli Warro Date: Tue, 19 Mar 2024 14:46:32 +0200 Subject: [PATCH 4/5] update readme --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f5ca6b3..bcbecea 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,30 @@ await tsynamoClient ## Delete item -WIP +### Simple delete item + +```ts +await tsynamoClient + .deleteItem("myTable") + .keys({ + userId: "123", + eventId: 313, + }) + .execute(); +``` + +### Simple delete item with ConditionExpression + +```ts +await tsynamoClient + .deleteItem("myTable") + .keys({ + userId: "123", + eventId: 313, + }) + .conditionExpression("eventType", "attribute_not_exists") + .execute(); +``` ## Update item From 10b4662defb247c00b98e9bad32f3ef44690cdee Mon Sep 17 00:00:00 2001 From: Olli Warro Date: Tue, 19 Mar 2024 14:50:06 +0200 Subject: [PATCH 5/5] update comment --- src/queryCreator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/queryCreator.ts b/src/queryCreator.ts index e105f81..d3d191e 100644 --- a/src/queryCreator.ts +++ b/src/queryCreator.ts @@ -96,7 +96,7 @@ export class QueryCreator { /** * - * @param table Table to perform the put item command to + * @param table Table to perform the delete item command to * * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/DeleteItemCommand/ */