@lafken/dynamo 0.14.3 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -4
- package/lib/service/query-builder/base/base.d.ts +9 -0
- package/lib/service/query-builder/base/base.js +10 -1
- package/lib/service/repository/repository.d.ts +2 -2
- package/lib/service/repository/repository.js +2 -2
- package/lib/service/repository/repository.types.d.ts +12 -0
- package/lib/service/transaction/index.d.ts +2 -2
- package/lib/service/transaction/index.js +2 -2
- package/lib/service/transaction/transaction-get/index.d.ts +2 -0
- package/lib/service/transaction/transaction-get/index.js +18 -0
- package/lib/service/transaction/transaction-get/transaction-get.d.ts +22 -0
- package/lib/service/transaction/transaction-get/transaction-get.js +50 -0
- package/lib/service/transaction/transaction-get/transaction-get.types.d.ts +13 -0
- package/lib/service/transaction/transaction-write/index.d.ts +2 -0
- package/lib/service/transaction/transaction-write/index.js +18 -0
- package/lib/service/transaction/{transaction.d.ts → transaction-write/transaction-write.d.ts} +9 -4
- package/lib/service/transaction/{transaction.js → transaction-write/transaction-write.js} +20 -11
- package/lib/service/transaction/transaction-write/transaction-write.types.d.ts +5 -0
- package/lib/service/transaction/transaction-write/transaction-write.types.js +2 -0
- package/lib/service/transaction/transaction.utils.d.ts +13 -0
- package/lib/service/transaction/transaction.utils.js +21 -0
- package/package.json +6 -6
- package/lib/service/transaction/transaction.types.d.ts +0 -5
- /package/{LICENCE → LICENSE} +0 -0
- /package/lib/service/transaction/{transaction.types.js → transaction-get/transaction-get.types.js} +0 -0
package/README.md
CHANGED
|
@@ -459,14 +459,32 @@ const result = await orderRepository
|
|
|
459
459
|
|
|
460
460
|
If `indexName` is omitted, the repository automatically selects the best matching index based on the key condition attributes.
|
|
461
461
|
|
|
462
|
+
#### Custom Client
|
|
463
|
+
|
|
464
|
+
By default every repository shares a `DynamoDBClient` built from the ambient AWS SDK configuration (the region, credentials and endpoint the SDK resolves from the environment). Pass a `client` to reach a table on a different region, account or endpoint, such as a local DynamoDB instance during development:
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
468
|
+
import { createRepository } from '@lafken/dynamo/service';
|
|
469
|
+
|
|
470
|
+
const client = new DynamoDBClient({
|
|
471
|
+
endpoint: 'http://localhost:8000',
|
|
472
|
+
region: 'us-east-1',
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
export const contactRepository = createRepository(Contact, { client });
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Reuse the same instance across your models instead of creating one per repository, so they share a single connection pool.
|
|
479
|
+
|
|
462
480
|
### Transactions
|
|
463
481
|
|
|
464
|
-
|
|
482
|
+
`transactionWrite` groups multiple write operations (create, update, upsert, delete) into an atomic transaction. All operations succeed or fail together:
|
|
465
483
|
|
|
466
484
|
```typescript
|
|
467
|
-
import {
|
|
485
|
+
import { transactionWrite } from '@lafken/dynamo/service';
|
|
468
486
|
|
|
469
|
-
await
|
|
487
|
+
await transactionWrite([
|
|
470
488
|
contactRepository.create({
|
|
471
489
|
email: 'new@example.com',
|
|
472
490
|
company: 'Acme',
|
|
@@ -485,7 +503,29 @@ await transaction([
|
|
|
485
503
|
```
|
|
486
504
|
|
|
487
505
|
> [!NOTE]
|
|
488
|
-
> Transaction builders are passed without calling `.exec()` — the `
|
|
506
|
+
> Transaction builders are passed without calling `.exec()` — the `transactionWrite` function handles execution internally.
|
|
507
|
+
|
|
508
|
+
The transaction is sent with the client of the repositories that created the builders, so every repository taking part in it must share the same client instance. A transaction is a single request and cannot be split across connections: mixing clients throws before anything is sent.
|
|
509
|
+
|
|
510
|
+
#### Transactional Reads
|
|
511
|
+
|
|
512
|
+
`transactionGet` reads several items atomically, from any number of tables, so all of them come from the same consistent snapshot. It accepts **only `getItem` queries** — any other builder throws — and resolves the items in the same order they were requested, typed by position:
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
import { transactionGet } from '@lafken/dynamo/service';
|
|
516
|
+
|
|
517
|
+
const [contact, order] = await transactionGet([
|
|
518
|
+
contactRepository.getItem({ email: 'jane@example.com', company: 'Acme' }),
|
|
519
|
+
orderRepository.getItem({ customerId: 'cust-1', orderId: 'ord-1' }),
|
|
520
|
+
]);
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
`contact` is `Contact | undefined` and `order` is `Order | undefined`: an item that does not exist resolves as `undefined` in its position.
|
|
524
|
+
|
|
525
|
+
Unlike `batchGet`, which splits its keys into several requests over a single table, this is one request and cannot be chunked — DynamoDB limits it to 100 items.
|
|
526
|
+
|
|
527
|
+
> [!NOTE]
|
|
528
|
+
> `consistentRead` and `cacheTtl` do not apply to the queries of a read transaction: it is already strongly consistent, and the in-memory cache is never read nor populated.
|
|
489
529
|
|
|
490
530
|
### Extending the Table
|
|
491
531
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
1
2
|
import type { ClassResource } from '@lafken/common';
|
|
2
3
|
import type { LocalIndex } from '../../../main';
|
|
3
4
|
import type { GlobalIndexProperty } from '../dynamo-index/dynamo-index.types';
|
|
@@ -9,6 +10,14 @@ export declare class QueryBuilderBase<E extends ClassResource> {
|
|
|
9
10
|
protected attributeNames: Record<string, string>;
|
|
10
11
|
protected attributeValues: Record<string, any>;
|
|
11
12
|
protected expressionGroupCounter: number;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the DynamoDB client used to execute this query.
|
|
15
|
+
*
|
|
16
|
+
* It is the client injected into the repository that created the builder, and it allows
|
|
17
|
+
* operations grouping several builders, like the transactions, to send their command through
|
|
18
|
+
* the same connection the builders would have used on their own.
|
|
19
|
+
*/
|
|
20
|
+
getClient(): DynamoDBClient;
|
|
12
21
|
protected getKeyConditionExpression(expression: KeyCondition<E>, index?: LocalIndex<E> | GlobalIndexProperty): string;
|
|
13
22
|
protected getFilterExpression<T>(filter: Filter<T> | OrFilter<T> | AndFilter<T>, names?: string[], union?: 'or' | 'and', counter?: number): string;
|
|
14
23
|
protected getProjectionExpression(projection?: (string | number | symbol)[] | 'ALL'): string | undefined;
|
|
@@ -5,13 +5,22 @@ const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
|
|
|
5
5
|
const base_utils_1 = require("./base.utils");
|
|
6
6
|
class QueryBuilderBase {
|
|
7
7
|
options;
|
|
8
|
-
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: ''
|
|
9
8
|
constructor(options) {
|
|
10
9
|
this.options = options;
|
|
11
10
|
}
|
|
12
11
|
attributeNames = {};
|
|
13
12
|
attributeValues = {};
|
|
14
13
|
expressionGroupCounter = 0;
|
|
14
|
+
/**
|
|
15
|
+
* Returns the DynamoDB client used to execute this query.
|
|
16
|
+
*
|
|
17
|
+
* It is the client injected into the repository that created the builder, and it allows
|
|
18
|
+
* operations grouping several builders, like the transactions, to send their command through
|
|
19
|
+
* the same connection the builders would have used on their own.
|
|
20
|
+
*/
|
|
21
|
+
getClient() {
|
|
22
|
+
return this.options.client;
|
|
23
|
+
}
|
|
15
24
|
getKeyConditionExpression(expression, index) {
|
|
16
25
|
const { partition, sort } = expression;
|
|
17
26
|
const isGlobalIndex = index?.type === 'global';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ClassResource } from '@lafken/common';
|
|
2
|
-
import type { RepositoryReturn } from './repository.types';
|
|
3
|
-
export declare const createRepository: <E extends ClassResource>(model: E) => RepositoryReturn<E>;
|
|
2
|
+
import type { RepositoryOptions, RepositoryReturn } from './repository.types';
|
|
3
|
+
export declare const createRepository: <E extends ClassResource>(model: E, options?: RepositoryOptions) => RepositoryReturn<E>;
|
|
@@ -16,10 +16,10 @@ const scan_1 = require("../query-builder/scan/scan");
|
|
|
16
16
|
const update_1 = require("../query-builder/update/update");
|
|
17
17
|
const upsert_1 = require("../query-builder/upsert/upsert");
|
|
18
18
|
const repository_utils_1 = require("./repository.utils");
|
|
19
|
-
const createRepository = (model) => {
|
|
19
|
+
const createRepository = (model, options = {}) => {
|
|
20
20
|
const { modelProps, partitionKey, sortKey, fields } = (0, repository_utils_1.getModelInformation)(model);
|
|
21
21
|
const queryBuilderProps = {
|
|
22
|
-
client: client_1.client,
|
|
22
|
+
client: options.client ?? client_1.client,
|
|
23
23
|
fields,
|
|
24
24
|
modelProps,
|
|
25
25
|
partitionKey,
|
|
@@ -15,6 +15,18 @@ import type { GetItemOptions } from '../query-builder/get-item/get-item.types';
|
|
|
15
15
|
import type { ScanBuilder } from '../query-builder/scan/scan';
|
|
16
16
|
import type { UpdateBuilder } from '../query-builder/update/update';
|
|
17
17
|
import type { UpsertBuilder } from '../query-builder/upsert/upsert';
|
|
18
|
+
export interface RepositoryOptions {
|
|
19
|
+
/**
|
|
20
|
+
* DynamoDB client used by every query of the repository.
|
|
21
|
+
*
|
|
22
|
+
* Injecting a client is the way to reach a table on a different region, account or endpoint,
|
|
23
|
+
* for example a local DynamoDB instance during development. Repositories taking part in the
|
|
24
|
+
* same transaction must share the same client instance.
|
|
25
|
+
*
|
|
26
|
+
* @default The shared client built from the ambient AWS SDK configuration.
|
|
27
|
+
*/
|
|
28
|
+
client?: DynamoDBClient;
|
|
29
|
+
}
|
|
18
30
|
export type RepositoryReturn<E extends ClassResource> = {
|
|
19
31
|
/**
|
|
20
32
|
* Queries a single item using `QueryCommand`.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './transaction';
|
|
2
|
-
export * from './transaction
|
|
1
|
+
export * from './transaction-get';
|
|
2
|
+
export * from './transaction-write';
|
|
@@ -14,5 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./transaction"), exports);
|
|
18
|
-
__exportStar(require("./transaction
|
|
17
|
+
__exportStar(require("./transaction-get"), exports);
|
|
18
|
+
__exportStar(require("./transaction-write"), exports);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./transaction-get"), exports);
|
|
18
|
+
__exportStar(require("./transaction-get.types"), exports);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { QueryGetTransactions, TransactionGetResult } from './transaction-get.types';
|
|
2
|
+
/**
|
|
3
|
+
* Reads several items atomically using `TransactGetItemsCommand`.
|
|
4
|
+
*
|
|
5
|
+
* Accepts only `getItem` builders, from any number of tables, and resolves their items in the
|
|
6
|
+
* same order they were requested, with `undefined` for the ones that do not exist. Every item
|
|
7
|
+
* is read from the same consistent snapshot, unlike `batchGet`, which splits the keys into
|
|
8
|
+
* several requests over a single table. Supports up to 100 items per transaction (DynamoDB
|
|
9
|
+
* limit).
|
|
10
|
+
*
|
|
11
|
+
* The request is sent with the client of the repositories that created the builders, so every
|
|
12
|
+
* builder must share the same client instance: a transaction is a single request and cannot be
|
|
13
|
+
* split across connections. An empty array is a no-op.
|
|
14
|
+
*
|
|
15
|
+
* `consistentRead` and `cacheTtl` do not apply here: a transactional read is already strongly
|
|
16
|
+
* consistent, and the in-memory cache is never read nor populated.
|
|
17
|
+
*
|
|
18
|
+
* @param queryBuilders - Array of `getItem` builders to include in the transaction.
|
|
19
|
+
* @throws If any query is not a `getItem`, if the builders do not share the same client, or if
|
|
20
|
+
* the transaction is rejected by DynamoDB.
|
|
21
|
+
*/
|
|
22
|
+
export declare const transactionGet: <T extends readonly QueryGetTransactions[]>(queryBuilders: [...T]) => Promise<TransactionGetResult<T>>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.transactionGet = void 0;
|
|
4
|
+
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
5
|
+
const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
|
|
6
|
+
const get_item_1 = require("../../query-builder/get-item/get-item");
|
|
7
|
+
const transaction_utils_1 = require("../transaction.utils");
|
|
8
|
+
/**
|
|
9
|
+
* Reads several items atomically using `TransactGetItemsCommand`.
|
|
10
|
+
*
|
|
11
|
+
* Accepts only `getItem` builders, from any number of tables, and resolves their items in the
|
|
12
|
+
* same order they were requested, with `undefined` for the ones that do not exist. Every item
|
|
13
|
+
* is read from the same consistent snapshot, unlike `batchGet`, which splits the keys into
|
|
14
|
+
* several requests over a single table. Supports up to 100 items per transaction (DynamoDB
|
|
15
|
+
* limit).
|
|
16
|
+
*
|
|
17
|
+
* The request is sent with the client of the repositories that created the builders, so every
|
|
18
|
+
* builder must share the same client instance: a transaction is a single request and cannot be
|
|
19
|
+
* split across connections. An empty array is a no-op.
|
|
20
|
+
*
|
|
21
|
+
* `consistentRead` and `cacheTtl` do not apply here: a transactional read is already strongly
|
|
22
|
+
* consistent, and the in-memory cache is never read nor populated.
|
|
23
|
+
*
|
|
24
|
+
* @param queryBuilders - Array of `getItem` builders to include in the transaction.
|
|
25
|
+
* @throws If any query is not a `getItem`, if the builders do not share the same client, or if
|
|
26
|
+
* the transaction is rejected by DynamoDB.
|
|
27
|
+
*/
|
|
28
|
+
const transactionGet = async (queryBuilders) => {
|
|
29
|
+
if (queryBuilders.length === 0) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
if (queryBuilders.some((builder) => !(builder instanceof get_item_1.GetItemBuilder))) {
|
|
33
|
+
throw new Error('The transaction includes a query that is not a getItem');
|
|
34
|
+
}
|
|
35
|
+
const client = (0, transaction_utils_1.getTransactionClient)(queryBuilders);
|
|
36
|
+
const command = new client_dynamodb_1.TransactGetItemsCommand({
|
|
37
|
+
TransactItems: queryBuilders.map((builder) => {
|
|
38
|
+
const { TableName, Key, ProjectionExpression, ExpressionAttributeNames } = builder.getCommand();
|
|
39
|
+
return {
|
|
40
|
+
Get: { TableName, Key, ProjectionExpression, ExpressionAttributeNames },
|
|
41
|
+
};
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
const { Responses = [] } = await client.send(command);
|
|
45
|
+
return queryBuilders.map((_, index) => {
|
|
46
|
+
const item = Responses[index]?.Item;
|
|
47
|
+
return item ? (0, util_dynamodb_1.unmarshall)(item) : undefined;
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
exports.transactionGet = transactionGet;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ClassResource } from '@lafken/common';
|
|
2
|
+
import type { GetItemBuilder } from '../../query-builder/get-item/get-item';
|
|
3
|
+
export type QueryGetTransactions = GetItemBuilder<any>;
|
|
4
|
+
/**
|
|
5
|
+
* Maps a tuple of `getItem` builders to the tuple of items they resolve.
|
|
6
|
+
*
|
|
7
|
+
* Keeps the result typed by position, so builders from different models do not collapse into
|
|
8
|
+
* a single union: `[userRepository.getItem(...), orderRepository.getItem(...)]` resolves as
|
|
9
|
+
* `[User | undefined, Order | undefined]`.
|
|
10
|
+
*/
|
|
11
|
+
export type TransactionGetResult<T extends readonly QueryGetTransactions[]> = {
|
|
12
|
+
[K in keyof T]: T[K] extends GetItemBuilder<infer E extends ClassResource> ? InstanceType<E> | undefined : never;
|
|
13
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./transaction-write"), exports);
|
|
18
|
+
__exportStar(require("./transaction-write.types"), exports);
|
package/lib/service/transaction/{transaction.d.ts → transaction-write/transaction-write.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TransactWriteItem } from '@aws-sdk/client-dynamodb';
|
|
2
|
-
import type { QueryTransactions } from './transaction.types';
|
|
2
|
+
import type { QueryTransactions } from './transaction-write.types';
|
|
3
3
|
/**
|
|
4
4
|
* Resolves the `TransactWriteItem` operation type for a given query builder.
|
|
5
5
|
*
|
|
@@ -23,8 +23,13 @@ export declare const getTransactionType: (builder: QueryTransactions) => keyof T
|
|
|
23
23
|
* carries; each builder's command is extracted via `getCommand()` and mapped to the appropriate
|
|
24
24
|
* `TransactWriteItem` operation type.
|
|
25
25
|
*
|
|
26
|
+
* The request is sent with the client of the repositories that created the builders, so every
|
|
27
|
+
* builder must share the same client instance: a transaction is a single request and cannot be
|
|
28
|
+
* split across connections. An empty array is a no-op.
|
|
29
|
+
*
|
|
26
30
|
* @param queryBuilders - Array of write builders to include in the transaction.
|
|
27
|
-
* @throws If any builder type is unsupported
|
|
28
|
-
* (e.g. a condition check fails in one of the
|
|
31
|
+
* @throws If any builder type is unsupported, if the builders do not share the same client, or
|
|
32
|
+
* if the transaction is rejected by DynamoDB (e.g. a condition check fails in one of the
|
|
33
|
+
* items).
|
|
29
34
|
*/
|
|
30
|
-
export declare const
|
|
35
|
+
export declare const transactionWrite: (queryBuilders: QueryTransactions[]) => Promise<void>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.transactionWrite = exports.getTransactionType = void 0;
|
|
4
4
|
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
5
|
+
const create_1 = require("../../query-builder/create/create");
|
|
6
|
+
const delete_1 = require("../../query-builder/delete/delete");
|
|
7
|
+
const update_1 = require("../../query-builder/update/update");
|
|
8
|
+
const upsert_1 = require("../../query-builder/upsert/upsert");
|
|
9
|
+
const transaction_utils_1 = require("../transaction.utils");
|
|
10
10
|
/**
|
|
11
11
|
* Resolves the `TransactWriteItem` operation type for a given query builder.
|
|
12
12
|
*
|
|
@@ -46,11 +46,20 @@ exports.getTransactionType = getTransactionType;
|
|
|
46
46
|
* carries; each builder's command is extracted via `getCommand()` and mapped to the appropriate
|
|
47
47
|
* `TransactWriteItem` operation type.
|
|
48
48
|
*
|
|
49
|
+
* The request is sent with the client of the repositories that created the builders, so every
|
|
50
|
+
* builder must share the same client instance: a transaction is a single request and cannot be
|
|
51
|
+
* split across connections. An empty array is a no-op.
|
|
52
|
+
*
|
|
49
53
|
* @param queryBuilders - Array of write builders to include in the transaction.
|
|
50
|
-
* @throws If any builder type is unsupported
|
|
51
|
-
* (e.g. a condition check fails in one of the
|
|
54
|
+
* @throws If any builder type is unsupported, if the builders do not share the same client, or
|
|
55
|
+
* if the transaction is rejected by DynamoDB (e.g. a condition check fails in one of the
|
|
56
|
+
* items).
|
|
52
57
|
*/
|
|
53
|
-
const
|
|
58
|
+
const transactionWrite = async (queryBuilders) => {
|
|
59
|
+
if (queryBuilders.length === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const client = (0, transaction_utils_1.getTransactionClient)(queryBuilders);
|
|
54
63
|
const transactionCommands = queryBuilders.map((builder) => {
|
|
55
64
|
return {
|
|
56
65
|
[(0, exports.getTransactionType)(builder)]: builder.getCommand(),
|
|
@@ -59,6 +68,6 @@ const transaction = async (queryBuilders) => {
|
|
|
59
68
|
const command = new client_dynamodb_1.TransactWriteItemsCommand({
|
|
60
69
|
TransactItems: transactionCommands,
|
|
61
70
|
});
|
|
62
|
-
await
|
|
71
|
+
await client.send(command);
|
|
63
72
|
};
|
|
64
|
-
exports.
|
|
73
|
+
exports.transactionWrite = transactionWrite;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CreateBuilder } from '../../query-builder/create/create';
|
|
2
|
+
import type { DeleteBuilder } from '../../query-builder/delete/delete';
|
|
3
|
+
import type { UpdateBuilder } from '../../query-builder/update/update';
|
|
4
|
+
import type { UpsertBuilder } from '../../query-builder/upsert/upsert';
|
|
5
|
+
export type QueryTransactions = CreateBuilder<any> | UpsertBuilder<any> | UpdateBuilder<any, any> | DeleteBuilder<any>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
2
|
+
import type { QueryBuilderBase } from '../query-builder/base/base';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the client shared by the queries of a transaction.
|
|
5
|
+
*
|
|
6
|
+
* A transaction is sent as a single request, so it cannot be split across connections: every
|
|
7
|
+
* builder must come from repositories using the same client instance.
|
|
8
|
+
*
|
|
9
|
+
* @param queryBuilders - Non empty list of builders taking part in the transaction.
|
|
10
|
+
* @throws If the builders do not share the same client.
|
|
11
|
+
* @returns The client used to send the transaction.
|
|
12
|
+
*/
|
|
13
|
+
export declare const getTransactionClient: (queryBuilders: readonly QueryBuilderBase<any>[]) => DynamoDBClient;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTransactionClient = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the client shared by the queries of a transaction.
|
|
6
|
+
*
|
|
7
|
+
* A transaction is sent as a single request, so it cannot be split across connections: every
|
|
8
|
+
* builder must come from repositories using the same client instance.
|
|
9
|
+
*
|
|
10
|
+
* @param queryBuilders - Non empty list of builders taking part in the transaction.
|
|
11
|
+
* @throws If the builders do not share the same client.
|
|
12
|
+
* @returns The client used to send the transaction.
|
|
13
|
+
*/
|
|
14
|
+
const getTransactionClient = (queryBuilders) => {
|
|
15
|
+
const client = queryBuilders[0].getClient();
|
|
16
|
+
if (queryBuilders.some((builder) => builder.getClient() !== client)) {
|
|
17
|
+
throw new Error('All queries in a transaction must share the same client');
|
|
18
|
+
}
|
|
19
|
+
return client;
|
|
20
|
+
};
|
|
21
|
+
exports.getTransactionClient = getTransactionClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lafken/dynamo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Define DynamoDB tables using TypeScript decorators - type-safe, declarative infrastructure with Lafken",
|
|
6
6
|
"keywords": [
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@aws-sdk/client-dynamodb": "^3.1106.0",
|
|
60
60
|
"@aws-sdk/util-dynamodb": "^3.996.7",
|
|
61
61
|
"reflect-metadata": "^0.2.2",
|
|
62
|
-
"@lafken/resolver": "0.
|
|
62
|
+
"@lafken/resolver": "0.15.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@cdktn/provider-aws": "^25.0.0",
|
|
@@ -68,21 +68,21 @@
|
|
|
68
68
|
"@vitest/runner": "^4.1.10",
|
|
69
69
|
"aws-sdk-client-mock": "^4.1.0",
|
|
70
70
|
"cdktn": "^0.24.0",
|
|
71
|
-
"cdktn
|
|
71
|
+
"@cdktn/vitest": "^0.1.0",
|
|
72
72
|
"constructs": "^10.8.0",
|
|
73
73
|
"typescript": "7.0.2",
|
|
74
74
|
"unplugin-swc": "^1.5.10",
|
|
75
75
|
"vitest": "^4.1.10",
|
|
76
|
-
"@lafken/common": "0.
|
|
76
|
+
"@lafken/common": "0.15.0"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
79
|
"@cdktn/provider-aws": ">=23.0.0",
|
|
80
80
|
"cdktn": ">=0.22.0",
|
|
81
81
|
"constructs": ">=10.7.0",
|
|
82
|
-
"@lafken/common": "0.
|
|
82
|
+
"@lafken/common": "0.15.0"
|
|
83
83
|
},
|
|
84
84
|
"engines": {
|
|
85
|
-
"node": ">=
|
|
85
|
+
"node": ">=22.13"
|
|
86
86
|
},
|
|
87
87
|
"publishConfig": {
|
|
88
88
|
"access": "public"
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { CreateBuilder } from '../query-builder/create/create';
|
|
2
|
-
import type { DeleteBuilder } from '../query-builder/delete/delete';
|
|
3
|
-
import type { UpdateBuilder } from '../query-builder/update/update';
|
|
4
|
-
import type { UpsertBuilder } from '../query-builder/upsert/upsert';
|
|
5
|
-
export type QueryTransactions = CreateBuilder<any> | UpsertBuilder<any> | UpdateBuilder<any, any> | DeleteBuilder<any>;
|
/package/{LICENCE → LICENSE}
RENAMED
|
File without changes
|
/package/lib/service/transaction/{transaction.types.js → transaction-get/transaction-get.types.js}
RENAMED
|
File without changes
|