Skip to content

Commit

Permalink
initial work for updateItemQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
mindler-olli committed Mar 22, 2024
1 parent ed2e564 commit 0b813d3
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 1 deletion.
12 changes: 12 additions & 0 deletions src/nodes/updateNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ExpressionNode } from "./expressionNode";
import { ItemNode } from "./itemNode";
import { ReturnValuesNode } from "./returnValuesNode";
import { TableNode } from "./tableNode";

export type UpdateNode = {
readonly kind: "UpdateNode";
readonly table: TableNode;
readonly conditionExpression: ExpressionNode;
readonly item?: ItemNode;
readonly returnValues?: ReturnValuesNode;
};
17 changes: 17 additions & 0 deletions src/queryBuilders/updateItemQueryBuilder.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { DDB } from "../../test/testFixture";
import { getDDBClientFor, startDDBTestContainer } from "../../test/testUtil";
import { Tsynamo } from "./../index";

describe("UpdateItemQueryBuilder", () => {
let tsynamoClient: Tsynamo<DDB>;

beforeAll(async () => {
const testContainer = await startDDBTestContainer();

tsynamoClient = new Tsynamo<DDB>({
ddbClient: await getDDBClientFor(testContainer),
});
});

it.todo("handles a simple update item query");
});
173 changes: 173 additions & 0 deletions src/queryBuilders/updateItemQueryBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { DynamoDBDocumentClient, UpdateCommand } from "@aws-sdk/lib-dynamodb";
import { ReturnValuesOptions } from "../nodes/returnValuesNode";
import { UpdateNode } from "../nodes/updateNode";
import { QueryCompiler } from "../queryCompiler";
import { ExecuteOutput, ObjectKeyPaths } from "../typeHelpers";
import { preventAwait } from "../util/preventAwait";
import {
AttributeBeginsWithExprArg,
AttributeBetweenExprArg,
AttributeContainsExprArg,
AttributeFuncExprArg,
BuilderExprArg,
ComparatorExprArg,
ExprArgs,
ExpressionBuilder,
NotExprArg,
} from "./expressionBuilder";

export interface UpdateItemQueryBuilderInterface<
DDB,
Table extends keyof DDB,
O
> {
// conditionExpression
conditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: ComparatorExprArg<DDB, Table, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

conditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: AttributeFuncExprArg<Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

conditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: AttributeBeginsWithExprArg<Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

conditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: AttributeContainsExprArg<DDB, Table, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

conditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: AttributeBetweenExprArg<DDB, Table, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

conditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: NotExprArg<DDB, Table, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

conditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: BuilderExprArg<DDB, Table, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

// orConditionExpression
orConditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: ComparatorExprArg<DDB, Table, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

orConditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: AttributeFuncExprArg<Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

orConditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: AttributeBeginsWithExprArg<Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

orConditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: AttributeContainsExprArg<DDB, Table, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

orConditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: AttributeBetweenExprArg<DDB, Table, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

orConditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: NotExprArg<DDB, Table, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

orConditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: BuilderExprArg<DDB, Table, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

returnValues(
option: ReturnValuesOptions
): UpdateItemQueryBuilderInterface<DDB, Table, O>;

compile(): UpdateCommand;
execute(): Promise<ExecuteOutput<O>[] | undefined>;
}

