@moicky/dynamodb 1.1.3 → 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 +18 -14
- 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 +1 -1
- package/dist/operations/update.js +7 -6
- package/package.json +1 -1
- package/readme.md +187 -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,32 +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
34
|
return withPadding;
|
|
31
35
|
}
|
|
32
|
-
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
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[]): Promise<UpdateItemCommandOutput>;
|
|
3
|
+
export declare function removeAttributes(key: any, attributes: string[], args?: Partial<UpdateItemCommandInput>): Promise<UpdateItemCommandOutput>;
|
|
@@ -2,9 +2,9 @@
|
|
|
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
|
-
const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
|
|
8
8
|
async function updateItem(key, data, args = {}) {
|
|
9
9
|
if (!Object.keys(data).includes("updatedAt")) {
|
|
10
10
|
data.updatedAt = Date.now();
|
|
@@ -15,8 +15,7 @@ async function updateItem(key, data, args = {}) {
|
|
|
15
15
|
const UpdateExpression = "SET " + attributesToUpdate.map((key) => `#${key} = :${key}`).join(", ");
|
|
16
16
|
return client_1.client
|
|
17
17
|
.send(new client_dynamodb_1.UpdateItemCommand({
|
|
18
|
-
|
|
19
|
-
Key: (0, helpers_1.stripKey)(key),
|
|
18
|
+
Key: (0, helpers_1.stripKey)(key, args),
|
|
20
19
|
UpdateExpression,
|
|
21
20
|
ExpressionAttributeValues: (0, helpers_1.getAttributeValues)(data, [
|
|
22
21
|
...attributesToUpdate,
|
|
@@ -27,20 +26,22 @@ async function updateItem(key, data, args = {}) {
|
|
|
27
26
|
...namesInCondition,
|
|
28
27
|
]),
|
|
29
28
|
...args,
|
|
29
|
+
TableName: args?.TableName || (0, client_1.getDefaultTable)(),
|
|
30
30
|
}))
|
|
31
31
|
.then((res) => args?.ReturnValues ? (0, util_dynamodb_1.unmarshall)(res.Attributes) : undefined);
|
|
32
32
|
}
|
|
33
33
|
exports.updateItem = updateItem;
|
|
34
|
-
async function removeAttributes(key, attributes) {
|
|
34
|
+
async function removeAttributes(key, attributes, args = {}) {
|
|
35
35
|
const UpdateExpression = "REMOVE " + attributes.map((att) => `#${att}`).join(", ");
|
|
36
36
|
return client_1.client.send(new client_dynamodb_1.UpdateItemCommand({
|
|
37
|
-
|
|
38
|
-
Key: (0, helpers_1.stripKey)(key),
|
|
37
|
+
Key: (0, helpers_1.stripKey)(key, args),
|
|
39
38
|
UpdateExpression,
|
|
40
39
|
ExpressionAttributeNames: (0, helpers_1.getAttributeNames)(attributes.reduce((acc, att) => {
|
|
41
40
|
acc[att] = att;
|
|
42
41
|
return acc;
|
|
43
42
|
}, {})),
|
|
43
|
+
...args,
|
|
44
|
+
TableName: args?.TableName || (0, client_1.getDefaultTable)(),
|
|
44
45
|
}));
|
|
45
46
|
}
|
|
46
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
|
|
|
@@ -186,30 +225,170 @@ const booksWithFilter = await queryAllItems(
|
|
|
186
225
|
### Miscellaneous
|
|
187
226
|
|
|
188
227
|
```ts
|
|
189
|
-
import { itemExists,
|
|
228
|
+
import { itemExists, getAscendingId } from "@moicky/dynamodb";
|
|
190
229
|
|
|
191
230
|
// Check if an item exists using keySchema
|
|
192
231
|
const exists = await itemExists({ PK: "User/1", SK: "Book/1" });
|
|
193
232
|
|
|
194
233
|
// Generate ascending ID
|
|
234
|
+
// Specify keySchemaHash and optionally item to start at using keySchemaRange
|
|
195
235
|
|
|
196
236
|
// Example Structure 1: PK: "User/1", SK: "{{ ASCENDING_ID }}"
|
|
197
237
|
// Last item: { PK: "User/1", SK: "00000009" }
|
|
198
|
-
const id1 = await
|
|
238
|
+
const id1 = await getAscendingId({ PK: "User/1" });
|
|
199
239
|
console.log(id1); // "00000010"
|
|
200
240
|
|
|
201
241
|
// Example Structure 2: PK: "User/1", SK: "Book/{{ ASCENDING_ID }}"
|
|
202
242
|
// Last item: { PK: "User/1", SK: "Book/00000009" }
|
|
203
|
-
const id2 = await
|
|
243
|
+
const id2 = await getAscendingId({ PK: "User/1", SKPrefix: "Book" });
|
|
204
244
|
console.log(id2); // "00000010"
|
|
205
245
|
|
|
206
246
|
// Specify length of ID
|
|
207
|
-
const id3 = await
|
|
247
|
+
const id3 = await getAscendingId({ PK: "User/1", SKPrefix: "Book", length: 4 });
|
|
208
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
|
+
);
|
|
209
373
|
```
|
|
210
374
|
|
|
211
375
|
## Tests
|
|
212
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
|
+
|
|
213
392
|
```bash
|
|
214
393
|
npm run test
|
|
215
394
|
```
|