@allthings/cloud-toolkit 1.3.0 → 1.5.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 CHANGED
@@ -18,5 +18,24 @@ Standardizes the setup of aws, datadog and other things
18
18
  |--------------|----------------------------|------------------------------------------------------|
19
19
  | invokeLambda | invocationType<br/>payload | Invokes set lambda with invocations type and payload |
20
20
 
21
+ ### dynamodb
22
+ | function | parameters | description |
23
+ |-----------------|--------------------------------------|----------------------------------------------------------|
24
+ | batchWriteItems | table<br/>items | Creates multiple item in a defined table |
25
+ | deleteItem | table<br/>key<br/>options (optional) | Deletes database item from defined table by given key |
26
+ | getItem | table<br/>key<br/>options (optional) | Returns item from Database by defined table and key |
27
+ | putItem | table<br/>key | Creates one item in defined table |
28
+ | queryTable | table<br/>options (optional) | Returns result items of query |
29
+ | scanTable | table<br/>options (optional) | Returns result items of scan |
30
+ | updateItem | table<br/>key<br/>options (optional) | Updates item in database by provided table and given key |
31
+
32
+ ### sqs
33
+ | function | parameters | description |
34
+ |-----------------|---------------------------------------------------------------------------------|----------------------------------------------------------|
35
+ | deleteQueueItem | queueUrl<br/>receiptHandle | Deletes item from queue |
36
+ | getQueueItems | queueUrl<br/>maxNumberOfMessages (optional) | Retrieves items from queue |
37
+ | getQueueUrl | queueName | Returns queue url which is needed for all other requests |
38
+ | sendMessage | queueUrl<br/>message<br/>messageAttributes(optional)<br/>messageDelay(optional) | Creates an item in the queue |
39
+
21
40
  ### logger
22
41
  A winston instance will be created with default log levels https://github.com/winstonjs/winston?tab=readme-ov-file#logging
package/dist/lib.cjs.js CHANGED
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ var clientDynamodb = require('@aws-sdk/client-dynamodb');
4
+ var libDynamodb = require('@aws-sdk/lib-dynamodb');
5
+ var Bottleneck = require('bottleneck');
3
6
  var clientLambda = require('@aws-sdk/client-lambda');
4
7
  var clientSsm = require('@aws-sdk/client-ssm');
5
8
  var aws = require('@aws-sdk/client-ses');
@@ -29,6 +32,189 @@ const DEFAULT_AWS_CONFIGURATION = {
29
32
  region: 'eu-central-2',
30
33
  };
31
34
 