export class UpdateItemQueryBuilder<
DDB,
Table extends keyof DDB,
O extends DDB[Table]
> implements UpdateItemQueryBuilderInterface<DDB, Table, O>
{
readonly #props: UpdateItemQueryBuilderProps;

constructor(props: UpdateItemQueryBuilderProps) {
this.#props = props;
}

conditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: ExprArgs<DDB, Table, O, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O> {
const eB = new ExpressionBuilder<DDB, Table, O>({
node: { ...this.#props.node.conditionExpression },
});

const expressionNode = eB.expression(...args)._getNode();

return new UpdateItemQueryBuilder<DDB, Table, O>({
...this.#props,
node: {
...this.#props.node,
conditionExpression: expressionNode,
},
});
}

orConditionExpression<Key extends ObjectKeyPaths<DDB[Table]>>(
...args: ExprArgs<DDB, Table, O, Key>
): UpdateItemQueryBuilderInterface<DDB, Table, O> {
const eB = new ExpressionBuilder<DDB, Table, O>({
node: { ...this.#props.node.conditionExpression },
});

const expressionNode = eB.orExpression(...args)._getNode();

return new UpdateItemQueryBuilder<DDB, Table, O>({
...this.#props,
node: {
...this.#props.node,
conditionExpression: expressionNode,
},
});
}

returnValues(
option: ReturnValuesOptions
): UpdateItemQueryBuilderInterface<DDB, Table, O> {
return new UpdateItemQueryBuilder<DDB, Table, O>({
...this.#props,
node: {
...this.#props.node,
returnValues: {
kind: "ReturnValuesNode",
option,
},
},
});
}

compile = (): UpdateCommand => {
return this.#props.queryCompiler.compile(this.#props.node);
};

execute = async (): Promise<ExecuteOutput<O>[] | undefined> => {
const putCommand = this.compile();
const data = await this.#props.ddbClient.send(putCommand);
return data.Attributes as any;
};
}

preventAwait(
UpdateItemQueryBuilder,
"Don't await UpdateItemQueryBuilder instances directly. To execute the query you need to call the `execute` method"
);

interface UpdateItemQueryBuilderProps {
readonly node: UpdateNode;
readonly ddbClient: DynamoDBDocumentClient;
readonly queryCompiler: QueryCompiler;
}
4 changes: 4 additions & 0 deletions src/queryCompiler/queryCompiler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe("QueryQueryBuilder", () => {

expect(data).toMatchSnapshot();
});

it("getItemQueryBuilder can be compiled", async () => {
const data = await tsynamoClient
.getItem("myTable")
Expand All @@ -48,6 +49,7 @@ describe("QueryQueryBuilder", () => {

expect(data).toMatchSnapshot();
});

it("deleteItemQueryBuilder can be compiled", async () => {
const data = await tsynamoClient
.deleteItem("myTable")
Expand All @@ -60,4 +62,6 @@ describe("QueryQueryBuilder", () => {

expect(data).toMatchSnapshot();
});

it.todo("updateItemQueryBuilder can be compiled");
});
14 changes: 13 additions & 1 deletion src/queryCompiler/queryCompiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
GetCommand,
PutCommand,
QueryCommand,
UpdateCommand,
} from "@aws-sdk/lib-dynamodb";
import { ExpressionJoinTypeNode } from "../nodes/expressionJoinTypeNode";
import { ExpressionNode } from "../nodes/expressionNode";
Expand All @@ -17,13 +18,15 @@ import {
import { AttributesNode } from "../nodes/attributesNode";
import { PutNode } from "../nodes/putNode";
import { DeleteNode } from "../nodes/deleteNode";
import { UpdateNode } from "../nodes/updateNode";

export class QueryCompiler {
compile(rootNode: QueryNode): QueryCommand;
compile(rootNode: GetNode): GetCommand;
compile(rootNode: PutNode): PutCommand;
compile(rootNode: DeleteNode): DeleteCommand;
compile(rootNode: QueryNode | GetNode | PutNode | DeleteNode) {
compile(rootNode: UpdateNode): UpdateCommand;
compile(rootNode: QueryNode | GetNode | PutNode | DeleteNode | UpdateNode) {
switch (rootNode.kind) {
case "GetNode":
return this.compileGetNode(rootNode);
Expand All @@ -33,6 +36,8 @@ export class QueryCompiler {
return this.compilePutNode(rootNode);
case "DeleteNode":
return this.compileDeleteNode(rootNode);
case "UpdateNode":
return this.compileUpdateNode(rootNode);
}
}

Expand Down Expand Up @@ -192,6 +197,13 @@ export class QueryCompiler {
});
}

compileUpdateNode(updateNode: UpdateNode) {
return new UpdateCommand({
TableName: "TODO",
Key: {},
});
}

compileAttributeNamesNode(node?: AttributesNode) {
const ProjectionExpression = node?.attributes
.map((att) => getExpressionAttributeNameFrom(att))
Expand Down
27 changes: 27 additions & 0 deletions src/queryCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
QueryQueryBuilderInterface,
} from "./queryBuilders/queryQueryBuilder";
import { QueryCompiler } from "./queryCompiler";
import { UpdateItemQueryBuilder } from "./queryBuilders/updateItemQueryBuilder";

export class QueryCreator<DDB> {
readonly #props: QueryCreatorProps;
Expand Down Expand Up @@ -119,6 +120,32 @@ export class QueryCreator<DDB> {
queryCompiler: this.#props.queryCompiler,
});
}

/**
*
* @param table Table to perform the update item command to
*
* @see https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/UpdateItemCommand/
*/
updateItem<Table extends keyof DDB & string>(
table: Table
): UpdateItemQueryBuilder<DDB, Table, DDB[Table]> {
return new UpdateItemQueryBuilder<DDB, Table, DDB[Table]>({
node: {
kind: "UpdateNode",
table: {
kind: "TableNode",
table,
},
conditionExpression: {
kind: "ExpressionNode",
expressions: [],
},
},
ddbClient: this.#props.ddbClient,
queryCompiler: this.#props.queryCompiler,
});
}
}

export interface QueryCreatorProps {
Expand Down

0 comments on commit 0b813d3

Please sign in to comment.