@moicky/dynamodb 1.1.2 → 1.2.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/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/lib/client.d.ts +28 -1
- package/dist/lib/client.js +74 -7
- package/dist/lib/helpers.d.ts +3 -1
- package/dist/lib/helpers.js +7 -3
- package/dist/operations/delete.d.ts +3 -1
- package/dist/operations/delete.js +7 -6
- package/dist/operations/get.d.ts +4 -2
- package/dist/operations/get.js +12 -10
- package/dist/operations/misc.d.ts +3 -3
- package/dist/operations/misc.js +19 -17
- package/dist/operations/put.d.ts +5 -3
- package/dist/operations/put.js +14 -11
- package/dist/operations/query.js +1 -1
- package/dist/operations/update.d.ts +2 -2
- package/dist/operations/update.js +23 -13
- package/package.json +1 -1
- package/readme.md +200 -8
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -14,6 +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("./operations"), exports);
|
|
18
17
|
__exportStar(require("./lib/client"), exports);
|
|
19
|
-
__exportStar(require("./
|
|
18
|
+
__exportStar(require("./operations"), exports);
|
package/dist/lib/client.d.ts
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
1
|
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
2
|
+
declare interface KeySchema {
|
|
3
|
+
hash: string;
|
|
4
|
+
range?: string;
|
|
5
|
+
}
|
|
6
|
+
declare interface KeySchemaCollection {
|
|
7
|
+
[tableName: string]: KeySchema;
|
|
8
|
+
}
|
|
9
|
+
declare class DynamoDBConfig {
|
|
10
|
+
#private;
|
|
11
|
+
client: DynamoDBClient;
|
|
12
|
+
tablesSchema: KeySchemaCollection;
|
|
13
|
+
constructor({ region }?: {
|
|
14
|
+
region?: string;
|
|
15
|
+
});
|
|
16
|
+
initSchema(schema: KeySchemaCollection): KeySchemaCollection;
|
|
17
|
+
getDefaultTable(): string;
|
|
18
|
+
getTableSchema(tableName?: string): KeySchema;
|
|
19
|
+
getDefaultTableSchema(): KeySchema;
|
|
20
|
+
validateSchema(schema: KeySchemaCollection): boolean;
|
|
21
|
+
destroy(): void;
|
|
22
|
+
}
|
|
23
|
+
declare const config: DynamoDBConfig;
|
|
2
24
|
export declare const client: DynamoDBClient;
|
|
3
|
-
export declare const
|
|
25
|
+
export declare const getDefaultTable: () => string;
|
|
26
|
+
export declare const getTableSchema: (tableName?: string) => KeySchema;
|
|
27
|
+
export declare const getDefaultTableSchema: () => KeySchema;
|
|
28
|
+
export declare const initSchema: (schema: KeySchemaCollection) => KeySchemaCollection;
|
|
29
|
+
export declare const destroy: () => void;
|
|
30
|
+
export default config;
|
package/dist/lib/client.js
CHANGED
|
@@ -1,11 +1,78 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.destroy = exports.initSchema = exports.getDefaultTableSchema = exports.getTableSchema = exports.getDefaultTable = exports.client = void 0;
|
|
4
4
|
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
class DynamoDBConfig {
|
|
6
|
+
client;
|
|
7
|
+
tablesSchema = {};
|
|
8
|
+
#initialized = false;
|
|
9
|
+
constructor({ region } = {}) {
|
|
10
|
+
this.client = new client_dynamodb_1.DynamoDBClient({
|
|
11
|
+
region: region || process.env.AWS_REGION || "eu-central-1",
|
|
12
|
+
});
|
|
13
|
+
const defaultTable = process.env.DYNAMODB_TABLE;
|
|
14
|
+
if (defaultTable) {
|
|
15
|
+
this.tablesSchema[defaultTable] = {
|
|
16
|
+
hash: "PK",
|
|
17
|
+
range: "SK",
|
|
18
|
+
};
|
|
19
|
+
this.#initialized = true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
initSchema(schema) {
|
|
23
|
+
if (!this.validateSchema(schema)) {
|
|
24
|
+
throw new Error("Invalid schema");
|
|
25
|
+
}
|
|
26
|
+
this.tablesSchema = Object.keys(schema).reduce((acc, table) => {
|
|
27
|
+
const { hash, range } = schema[table];
|
|
28
|
+
acc[table] = {
|
|
29
|
+
hash: hash + "",
|
|
30
|
+
...(range && { range: range + "" }),
|
|
31
|
+
};
|
|
32
|
+
return acc;
|
|
33
|
+
}, {});
|
|
34
|
+
this.#initialized = true;
|
|
35
|
+
return this.tablesSchema;
|
|
36
|
+
}
|
|
37
|
+
getDefaultTable() {
|
|
38
|
+
if (!this.#initialized) {
|
|
39
|
+
throw new Error("Schema not initialized");
|
|
40
|
+
}
|
|
41
|
+
const table = Object.keys(this.tablesSchema)[0];
|
|
42
|
+
return table;
|
|
43
|
+
}
|
|
44
|
+
getTableSchema(tableName) {
|
|
45
|
+
if (!this.#initialized) {
|
|
46
|
+
throw new Error("Schema not initialized");
|
|
47
|
+
}
|
|
48
|
+
const table = tableName || this.getDefaultTable();
|
|
49
|
+
return this.tablesSchema[table] || this.getDefaultTableSchema();
|
|
50
|
+
}
|
|
51
|
+
getDefaultTableSchema() {
|
|
52
|
+
return { hash: "PK", range: "SK" };
|
|
53
|
+
}
|
|
54
|
+
validateSchema(schema) {
|
|
55
|
+
const tables = Object.keys(schema);
|
|
56
|
+
if (tables.length === 0) {
|
|
57
|
+
throw new Error("No tables provided");
|
|
58
|
+
}
|
|
59
|
+
tables.forEach((table) => {
|
|
60
|
+
const { hash } = schema[table];
|
|
61
|
+
if (!hash) {
|
|
62
|
+
throw new Error(`No hash key provided for table ${table}`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
destroy() {
|
|
68
|
+
this.client.destroy();
|
|
69
|
+
}
|
|
11
70
|
}
|
|
71
|
+
const config = new DynamoDBConfig();
|
|
72
|
+
exports.client = config.client;
|
|
73
|
+
exports.getDefaultTable = config.getDefaultTable.bind(config);
|
|
74
|
+
exports.getTableSchema = config.getTableSchema.bind(config);
|
|
75
|
+
exports.getDefaultTableSchema = config.getDefaultTableSchema.bind(config);
|
|
76
|
+
exports.initSchema = config.initSchema.bind(config);
|
|
77
|
+
exports.destroy = config.destroy.bind(config);
|
|
78
|
+
exports.default = config;
|
package/dist/lib/helpers.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export declare function stripKey(key: any
|
|
1
|
+
export declare function stripKey(key: any, args?: {
|
|
2
|
+
TableName?: string;
|
|
3
|
+
}): Record<string, any>;
|
|
2
4
|
export declare function splitEvery<T>(items: T[], limit?: number): T[][];
|
|
3
5
|
export declare function getAttributeValues(key: any, attributesToGet?: string[]): Record<string, any>;
|
|
4
6
|
export declare function getAttributeNames(key: any, attributesToGet?: string[]): Record<string, string>;
|
package/dist/lib/helpers.js
CHANGED
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getAttributesFromExpression = exports.getAttributeNames = exports.getAttributeValues = exports.splitEvery = exports.stripKey = void 0;
|
|
4
4
|
const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
|
|
5
|
-
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
6
|
// Since dynamo only accepts key atrtributes which are described in table schema
|
|
7
7
|
// we remove any other attributes from the item if they are present and passed as
|
|
8
8
|
// key parameter to any of the functions below (getItem, updateItem, deleteItem...)
|
|
9
|
-
function stripKey(key) {
|
|
10
|
-
|
|
9
|
+
function stripKey(key, args) {
|
|
10
|
+
const { hash, range } = (0, client_1.getTableSchema)(args?.TableName);
|
|
11
|
+
return (0, util_dynamodb_1.marshall)({
|
|
12
|
+
[hash]: key[hash],
|
|
13
|
+
...(range && { [range]: key[range] }),
|
|
14
|
+
});
|
|
11
15
|
}
|
|
12
16
|
exports.stripKey = stripKey;
|
|
13
17
|
function splitEvery(items, limit = 25) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { BatchWriteItemCommandInput, DeleteItemCommandInput, DeleteItemCommandOutput } from "@aws-sdk/client-dynamodb";
|
|
2
2
|
export declare function deleteItem(key: any, args?: Partial<DeleteItemCommandInput>): Promise<DeleteItemCommandOutput>;
|
|
3
|
-
export declare function deleteItems(keys: any[], args?: Partial<BatchWriteItemCommandInput
|
|
3
|
+
export declare function deleteItems(keys: any[], args?: Partial<BatchWriteItemCommandInput & {
|
|
4
|
+
TableName?: string;
|
|
5
|
+
}>, retry?: number): Promise<void>;
|
|
@@ -6,15 +6,15 @@ const client_1 = require("../lib/client");
|
|
|
6
6
|
const helpers_1 = require("../lib/helpers");
|
|
7
7
|
async function deleteItem(key, args = {}) {
|
|
8
8
|
return client_1.client.send(new client_dynamodb_1.DeleteItemCommand({
|
|
9
|
-
|
|
10
|
-
Key: (0, helpers_1.stripKey)(key),
|
|
9
|
+
Key: (0, helpers_1.stripKey)(key, args),
|
|
11
10
|
...args,
|
|
11
|
+
TableName: args?.TableName || (0, client_1.getDefaultTable)(),
|
|
12
12
|
}));
|
|
13
13
|
}
|
|
14
14
|
exports.deleteItem = deleteItem;
|
|
15
15
|
async function deleteItems(keys, args = {}, retry = 0) {
|
|
16
16
|
const uniqueKeys = Object.values(keys.reduce((acc, key) => {
|
|
17
|
-
const strippedKey = (0, helpers_1.stripKey)(key);
|
|
17
|
+
const strippedKey = (0, helpers_1.stripKey)(key, args);
|
|
18
18
|
const keyString = JSON.stringify(strippedKey);
|
|
19
19
|
if (!acc[keyString]) {
|
|
20
20
|
acc[keyString] = strippedKey;
|
|
@@ -25,11 +25,12 @@ async function deleteItems(keys, args = {}, retry = 0) {
|
|
|
25
25
|
const batches = (0, helpers_1.splitEvery)(uniqueKeys);
|
|
26
26
|
if (retry > 3)
|
|
27
27
|
return;
|
|
28
|
+
const table = args?.TableName || (0, client_1.getDefaultTable)();
|
|
28
29
|
for (const batch of batches) {
|
|
29
30
|
await client_1.client
|
|
30
31
|
.send(new client_dynamodb_1.BatchWriteItemCommand({
|
|
31
32
|
RequestItems: {
|
|
32
|
-
[
|
|
33
|
+
[table]: batch.map((Key) => ({
|
|
33
34
|
DeleteRequest: {
|
|
34
35
|
Key,
|
|
35
36
|
},
|
|
@@ -38,10 +39,10 @@ async function deleteItems(keys, args = {}, retry = 0) {
|
|
|
38
39
|
...args,
|
|
39
40
|
}))
|
|
40
41
|
.then((res) => {
|
|
41
|
-
if (res?.UnprocessedItems?.[
|
|
42
|
+
if (res?.UnprocessedItems?.[table]?.length) {
|
|
42
43
|
if (retry + 1 > 3)
|
|
43
44
|
return reject(res);
|
|
44
|
-
return deleteItems(res.UnprocessedItems[
|
|
45
|
+
return deleteItems(res.UnprocessedItems[table], args, retry + 1);
|
|
45
46
|
}
|
|
46
47
|
})
|
|
47
48
|
.catch(reject);
|
package/dist/operations/get.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { BatchGetItemCommandInput, GetItemCommandInput, ScanCommandInput } from "@aws-sdk/client-dynamodb";
|
|
2
|
-
export declare function getItem(key: any, args?: Partial<GetItemCommandInput>): Promise<Record<string, any
|
|
3
|
-
export declare function getItems(keys: any[], args?: Partial<BatchGetItemCommandInput
|
|
2
|
+
export declare function getItem(key: any, args?: Partial<GetItemCommandInput>): Promise<Record<string, any> | undefined>;
|
|
3
|
+
export declare function getItems(keys: any[], args?: Partial<BatchGetItemCommandInput & {
|
|
4
|
+
TableName?: string;
|
|
5
|
+
}>, retry?: number): Promise<Record<string, any>[]>;
|
|
4
6
|
export declare function getAllItems(args?: Partial<ScanCommandInput>): Promise<Record<string, any>[]>;
|
package/dist/operations/get.js
CHANGED
|
@@ -8,9 +8,9 @@ const helpers_1 = require("../lib/helpers");
|
|
|
8
8
|
async function getItem(key, args = {}) {
|
|
9
9
|
return client_1.client
|
|
10
10
|
.send(new client_dynamodb_1.GetItemCommand({
|
|
11
|
-
|
|
12
|
-
Key: (0, helpers_1.stripKey)(key),
|
|
11
|
+
Key: (0, helpers_1.stripKey)(key, args),
|
|
13
12
|
...args,
|
|
13
|
+
TableName: args?.TableName || (0, client_1.getDefaultTable)(),
|
|
14
14
|
}))
|
|
15
15
|
.then((res) => res?.Item && (0, util_dynamodb_1.unmarshall)(res.Item));
|
|
16
16
|
}
|
|
@@ -24,7 +24,7 @@ async function getItems(keys, args = {}, retry = 0) {
|
|
|
24
24
|
const batchReadLimit = 100;
|
|
25
25
|
// duplicate key entries would cause an error, so we remove them
|
|
26
26
|
const uniqueKeys = Object.values(keys.reduce((acc, key) => {
|
|
27
|
-
const strippedKey = (0, helpers_1.stripKey)(key);
|
|
27
|
+
const strippedKey = (0, helpers_1.stripKey)(key, args);
|
|
28
28
|
const keyString = JSON.stringify(strippedKey);
|
|
29
29
|
if (!acc[keyString]) {
|
|
30
30
|
acc[keyString] = strippedKey;
|
|
@@ -36,21 +36,23 @@ async function getItems(keys, args = {}, retry = 0) {
|
|
|
36
36
|
if (retry > 2) {
|
|
37
37
|
return results;
|
|
38
38
|
}
|
|
39
|
+
const TableName = args?.TableName || (0, client_1.getDefaultTable)();
|
|
40
|
+
delete args.TableName;
|
|
39
41
|
await Promise.all(batches.map(async (batch) => {
|
|
40
42
|
await client_1.client
|
|
41
43
|
.send(new client_dynamodb_1.BatchGetItemCommand({
|
|
42
44
|
RequestItems: {
|
|
43
|
-
[
|
|
45
|
+
[TableName]: {
|
|
44
46
|
Keys: batch,
|
|
45
47
|
...args,
|
|
46
48
|
},
|
|
47
49
|
},
|
|
48
50
|
}))
|
|
49
51
|
.then((res) => {
|
|
50
|
-
const unprocessed = res?.UnprocessedKeys?.[
|
|
51
|
-
const allItemsFromBatch = res?.Responses?.[
|
|
52
|
+
const unprocessed = res?.UnprocessedKeys?.[TableName];
|
|
53
|
+
const allItemsFromBatch = res?.Responses?.[TableName] || [];
|
|
52
54
|
if (unprocessed) {
|
|
53
|
-
return getItems(unprocessed.Keys, args, retry + 1).then((items) => allItemsFromBatch.concat(items));
|
|
55
|
+
return getItems(unprocessed.Keys, { ...args, TableName }, retry + 1).then((items) => allItemsFromBatch.concat(items));
|
|
54
56
|
}
|
|
55
57
|
return allItemsFromBatch.map((item) => item && (0, util_dynamodb_1.unmarshall)(item));
|
|
56
58
|
})
|
|
@@ -59,18 +61,18 @@ async function getItems(keys, args = {}, retry = 0) {
|
|
|
59
61
|
const resultItems = results
|
|
60
62
|
.filter((i) => i)
|
|
61
63
|
.reduce((acc, item) => {
|
|
62
|
-
const keyString = JSON.stringify((0, helpers_1.stripKey)(item));
|
|
64
|
+
const keyString = JSON.stringify((0, helpers_1.stripKey)(item, { TableName }));
|
|
63
65
|
acc[keyString] = item;
|
|
64
66
|
return acc;
|
|
65
67
|
}, {});
|
|
66
|
-
return keys.map((key) => resultItems[JSON.stringify((0, helpers_1.stripKey)(key))] || undefined);
|
|
68
|
+
return keys.map((key) => resultItems[JSON.stringify((0, helpers_1.stripKey)(key, { TableName }))] || undefined);
|
|
67
69
|
}
|
|
68
70
|
exports.getItems = getItems;
|
|
69
71
|
async function getAllItems(args = {}) {
|
|
70
72
|
return client_1.client
|
|
71
73
|
.send(new client_dynamodb_1.ScanCommand({
|
|
72
|
-
TableName: client_1.TableName,
|
|
73
74
|
...args,
|
|
75
|
+
TableName: args?.TableName || (0, client_1.getDefaultTable)(),
|
|
74
76
|
}))
|
|
75
77
|
.then((res) => res.Items.map((item) => item && (0, util_dynamodb_1.unmarshall)(item)));
|
|
76
78
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GetItemCommandInput } from "@aws-sdk/client-dynamodb";
|
|
2
2
|
export declare function itemExists(key: any, args?: Partial<GetItemCommandInput>): Promise<boolean>;
|
|
3
|
-
export declare function
|
|
4
|
-
PK: string;
|
|
5
|
-
SK?: string;
|
|
3
|
+
export declare function getAscendingId({ length, TableName, ...keySchema }: {
|
|
6
4
|
length?: number;
|
|
5
|
+
TableName?: string;
|
|
6
|
+
[keySchema: string]: any;
|
|
7
7
|
}): Promise<string>;
|
package/dist/operations/misc.js
CHANGED
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getAscendingId = exports.itemExists = void 0;
|
|
4
|
+
const client_1 = require("../lib/client");
|
|
4
5
|
const get_1 = require("./get");
|
|
5
6
|
const query_1 = require("./query");
|
|
6
7
|
async function itemExists(key, args = {}) {
|
|
7
|
-
|
|
8
|
-
return item !== undefined && item !== null;
|
|
8
|
+
return !!(await (0, get_1.getItem)(key, args));
|
|
9
9
|
}
|
|
10
10
|
exports.itemExists = itemExists;
|
|
11
|
-
async function
|
|
12
|
-
// Assumes that you are
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
async function getAscendingId({ length = 8, TableName, ...keySchema }) {
|
|
12
|
+
// Assumes that you are the incrementing ID inside or as the keySchema range key
|
|
13
|
+
const table = TableName || (0, client_1.getDefaultTable)();
|
|
14
|
+
const { hash, range } = (0, client_1.getTableSchema)(table);
|
|
15
|
+
const keySchemaHash = keySchema[hash];
|
|
16
|
+
const keySchemaRange = keySchema[range];
|
|
17
|
+
if (!keySchemaHash) {
|
|
18
|
+
throw new Error(`Cannot generate new ID: keySchemaHash is missing, expected '${hash}'`);
|
|
15
19
|
}
|
|
16
20
|
let lastId = "0";
|
|
17
|
-
if (!
|
|
18
|
-
const lastItem = (await (0, query_1.queryItems)(
|
|
19
|
-
const parts = lastItem?.[
|
|
21
|
+
if (!keySchemaRange) {
|
|
22
|
+
const lastItem = (await (0, query_1.queryItems)(`#${hash} = :${hash}`, { [hash]: keySchemaHash }, { Limit: 1, ScanIndexForward: false, TableName: table }))?.[0];
|
|
23
|
+
const parts = lastItem?.[range]?.split("/") || [];
|
|
20
24
|
lastId = parts?.[parts.length - 1] || "0";
|
|
21
25
|
}
|
|
22
26
|
else {
|
|
23
|
-
const formattedSK =
|
|
24
|
-
const lastItem = (await (0, query_1.queryItems)(
|
|
25
|
-
const parts = lastItem?.[
|
|
27
|
+
const formattedSK = keySchemaRange + (!keySchemaRange.endsWith("/") ? "/" : "");
|
|
28
|
+
const lastItem = (await (0, query_1.queryItems)(`#${hash} = :${hash} and begins_with(#${range}, :${range})`, { [hash]: keySchemaHash, [range]: formattedSK }, { Limit: 1, ScanIndexForward: false, TableName: table }))?.[0];
|
|
29
|
+
const parts = lastItem?.[range]?.split("/") || [];
|
|
26
30
|
lastId = parts?.[formattedSK.split("/").length - 1] || "0";
|
|
27
31
|
}
|
|
28
32
|
const newId = parseInt(lastId) + 1 + "";
|
|
29
33
|
const withPadding = newId.padStart(length || 0, "0");
|
|
30
|
-
return
|
|
31
|
-
? `${SK}${!SK.endsWith("/") ? "/" : ""}${withPadding}`
|
|
32
|
-
: withPadding;
|
|
34
|
+
return withPadding;
|
|
33
35
|
}
|
|
34
|
-
exports.
|
|
36
|
+
exports.getAscendingId = getAscendingId;
|
package/dist/operations/put.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import { BatchWriteItemCommandInput, PutItemCommandInput, PutItemCommandOutput } from "@aws-sdk/client-dynamodb";
|
|
2
|
-
export declare function putItem(data: any, args?: Partial<PutItemCommandInput>): Promise<PutItemCommandOutput
|
|
3
|
-
export declare function putItems(items: any[], args?: Partial<BatchWriteItemCommandInput
|
|
1
|
+
import { BatchWriteItemCommandInput, BatchWriteItemCommandOutput, PutItemCommandInput, PutItemCommandOutput } from "@aws-sdk/client-dynamodb";
|
|
2
|
+
export declare function putItem(data: any, args?: Partial<PutItemCommandInput>): Promise<PutItemCommandOutput | Record<string, any>>;
|
|
3
|
+
export declare function putItems(items: any[], args?: Partial<BatchWriteItemCommandInput & {
|
|
4
|
+
TableName?: string;
|
|
5
|
+
}>): Promise<BatchWriteItemCommandOutput[]>;
|
package/dist/operations/put.js
CHANGED
|
@@ -9,27 +9,29 @@ async function putItem(data, args = {}) {
|
|
|
9
9
|
if (!Object.keys(data).includes("createdAt")) {
|
|
10
10
|
data.createdAt = Date.now();
|
|
11
11
|
}
|
|
12
|
-
return client_1.client
|
|
13
|
-
|
|
12
|
+
return client_1.client
|
|
13
|
+
.send(new client_dynamodb_1.PutItemCommand({
|
|
14
14
|
Item: (0, util_dynamodb_1.marshall)(data),
|
|
15
15
|
...args,
|
|
16
|
-
|
|
16
|
+
TableName: args?.TableName || (0, client_1.getDefaultTable)(),
|
|
17
|
+
}))
|
|
18
|
+
.then((res) => (args?.ReturnValues ? (0, util_dynamodb_1.unmarshall)(res?.Attributes) : res));
|
|
17
19
|
}
|
|
18
20
|
exports.putItem = putItem;
|
|
19
21
|
async function putItems(items, args = {}) {
|
|
20
22
|
return new Promise(async (resolve, reject) => {
|
|
21
|
-
const batches = (0, helpers_1.splitEvery)(items);
|
|
22
23
|
const now = Date.now();
|
|
24
|
+
const batches = (0, helpers_1.splitEvery)(items.map((item) => ({
|
|
25
|
+
...item,
|
|
26
|
+
createdAt: item?.createdAt ?? now,
|
|
27
|
+
})));
|
|
28
|
+
const results = [];
|
|
29
|
+
const table = args?.TableName || (0, client_1.getDefaultTable)();
|
|
23
30
|
for (const batch of batches) {
|
|
24
|
-
batch.forEach((item) => {
|
|
25
|
-
if (!Object.keys(item).includes("createdAt")) {
|
|
26
|
-
item.createdAt = now;
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
31
|
await client_1.client
|
|
30
32
|
.send(new client_dynamodb_1.BatchWriteItemCommand({
|
|
31
33
|
RequestItems: {
|
|
32
|
-
[
|
|
34
|
+
[table]: batch.map((item) => ({
|
|
33
35
|
PutRequest: {
|
|
34
36
|
Item: (0, util_dynamodb_1.marshall)(item),
|
|
35
37
|
},
|
|
@@ -37,9 +39,10 @@ async function putItems(items, args = {}) {
|
|
|
37
39
|
},
|
|
38
40
|
...args,
|
|
39
41
|
}))
|
|
42
|
+
.then((res) => results.push(res))
|
|
40
43
|
.catch(reject);
|
|
41
44
|
}
|
|
42
|
-
resolve();
|
|
45
|
+
resolve(results);
|
|
43
46
|
});
|
|
44
47
|
}
|
|
45
48
|
exports.putItems = putItems;
|
package/dist/operations/query.js
CHANGED
|
@@ -7,7 +7,6 @@ const client_1 = require("../lib/client");
|
|
|
7
7
|
const helpers_1 = require("../lib/helpers");
|
|
8
8
|
async function query(keyCondition, key, args = {}) {
|
|
9
9
|
return client_1.client.send(new client_dynamodb_1.QueryCommand({
|
|
10
|
-
TableName: client_1.TableName,
|
|
11
10
|
KeyConditionExpression: keyCondition,
|
|
12
11
|
ExpressionAttributeValues: (0, helpers_1.getAttributeValues)(key, [
|
|
13
12
|
...(0, helpers_1.getAttributesFromExpression)(keyCondition, ":"),
|
|
@@ -18,6 +17,7 @@ async function query(keyCondition, key, args = {}) {
|
|
|
18
17
|
...(0, helpers_1.getAttributesFromExpression)(args?.FilterExpression || ""),
|
|
19
18
|
]),
|
|
20
19
|
...args,
|
|
20
|
+
TableName: args?.TableName || (0, client_1.getDefaultTable)(),
|
|
21
21
|
}));
|
|
22
22
|
}
|
|
23
23
|
exports.query = query;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { UpdateItemCommandInput, UpdateItemCommandOutput } from "@aws-sdk/client-dynamodb";
|
|
2
|
-
export declare function updateItem(key: any, data: any, args?: Partial<UpdateItemCommandInput>): Promise<
|
|
3
|
-
export declare function removeAttributes(key: any, attributes: string[]): Promise<UpdateItemCommandOutput>;
|
|
2
|
+
export declare function updateItem(key: any, data: any, args?: Partial<UpdateItemCommandInput>): Promise<undefined | Record<string, any>>;
|
|
3
|
+
export declare function removeAttributes(key: any, attributes: string[], args?: Partial<UpdateItemCommandInput>): Promise<UpdateItemCommandOutput>;
|
|
@@ -2,36 +2,46 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.removeAttributes = exports.updateItem = void 0;
|
|
4
4
|
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
5
|
+
const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
|
|
5
6
|
const client_1 = require("../lib/client");
|
|
6
7
|
const helpers_1 = require("../lib/helpers");
|
|
7
8
|
async function updateItem(key, data, args = {}) {
|
|
8
9
|
if (!Object.keys(data).includes("updatedAt")) {
|
|
9
10
|
data.updatedAt = Date.now();
|
|
10
11
|
}
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return client_1.client
|
|
16
|
-
|
|
17
|
-
Key: (0, helpers_1.stripKey)(key),
|
|
12
|
+
const valuesInCondition = (0, helpers_1.getAttributesFromExpression)(args?.ConditionExpression || "", ":");
|
|
13
|
+
const namesInCondition = (0, helpers_1.getAttributesFromExpression)(args?.ConditionExpression || "");
|
|
14
|
+
const attributesToUpdate = Object.keys(data).filter((key) => !valuesInCondition.includes(key));
|
|
15
|
+
const UpdateExpression = "SET " + attributesToUpdate.map((key) => `#${key} = :${key}`).join(", ");
|
|
16
|
+
return client_1.client
|
|
17
|
+
.send(new client_dynamodb_1.UpdateItemCommand({
|
|
18
|
+
Key: (0, helpers_1.stripKey)(key, args),
|
|
18
19
|
UpdateExpression,
|
|
19
|
-
ExpressionAttributeValues: (0, helpers_1.getAttributeValues)(data
|
|
20
|
-
|
|
20
|
+
ExpressionAttributeValues: (0, helpers_1.getAttributeValues)(data, [
|
|
21
|
+
...attributesToUpdate,
|
|
22
|
+
...valuesInCondition,
|
|
23
|
+
]),
|
|
24
|
+
ExpressionAttributeNames: (0, helpers_1.getAttributeNames)(data, [
|
|
25
|
+
...attributesToUpdate,
|
|
26
|
+
...namesInCondition,
|
|
27
|
+
]),
|
|
21
28
|
...args,
|
|
22
|
-
|
|
29
|
+
TableName: args?.TableName || (0, client_1.getDefaultTable)(),
|
|
30
|
+
}))
|
|
31
|
+
.then((res) => args?.ReturnValues ? (0, util_dynamodb_1.unmarshall)(res.Attributes) : undefined);
|
|
23
32
|
}
|
|
24
33
|
exports.updateItem = updateItem;
|
|
25
|
-
async function removeAttributes(key, attributes) {
|
|
34
|
+
async function removeAttributes(key, attributes, args = {}) {
|
|
26
35
|
const UpdateExpression = "REMOVE " + attributes.map((att) => `#${att}`).join(", ");
|
|
27
36
|
return client_1.client.send(new client_dynamodb_1.UpdateItemCommand({
|
|
28
|
-
|
|
29
|
-
Key: (0, helpers_1.stripKey)(key),
|
|
37
|
+
Key: (0, helpers_1.stripKey)(key, args),
|
|
30
38
|
UpdateExpression,
|
|
31
39
|
ExpressionAttributeNames: (0, helpers_1.getAttributeNames)(attributes.reduce((acc, att) => {
|
|
32
40
|
acc[att] = att;
|
|
33
41
|
return acc;
|
|
34
42
|
}, {})),
|
|
43
|
+
...args,
|
|
44
|
+
TableName: args?.TableName || (0, client_1.getDefaultTable)(),
|
|
35
45
|
}));
|
|
36
46
|
}
|
|
37
47
|
exports.removeAttributes = removeAttributes;
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# @moicky/dynamodb
|
|
2
2
|
|
|
3
|
-

|
|
4
3
|

|
|
5
4
|

|
|
6
5
|

|
|
7
|
-

|
|
8
7
|
|
|
9
8
|
## Description
|
|
10
9
|
|
|
@@ -25,9 +24,49 @@ npm i @moicky/dynamodb
|
|
|
25
24
|
|
|
26
25
|
## Setup
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
Requires a **keySchema** definition to be setup. Automatically grabs `DYNAMODB_TABLE` as an **environment variable** and uses `PK` and `SK` for it's schema. Can be overwritten using `initSchema` with multiple tables:
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
```ts
|
|
30
|
+
import { initSchema } from "@moicky/dynamodb";
|
|
31
|
+
|
|
32
|
+
// Should be called once at the start of the runtime before any operation is executed
|
|
33
|
+
initSchema({
|
|
34
|
+
// first one will be used if no TableName is specified
|
|
35
|
+
[process.env.DEFAULT_TABLE]: {
|
|
36
|
+
hash: "PK",
|
|
37
|
+
range: "SK",
|
|
38
|
+
},
|
|
39
|
+
[process.env.SECOND_TABLE]: {
|
|
40
|
+
hash: "bookId",
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Working with multiple tables
|
|
46
|
+
|
|
47
|
+
Every function accepts `args` which can include a `TableName` property that specifies the table and uses the keySchema from `initSchema()`
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { getItem, putItem, deleteItem } from "@moicky/dynamodb";
|
|
51
|
+
|
|
52
|
+
await putItem(
|
|
53
|
+
{
|
|
54
|
+
PK: "User/1",
|
|
55
|
+
someSortKey: "Book/1",
|
|
56
|
+
title: "The Great Gatsby",
|
|
57
|
+
author: "F. Scott Fitzgerald",
|
|
58
|
+
released: 1925,
|
|
59
|
+
},
|
|
60
|
+
{ TableName: process.env.SECOND_TABLE }
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const item = await getItem(
|
|
64
|
+
{ PK: "User/1", someSortKey: "Book/1" },
|
|
65
|
+
{ TableName: process.env.SECOND_TABLE }
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
await deleteItem(item, { TableName: process.env.SECOND_TABLE });
|
|
69
|
+
```
|
|
31
70
|
|
|
32
71
|
## Usage Examples
|
|
33
72
|
|
|
@@ -126,6 +165,19 @@ await updateItem(
|
|
|
126
165
|
{ description: "A book about a rich guy", author: "F. Scott Fitzgerald" }
|
|
127
166
|
);
|
|
128
167
|
|
|
168
|
+
await updateItem(
|
|
169
|
+
{ PK: "User/1", SK: "Book/1" },
|
|
170
|
+
{ released: 2000, maxReleased: 1950 }, // maxReleased will not be updated, since it is referenced inside the ConditionExpression
|
|
171
|
+
{ ConditionExpression: "#released < :maxReleased" }
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const newItem = await updateItem(
|
|
175
|
+
{ PK: "User/1", SK: "Book/1" },
|
|
176
|
+
{ released: 2000 },
|
|
177
|
+
{ ReturnValues: "ALL_NEW" }
|
|
178
|
+
);
|
|
179
|
+
console.log(newItem); // { "PK": "User/1", "SK": "Book/1", "released": 2000 }
|
|
180
|
+
|
|
129
181
|
await removeAttributes({ PK: "User/1", SK: "Book/1" }, ["description"]);
|
|
130
182
|
```
|
|
131
183
|
|
|
@@ -173,30 +225,170 @@ const booksWithFilter = await queryAllItems(
|
|
|
173
225
|
### Miscellaneous
|
|
174
226
|
|
|
175
227
|
```ts
|
|
176
|
-
import { itemExists,
|
|
228
|
+
import { itemExists, getAscendingId } from "@moicky/dynamodb";
|
|
177
229
|
|
|
178
230
|
// Check if an item exists using keySchema
|
|
179
231
|
const exists = await itemExists({ PK: "User/1", SK: "Book/1" });
|
|
180
232
|
|
|
181
233
|
// Generate ascending ID
|
|
234
|
+
// Specify keySchemaHash and optionally item to start at using keySchemaRange
|
|
182
235
|
|
|
183
236
|
// Example Structure 1: PK: "User/1", SK: "{{ ASCENDING_ID }}"
|
|
184
237
|
// Last item: { PK: "User/1", SK: "00000009" }
|
|
185
|
-
const id1 = await
|
|
238
|
+
const id1 = await getAscendingId({ PK: "User/1" });
|
|
186
239
|
console.log(id1); // "00000010"
|
|
187
240
|
|
|
188
241
|
// Example Structure 2: PK: "User/1", SK: "Book/{{ ASCENDING_ID }}"
|
|
189
242
|
// Last item: { PK: "User/1", SK: "Book/00000009" }
|
|
190
|
-
const id2 = await
|
|
243
|
+
const id2 = await getAscendingId({ PK: "User/1", SKPrefix: "Book" });
|
|
191
244
|
console.log(id2); // "00000010"
|
|
192
245
|
|
|
193
246
|
// Specify length of ID
|
|
194
|
-
const id3 = await
|
|
247
|
+
const id3 = await getAscendingId({ PK: "User/1", SKPrefix: "Book", length: 4 });
|
|
195
248
|
console.log(id3); // "0010"
|
|
249
|
+
|
|
250
|
+
// Example Structure 3: someKeySchemaHash: "User/1", SK: "Book/{{ ASCENDING_ID }}"
|
|
251
|
+
// Last item: { someKeySchemaHash: "User/1", SK: "Book/00000009" }
|
|
252
|
+
const id4 = await getAscendingId({
|
|
253
|
+
someKeySchemaHash: "User/1",
|
|
254
|
+
SKPrefix: "Book",
|
|
255
|
+
});
|
|
256
|
+
console.log(id4); // "00000010"
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Why should I use this?
|
|
260
|
+
|
|
261
|
+
Generally it makes it easier to interact with the dynamodb from AWS. Here are some before and after examples using the new aws-sdk v3:
|
|
262
|
+
|
|
263
|
+
### Put
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
const demoItem = {
|
|
267
|
+
PK: "User/1",
|
|
268
|
+
SK: "Book/1",
|
|
269
|
+
title: "The Great Gatsby",
|
|
270
|
+
author: "F. Scott Fitzgerald",
|
|
271
|
+
released: 1925,
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// Without helpers:
|
|
275
|
+
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
|
|
276
|
+
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
|
|
277
|
+
|
|
278
|
+
const client = new DynamoDBClient({
|
|
279
|
+
region: process.env.AWS_REGION,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const newItem = await client
|
|
283
|
+
.send(
|
|
284
|
+
new PutItemCommand({
|
|
285
|
+
TableName: process.env.DYNAMODB_TABLE,
|
|
286
|
+
Item: marshall(demoItem),
|
|
287
|
+
ReturnValues: "ALL_NEW",
|
|
288
|
+
})
|
|
289
|
+
)
|
|
290
|
+
.then((result) => unmarshall(result.Attributes));
|
|
291
|
+
|
|
292
|
+
// With helpers:
|
|
293
|
+
import { putItem } from "@moicky/dynamodb";
|
|
294
|
+
|
|
295
|
+
const newItem = await putItem(demoItem, { ReturnValues: "ALL_NEW" });
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Query
|
|
299
|
+
|
|
300
|
+
```js
|
|
301
|
+
// Without helpers:
|
|
302
|
+
import { DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb";
|
|
303
|
+
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
|
|
304
|
+
|
|
305
|
+
const client = new DynamoDBClient({
|
|
306
|
+
region: process.env.AWS_REGION,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const results = await client
|
|
310
|
+
.send(
|
|
311
|
+
new QueryCommand({
|
|
312
|
+
TableName: process.env.DYNAMODB_TABLE,
|
|
313
|
+
KeyConditionExpression: "#PK = :PK and begins_with(#SK, :SK)",
|
|
314
|
+
ExpressionAttributeNames: {
|
|
315
|
+
"#PK": "PK",
|
|
316
|
+
"#SK": "SK",
|
|
317
|
+
},
|
|
318
|
+
ExpressionAttributeValues: {
|
|
319
|
+
":PK": marshall("User/1"),
|
|
320
|
+
":SK": marshall("Book/"),
|
|
321
|
+
},
|
|
322
|
+
})
|
|
323
|
+
)
|
|
324
|
+
.then((result) => result.Items.map((item) => unmarshall(item)));
|
|
325
|
+
|
|
326
|
+
// With helpers
|
|
327
|
+
import { queryItems } from "@moicky/dynamodb";
|
|
328
|
+
|
|
329
|
+
const results = await queryItems("#PK = :PK and begins_with(#SK, :SK)", {
|
|
330
|
+
PK: "User/1",
|
|
331
|
+
SK: "Book/",
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Update
|
|
336
|
+
|
|
337
|
+
```js
|
|
338
|
+
// Without helpers
|
|
339
|
+
import { DynamoDBClient, UpdateItemCommand } from "@aws-sdk/client-dynamodb";
|
|
340
|
+
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
|
|
341
|
+
|
|
342
|
+
const client = new DynamoDBClient({
|
|
343
|
+
region: process.env.AWS_REGION,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const result = await client
|
|
347
|
+
.send(
|
|
348
|
+
new UpdateItemCommand({
|
|
349
|
+
TableName: process.env.DYNAMODB_TABLE,
|
|
350
|
+
Key: marshall({ PK: "User/1", SK: "Book/1" }),
|
|
351
|
+
UpdateExpression: "SET #released = :released, #title = :title",
|
|
352
|
+
ExpressionAttributeNames: {
|
|
353
|
+
"#released": "released",
|
|
354
|
+
"#title": "title",
|
|
355
|
+
},
|
|
356
|
+
ExpressionAttributeValues: marshall({
|
|
357
|
+
":released": 2000,
|
|
358
|
+
":title": "New Title",
|
|
359
|
+
}),
|
|
360
|
+
ReturnValues: "ALL_NEW",
|
|
361
|
+
})
|
|
362
|
+
)
|
|
363
|
+
.then((result) => unmarshall(result.Attributes));
|
|
364
|
+
|
|
365
|
+
// With helpers
|
|
366
|
+
import { updateItem } from "@aws-sdk/lib-dynamodb";
|
|
367
|
+
|
|
368
|
+
const result = await updateItem(
|
|
369
|
+
{ PK: "User/1", SK: "Book/1" },
|
|
370
|
+
{ released: 2000, title: "New Title" },
|
|
371
|
+
{ ReturnValues: "ALL_NEW" }
|
|
372
|
+
);
|
|
196
373
|
```
|
|
197
374
|
|
|
198
375
|
## Tests
|
|
199
376
|
|
|
377
|
+
### Setup
|
|
378
|
+
|
|
379
|
+
Requires `DEFAULT_TABLE` and `SECOND_TABLE` to be present inside the environment (`.env file`)
|
|
380
|
+
Can be deployed using the `template.yml` on aws:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
sam deploy
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Will then return the table-names as the output of the template
|
|
387
|
+
|
|
388
|
+
### Execution
|
|
389
|
+
|
|
390
|
+
Finally executing all tests:
|
|
391
|
+
|
|
200
392
|
```bash
|
|
201
393
|
npm run test
|
|
202
394
|
```
|