35
+ const splitEvery = (every, list) => Array.from({ length: Math.ceil(list.length / every) }).map((_, count) => list.slice(count * every, count * every + every));
36
+ const until = async (predicate, transformer, initialValue, iterationCount = 0) => {
37
+ const transformed = await transformer(initialValue, iterationCount);
38
+ return (await predicate(transformed, iterationCount))
39
+ ? transformed
40
+ : until(predicate, transformer, transformed, iterationCount + 1);
41
+ };
42
+
43
+ const DEFAULT_BATCH_ITEMS_MAX_RETRIES = 50;
44
+ let queue;
45
+ const makeKey = (key) => {
46
+ if (typeof key === 'string') {
47
+ return { id: key };
48
+ }
49
+ return key;
50
+ };
51
+ const deleteItem = async (config, documentClient, table, key, options = {}) => {
52
+ try {
53
+ const { ReturnValues = 'NONE' } = options;
54
+ const command = new libDynamodb.DeleteCommand({
55
+ Key: makeKey(key),
56
+ ReturnValues,
57
+ TableName: config.tableNamePrefix + table,
58
+ });
59
+ const result = await queue.schedule(() => documentClient.send(command));
60
+ return result.Attributes;
61
+ }
62
+ catch (error) {
63
+ if (config.logger) {
64
+ config.logger('error', error);
65
+ }
66
+ return undefined;
67
+ }
68
+ };
69
+ const getItem = async (config, documentClient, table, key, options = {}) => {
70
+ try {
71
+ const { ConsistentRead = false } = options;
72
+ const command = new libDynamodb.GetCommand({
73
+ ConsistentRead,
74
+ Key: makeKey(key),
75
+ TableName: config.tableNamePrefix + table,
76
+ });
77
+ const result = await queue.schedule(() => documentClient.send(command));
78
+ return result.Item;
79
+ }
80
+ catch (error) {
81
+ if (config.logger) {
82
+ config.logger('error', error);
83
+ }
84
+ return undefined;
85
+ }
86
+ };
87
+ const putItem = async (config, documentClient, table, Item) => {
88
+ try {
89
+ const { expires, ...data } = Item;
90
+ const command = new libDynamodb.PutCommand({
91
+ Item: expires === null ? data : Item,
92
+ TableName: config.tableNamePrefix + table,
93
+ });
94
+ return (await queue.schedule(() => documentClient.send(command))) && Item;
95
+ }
96
+ catch (error) {
97
+ if (config.logger) {
98
+ config.logger('error', error);
99
+ }
100
+ return undefined;
101
+ }
102
+ };
103
+ const updateItem = async (config, documentClient, table, key, options = {}) => {
104
+ try {
105
+ const command = new libDynamodb.UpdateCommand({
106
+ Key: makeKey(key),
107
+ ...options,
108
+ ReturnValues: 'ALL_NEW',
109
+ TableName: config.tableNamePrefix + table,
110
+ });
111
+ const result = await queue.schedule(async () => documentClient.send(command));
112
+ return result.Attributes;
113
+ }
114
+ catch (error) {
115
+ if (config.logger) {
116
+ config.logger('error', error);
117
+ }
118
+ return undefined;
119
+ }
120
+ };
121
+ const hasNoUnprocessedItems = ({ UnprocessedItems, }) => !UnprocessedItems || Object.keys(UnprocessedItems).length === 0;
122
+ const makeBatchWriteRequestWithExponentialBackoff = (documentClient, table, batchWriteMaxRetries) => async (batchItems, retries) => {
123
+ if (retries > batchWriteMaxRetries) {
124
+ throw new Error('Reached maximum number of DynamoDB BatchWrite request retries.');
125
+ }
126
+ const command = new libDynamodb.BatchWriteCommand({
127
+ RequestItems: batchItems.UnprocessedItems ?? {
128
+ [table]: batchItems.map((Item) => ({
129
+ PutRequest: { Item },
130
+ })),
131
+ },
132
+ ReturnConsumedCapacity: 'TOTAL',
133
+ ReturnItemCollectionMetrics: 'SIZE',
134
+ });
135
+ return documentClient.send(command);
136
+ };
137
+ const batchWriteItems = async (config, documentClient, table, items) => {
138
+ try {
139
+ const batches = splitEvery(25, items);
140
+ const result = await Promise.all(batches.map(async (batch) => queue.schedule(async () => until(hasNoUnprocessedItems, makeBatchWriteRequestWithExponentialBackoff(documentClient, config.tableNamePrefix + table, config.batchWriteMaxRetries ?? DEFAULT_BATCH_ITEMS_MAX_RETRIES), batch))));
141
+ return result && items;
142
+ }
143
+ catch (error) {
144
+ if (config.logger) {
145
+ config.logger('error', error);
146
+ }
147
+ return undefined;
148
+ }
149
+ };
150
+ const scanTable = async (config, documentClient, table, options = {}) => {
151
+ try {
152
+ const { ConsistentRead = false } = options;
153
+ const command = new libDynamodb.ScanCommand({
154
+ ...options,
155
+ ConsistentRead,
156
+ TableName: config.tableNamePrefix + table,
157
+ });
158
+ const result = await queue.schedule(() => documentClient.send(command));
159
+ return result;
160
+ }
161
+ catch (error) {
162
+ if (config.logger) {
163
+ config.logger('error', error);
164
+ }
165
+ return undefined;
166
+ }
167
+ };
168
+ const queryTable = async (config, documentClient, table, options = {}) => {
169
+ try {
170
+ const { ConsistentRead = false } = options;
171
+ const command = new libDynamodb.QueryCommand({
172
+ ...options,
173
+ ConsistentRead,
174
+ TableName: config.tableNamePrefix + table,
175
+ });
176
+ const result = await queue.schedule(() => documentClient.send(command));
177
+ return result;
178
+ }
179
+ catch (error) {
180
+ if (config.logger) {
181
+ config.logger('error', error);
182
+ }
183
+ return undefined;
184
+ }
185
+ };
186
+ const dynamodb = (userConfig) => {
187
+ const { clientConfig, ...config } = {
188
+ ...DEFAULT_AWS_CONFIGURATION,
189
+ ...userConfig,
190
+ };
191
+ const ddbClient = new clientDynamodb.DynamoDBClient(config.dockerConfig
192
+ ? {
193
+ credentials: {
194
+ accessKeyId: 'foobarkey',
195
+ secretAccessKey: 'foobarsecret',
196
+ },
197
+ endpoint: config.dockerConfig.endpoint,
198
+ region: 'local',
199
+ retryMode: 'standard',
200
+ }
201
+ : { retryMode: 'standard', ...clientConfig });
202
+ const documentClient = libDynamodb.DynamoDBDocumentClient.from(ddbClient);
203
+ queue = new Bottleneck({
204
+ maxConcurrent: config.queueConfig?.queueConcurrency ?? 25,
205
+ minTime: config.queueConfig?.queueDelay ?? undefined,
206
+ });
207
+ return {
208
+ batchWriteItems: (table, items) => batchWriteItems(config, documentClient, table, items),
209
+ deleteItem: (table, key, options) => deleteItem(config, documentClient, table, key, options),
210
+ getItem: (table, key, options) => getItem(config, documentClient, table, key, options),
211
+ putItem: (table, item) => putItem(config, documentClient, table, item),
212
+ queryTable: (table, options) => queryTable(config, documentClient, table, options),
213
+ scanTable: (table, options) => scanTable(config, documentClient, table, options),
214
+ updateItem: (table, key, options) => updateItem(config, documentClient, table, key, options),
215
+ };
216
+ };
217
+
32
218
  const invokeLambda = async (lambdaClient, config, invocationType, payload) => {
33
219
  try {
34
220
  const invokeCommand = new clientLambda.InvokeCommand({
@@ -198,6 +384,7 @@ const logger = (config) => {
198
384
 
199
385
  exports.DEFAULT_PARAMETER_STORE_CLIENT_CONFIG = DEFAULT_CLIENT_CONFIG$1;
200
386
  exports.DEFAULT_SES_CLIENT_CONFIG = DEFAULT_CLIENT_CONFIG;
387
+ exports.dynamodb = dynamodb;
201
388
  exports.lambda = lambda;
202
389
  exports.logger = logger;
203
390
  exports.parameterStore = parameterStore;
package/dist/lib.esm.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
+ import { DynamoDBDocumentClient, DeleteCommand, GetCommand, PutCommand, UpdateCommand, ScanCommand, QueryCommand, BatchWriteCommand } from '@aws-sdk/lib-dynamodb';
3
+ import Bottleneck from 'bottleneck';
1
4
  import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
2
5
  import { SSMClient, GetParametersCommand, GetParameterCommand } from '@aws-sdk/client-ssm';
3
6
  import * as aws from '@aws-sdk/client-ses';
@@ -8,6 +11,189 @@ const DEFAULT_AWS_CONFIGURATION = {
8
11
  region: 'eu-central-2',
9
12
  };
10
13
 
14
+ const splitEvery = (every, list) => Array.from({ length: Math.ceil(list.length / every) }).map((_, count) => list.slice(count * every, count * every + every));
15
+ const until = async (predicate, transformer, initialValue, iterationCount = 0) => {
16
+ const transformed = await transformer(initialValue, iterationCount);
17
+ return (await predicate(transformed, iterationCount))
18
+ ? transformed
19
+ : until(predicate, transformer, transformed, iterationCount + 1);
20
+ };
21
+
22
+ const DEFAULT_BATCH_ITEMS_MAX_RETRIES = 50;
23
+ let queue;
24
+ const makeKey = (key) => {
25
+ if (typeof key === 'string') {
26
+ return { id: key };
27
+ }
28
+ return key;
29
+ };
30
+ const deleteItem = async (config, documentClient, table, key, options = {}) => {
31
+ try {
32
+ const { ReturnValues = 'NONE' } = options;
33
+ const command = new DeleteCommand({
34
+ Key: makeKey(key),
35
+ ReturnValues,
36
+ TableName: config.tableNamePrefix + table,
37
+ });
38
+ const result = await queue.schedule(() => documentClient.send(command));
39
+ return result.Attributes;
40
+ }
41
+ catch (error) {
42
+ if (config.logger) {
43
+ config.logger('error', error);
44
+ }
45
+ return undefined;
46
+ }
47
+ };
48
+ const getItem = async (config, documentClient, table, key, options = {}) => {
49
+ try {
50
+ const { ConsistentRead = false } = options;
51
+ const command = new GetCommand({
52
+ ConsistentRead,
53
+ Key: makeKey(key),
54
+ TableName: config.tableNamePrefix + table,
55
+ });
56
+ const result = await queue.schedule(() => documentClient.send(command));
57
+ return result.Item;
58
+ }
59
+ catch (error) {
60
+ if (config.logger) {
61
+ config.logger('error', error);
62
+ }
63
+ return undefined;
64
+ }
65
+ };
66
+ const putItem = async (config, documentClient, table, Item) => {
67
+ try {
68
+ const { expires, ...data } = Item;
69
+ const command = new PutCommand({
70
+ Item: expires === null ? data : Item,
71
+ TableName: config.tableNamePrefix + table,
72
+ });
73
+ return (await queue.schedule(() => documentClient.send(command))) && Item;
74
+ }
75
+ catch (error) {
76
+ if (config.logger) {
77
+ config.logger('error', error);
78
+ }
79
+ return undefined;
80
+ }
81
+ };
82
+ const updateItem = async (config, documentClient, table, key, options = {}) => {
83
+ try {
84
+ const command = new UpdateCommand({
85
+ Key: makeKey(key),
86
+ ...options,
87
+ ReturnValues: 'ALL_NEW',
88
+ TableName: config.tableNamePrefix + table,
89
+ });
90
+ const result = await queue.schedule(async () => documentClient.send(command));
91
+ return result.Attributes;
92
+ }
93
+ catch (error) {
94
+ if (config.logger) {
95
+ config.logger('error', error);
96
+ }
97
+ return undefined;
98
+ }
99
+ };
100
+ const hasNoUnprocessedItems = ({ UnprocessedItems, }) => !UnprocessedItems || Object.keys(UnprocessedItems).length === 0;
101
+ const makeBatchWriteRequestWithExponentialBackoff = (documentClient, table, batchWriteMaxRetries) => async (batchItems, retries) => {
102
+ if (retries > batchWriteMaxRetries) {
103
+ throw new Error('Reached maximum number of DynamoDB BatchWrite request retries.');
104
+ }
105
+ const command = new BatchWriteCommand({
106
+ RequestItems: batchItems.UnprocessedItems ?? {
107
+ [table]: batchItems.map((Item) => ({
108
+ PutRequest: { Item },
109
+ })),
110
+ },
111
+ ReturnConsumedCapacity: 'TOTAL',
112
+ ReturnItemCollectionMetrics: 'SIZE',
113
+ });
114
+ return documentClient.send(command);
115
+ };
116
+ const batchWriteItems = async (config, documentClient, table, items) => {
117
+ try {
118
+ const batches = splitEvery(25, items);
119
+ const result = await Promise.all(batches.map(async (batch) => queue.schedule(async () => until(hasNoUnprocessedItems, makeBatchWriteRequestWithExponentialBackoff(documentClient, config.tableNamePrefix + table, config.batchWriteMaxRetries ?? DEFAULT_BATCH_ITEMS_MAX_RETRIES), batch))));
120
+ return result && items;
121
+ }
122
+ catch (error) {
123
+ if (config.logger) {
124
+ config.logger('error', error);
125
+ }
126
+ return undefined;
127
+ }
128
+ };
129
+ const scanTable = async (config, documentClient, table, options = {}) => {
130
+ try {
131
+ const { ConsistentRead = false } = options;
132
+ const command = new ScanCommand({
133
+ ...options,
134
+ ConsistentRead,
135
+ TableName: config.tableNamePrefix + table,
136
+ });
137
+ const result = await queue.schedule(() => documentClient.send(command));
138
+ return result;
139
+ }
140
+ catch (error) {
141
+ if (config.logger) {
142
+ config.logger('error', error);
143
+ }
144
+ return undefined;
145
+ }
146
+ };
147
+ const queryTable = async (config, documentClient, table, options = {}) => {
148
+ try {
149
+ const { ConsistentRead = false } = options;
150
+ const command = new QueryCommand({
151
+ ...options,
152
+ ConsistentRead,
153
+ TableName: config.tableNamePrefix + table,
154
+ });
155
+ const result = await queue.schedule(() => documentClient.send(command));
156
+ return result;
157
+ }
158
+ catch (error) {
159
+ if (config.logger) {
160
+ config.logger('error', error);
161
+ }
162
+ return undefined;
163
+ }
164
+ };
165
+ const dynamodb = (userConfig) => {
166
+ const { clientConfig, ...config } = {
167
+ ...DEFAULT_AWS_CONFIGURATION,
168
+ ...userConfig,
169
+ };
170
+ const ddbClient = new DynamoDBClient(config.dockerConfig
171
+ ? {
172
+ credentials: {
173
+ accessKeyId: 'foobarkey',
174
+ secretAccessKey: 'foobarsecret',
175
+ },
176
+ endpoint: config.dockerConfig.endpoint,
177
+ region: 'local',
178
+ retryMode: 'standard',
179
+ }
180
+ : { retryMode: 'standard', ...clientConfig });
181
+ const documentClient = DynamoDBDocumentClient.from(ddbClient);
182
+ queue = new Bottleneck({
183
+ maxConcurrent: config.queueConfig?.queueConcurrency ?? 25,
184
+ minTime: config.queueConfig?.queueDelay ?? undefined,
185
+ });
186
+ return {
187
+ batchWriteItems: (table, items) => batchWriteItems(config, documentClient, table, items),
188
+ deleteItem: (table, key, options) => deleteItem(config, documentClient, table, key, options),
189
+ getItem: (table, key, options) => getItem(config, documentClient, table, key, options),
190
+ putItem: (table, item) => putItem(config, documentClient, table, item),
191
+ queryTable: (table, options) => queryTable(config, documentClient, table, options),
192
+ scanTable: (table, options) => scanTable(config, documentClient, table, options),
193
+ updateItem: (table, key, options) => updateItem(config, documentClient, table, key, options),
194
+ };
195
+ };
196
+
11
197
  const invokeLambda = async (lambdaClient, config, invocationType, payload) => {
12
198
  try {
13
199
  const invokeCommand = new InvokeCommand({
@@ -175,4 +361,4 @@ const logger = (config) => {
175
361
  });
176
362
  };
177
363
 
178
- export { DEFAULT_CLIENT_CONFIG$1 as DEFAULT_PARAMETER_STORE_CLIENT_CONFIG, DEFAULT_CLIENT_CONFIG as DEFAULT_SES_CLIENT_CONFIG, lambda, logger, parameterStore, ses };
364
+ export { DEFAULT_CLIENT_CONFIG$1 as DEFAULT_PARAMETER_STORE_CLIENT_CONFIG, DEFAULT_CLIENT_CONFIG as DEFAULT_SES_CLIENT_CONFIG, dynamodb, lambda, logger, parameterStore, ses };