@certik/skynet 0.18.9 → 0.19.1
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/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/{abi.js → abi.ts} +5 -132
- package/{address.js → address.ts} +4 -4
- package/{api.js → api.ts} +80 -20
- package/{app.js → app.ts} +138 -25
- package/{availability.js → availability.ts} +18 -21
- package/bun.lockb +0 -0
- package/{cli.js → cli.ts} +1 -1
- package/{const.js → const.ts} +42 -3
- package/databricks.ts +82 -0
- package/{date.js → date.ts} +5 -5
- package/{deploy.js → deploy.ts} +157 -44
- package/{dynamodb.js → dynamodb.ts} +159 -186
- package/env.ts +25 -0
- package/eslint.config.mjs +7 -0
- package/examples/{api.js → api.ts} +7 -6
- package/examples/{indexer.js → indexer.ts} +9 -10
- package/examples/{mode-indexer.js → mode-indexer.ts} +18 -10
- package/graphql.ts +43 -0
- package/{indexer.js → indexer.ts} +228 -109
- package/{log.js → log.ts} +6 -6
- package/{opsgenie.js → opsgenie.ts} +29 -6
- package/package.json +17 -11
- package/{s3.js → s3.ts} +29 -19
- package/search.ts +37 -0
- package/selector.ts +72 -0
- package/{slack.js → slack.ts} +19 -12
- package/snowflake.ts +51 -0
- package/tsconfig.json +26 -0
- package/util.ts +35 -0
- package/web3.ts +41 -0
- package/.eslintignore +0 -1
- package/.eslintrc.json +0 -20
- package/abi.d.ts +0 -20
- package/address.d.ts +0 -2
- package/api.d.ts +0 -12
- package/app.d.ts +0 -118
- package/availability.d.ts +0 -18
- package/cli.d.ts +0 -4
- package/const.d.ts +0 -42
- package/databricks.js +0 -82
- package/deploy.d.ts +0 -51
- package/dns.d.ts +0 -1
- package/dns.js +0 -27
- package/dynamodb.d.ts +0 -63
- package/env.d.ts +0 -6
- package/env.js +0 -42
- package/examples/consumer.js +0 -30
- package/examples/producer.js +0 -63
- package/graphql.d.ts +0 -3
- package/graphql.js +0 -22
- package/indexer.d.ts +0 -32
- package/log.d.ts +0 -8
- package/opsgenie.d.ts +0 -8
- package/proxy.d.ts +0 -9
- package/proxy.js +0 -154
- package/s3.d.ts +0 -36
- package/search.d.ts +0 -5
- package/search.js +0 -29
- package/selector.d.ts +0 -19
- package/selector.js +0 -71
- package/slack.d.ts +0 -10
- package/snowflake.d.ts +0 -4
- package/snowflake.js +0 -33
- package/sqs.d.ts +0 -3
- package/sqs.js +0 -5
- package/util.d.ts +0 -5
- package/util.js +0 -61
- package/web3.d.ts +0 -25
- package/web3.js +0 -113
|
@@ -6,14 +6,24 @@ import {
|
|
|
6
6
|
PutCommand,
|
|
7
7
|
QueryCommand,
|
|
8
8
|
UpdateCommand,
|
|
9
|
-
|
|
9
|
+
} from "@aws-sdk/lib-dynamodb";
|
|
10
|
+
import type {
|
|
11
|
+
ScanCommandInput,
|
|
12
|
+
PutCommandInput,
|
|
13
|
+
GetCommandInput,
|
|
14
|
+
QueryCommandInput,
|
|
15
|
+
UpdateCommandOutput,
|
|
16
|
+
UpdateCommandInput,
|
|
17
|
+
ScanCommandOutput,
|
|
10
18
|
} from "@aws-sdk/lib-dynamodb";
|
|
11
19
|
import { DynamoDBClient, DescribeTableCommand } from "@aws-sdk/client-dynamodb";
|
|
20
|
+
import type { KeySchemaElement, GlobalSecondaryIndexDescription } from "@aws-sdk/client-dynamodb";
|
|
12
21
|
import { wait } from "./availability.js";
|
|
22
|
+
import { arrayGroup } from "./util.js";
|
|
13
23
|
|
|
14
|
-
let _dynamoDB;
|
|
24
|
+
let _dynamoDB: DynamoDBClient;
|
|
15
25
|
|
|
16
|
-
let _docClient;
|
|
26
|
+
let _docClient: DynamoDBDocumentClient;
|
|
17
27
|
|
|
18
28
|
function getDynamoDB(forceNew = false) {
|
|
19
29
|
if (!_dynamoDB || forceNew) {
|
|
@@ -46,47 +56,44 @@ function getDocClient(forceNew = false) {
|
|
|
46
56
|
return _docClient;
|
|
47
57
|
}
|
|
48
58
|
|
|
49
|
-
function
|
|
50
|
-
return {
|
|
51
|
-
Items: q1.Items.concat(q2.Items),
|
|
52
|
-
Count: q1.Count + q2.Count,
|
|
53
|
-
ScannedCount: q1.ScannedCount + q2.ScannedCount,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function scanWholeTable(options) {
|
|
59
|
+
async function scanWholeTable(options: ScanCommandInput) {
|
|
58
60
|
const dynamodb = getDocClient();
|
|
59
61
|
|
|
60
|
-
let items =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
ScannedCount: 0,
|
|
64
|
-
};
|
|
62
|
+
let items: ScanCommandOutput["Items"] = [];
|
|
63
|
+
let count = 0;
|
|
64
|
+
let scannedCount = 0;
|
|
65
65
|
|
|
66
66
|
let data = await dynamodb.send(new ScanCommand(options));
|
|
67
67
|
|
|
68
68
|
while (data.LastEvaluatedKey) {
|
|
69
|
-
|
|
69
|
+
if (data.Items) {
|
|
70
|
+
items = items.concat(data.Items);
|
|
71
|
+
}
|
|
72
|
+
count += data.Count || 0;
|
|
73
|
+
scannedCount += data.ScannedCount || 0;
|
|
70
74
|
|
|
71
75
|
data = await dynamodb.send(new ScanCommand({ ...options, ExclusiveStartKey: data.LastEvaluatedKey }));
|
|
72
76
|
}
|
|
73
77
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return items;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function toSlices(items) {
|
|
80
|
-
let slices = [];
|
|
81
|
-
|
|
82
|
-
for (let start = 0; start < items.length; start += 25) {
|
|
83
|
-
slices.push(items.slice(start, start + 25));
|
|
78
|
+
if (data.Items) {
|
|
79
|
+
items = items.concat(data.Items);
|
|
84
80
|
}
|
|
81
|
+
count += data.Count || 0;
|
|
82
|
+
scannedCount += data.ScannedCount || 0;
|
|
85
83
|
|
|
86
|
-
return
|
|
84
|
+
return {
|
|
85
|
+
Items: items,
|
|
86
|
+
Count: count,
|
|
87
|
+
ScannedCount: scannedCount,
|
|
88
|
+
};
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
async function batchCreateRecords(
|
|
91
|
+
async function batchCreateRecords(
|
|
92
|
+
tableName: string,
|
|
93
|
+
records: Record<string, unknown>[],
|
|
94
|
+
maxWritingCapacity?: number,
|
|
95
|
+
verbose = false,
|
|
96
|
+
) {
|
|
90
97
|
if (verbose) {
|
|
91
98
|
console.log(`creating ${records.length} items in ${tableName}`);
|
|
92
99
|
}
|
|
@@ -96,7 +103,7 @@ async function batchCreateRecords(tableName, records, maxWritingCapacity, verbos
|
|
|
96
103
|
// an arbitrary number to ensure while first loop is entered
|
|
97
104
|
let prevRemainingCount = remainingItems.length + 1;
|
|
98
105
|
let factor = 1;
|
|
99
|
-
let rejection = undefined;
|
|
106
|
+
let rejection: PromiseRejectedResult | undefined = undefined;
|
|
100
107
|
|
|
101
108
|
while (remainingItems.length > 0 && factor <= 128 && !rejection) {
|
|
102
109
|
if (prevRemainingCount === remainingItems.length) {
|
|
@@ -108,35 +115,36 @@ async function batchCreateRecords(tableName, records, maxWritingCapacity, verbos
|
|
|
108
115
|
console.log(`WARNING: no progress for a long time for batchCreateRecords, please check`);
|
|
109
116
|
}
|
|
110
117
|
|
|
111
|
-
const slices =
|
|
118
|
+
const slices = arrayGroup(remainingItems.slice(0, maxWritingCapacity), 25);
|
|
112
119
|
const results = await Promise.allSettled(
|
|
113
|
-
slices.map((rs) =>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return docClient.send(new BatchWriteCommand(params));
|
|
123
|
-
}),
|
|
120
|
+
slices.map((rs) =>
|
|
121
|
+
docClient.send(
|
|
122
|
+
new BatchWriteCommand({
|
|
123
|
+
RequestItems: {
|
|
124
|
+
[tableName]: rs.map((record) => ({ PutRequest: { Item: record } })),
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
),
|
|
128
|
+
),
|
|
124
129
|
);
|
|
125
130
|
|
|
131
|
+
const isFulfilled = <T>(p: PromiseSettledResult<T>): p is PromiseFulfilledResult<T> => p.status === "fulfilled";
|
|
132
|
+
const isRejected = <T>(p: PromiseSettledResult<T>): p is PromiseRejectedResult => p.status === "rejected";
|
|
133
|
+
|
|
126
134
|
prevRemainingCount = remainingItems.length;
|
|
135
|
+
remainingItems = remainingItems.slice(maxWritingCapacity);
|
|
127
136
|
|
|
128
|
-
|
|
129
|
-
if (rs
|
|
137
|
+
results.forEach((rs, idx) => {
|
|
138
|
+
if (isRejected(rs)) {
|
|
130
139
|
// whole slice fails, redo whole slice
|
|
131
|
-
|
|
140
|
+
remainingItems = remainingItems.concat(slices[idx]);
|
|
132
141
|
rejection = rs;
|
|
133
|
-
} else if (Object.keys(rs.value.UnprocessedItems).length > 0) {
|
|
142
|
+
} else if (isFulfilled(rs) && rs.value.UnprocessedItems && Object.keys(rs.value.UnprocessedItems).length > 0) {
|
|
134
143
|
// partially fails, redo unprocessedItems
|
|
135
|
-
|
|
144
|
+
const unprocessedItems = rs.value.UnprocessedItems[tableName].map((it) => it.PutRequest?.Item ?? []).flat();
|
|
145
|
+
remainingItems = remainingItems.concat(unprocessedItems);
|
|
136
146
|
}
|
|
137
|
-
|
|
138
|
-
return acc;
|
|
139
|
-
}, remainingItems.slice(maxWritingCapacity));
|
|
147
|
+
});
|
|
140
148
|
|
|
141
149
|
if (verbose) {
|
|
142
150
|
console.log(`processed=${prevRemainingCount - remainingItems.length}, remaining=${remainingItems.length}`);
|
|
@@ -156,7 +164,7 @@ async function batchCreateRecords(tableName, records, maxWritingCapacity, verbos
|
|
|
156
164
|
}
|
|
157
165
|
}
|
|
158
166
|
|
|
159
|
-
async function createRecord(tableName, fields, verbose = false) {
|
|
167
|
+
async function createRecord(tableName: string, fields: PutCommandInput["Item"], verbose = false) {
|
|
160
168
|
if (verbose) {
|
|
161
169
|
console.log("creating", tableName, fields);
|
|
162
170
|
}
|
|
@@ -170,7 +178,7 @@ async function createRecord(tableName, fields, verbose = false) {
|
|
|
170
178
|
return docClient.send(new PutCommand(params));
|
|
171
179
|
}
|
|
172
180
|
|
|
173
|
-
async function readRecord(tableName, key, verbose = false) {
|
|
181
|
+
async function readRecord<T>(tableName: string, key: GetCommandInput["Key"], verbose = false) {
|
|
174
182
|
if (verbose) {
|
|
175
183
|
console.log("reading", tableName, key);
|
|
176
184
|
}
|
|
@@ -184,16 +192,20 @@ async function readRecord(tableName, key, verbose = false) {
|
|
|
184
192
|
}),
|
|
185
193
|
);
|
|
186
194
|
|
|
187
|
-
return record.Item;
|
|
195
|
+
return record.Item as T;
|
|
188
196
|
}
|
|
189
197
|
|
|
190
|
-
async function getRecordsByKey
|
|
198
|
+
async function getRecordsByKey<TReturn, TKey extends Record<string, unknown> = Record<string, unknown>>(
|
|
199
|
+
tableName: string,
|
|
200
|
+
keys: TKey,
|
|
201
|
+
indexName?: string,
|
|
202
|
+
) {
|
|
191
203
|
const docClient = getDocClient();
|
|
192
204
|
|
|
193
205
|
const keyNames = Object.keys(keys);
|
|
194
206
|
const conditionExpression = keyNames.map((key) => `#${key} = :${key}`).join(" and ");
|
|
195
207
|
|
|
196
|
-
const params = {
|
|
208
|
+
const params: QueryCommandInput = {
|
|
197
209
|
TableName: tableName,
|
|
198
210
|
KeyConditionExpression: conditionExpression,
|
|
199
211
|
ExpressionAttributeNames: generateExpressionNames(keyNames),
|
|
@@ -204,12 +216,10 @@ async function getRecordsByKey(tableName, keys, indexName) {
|
|
|
204
216
|
params.IndexName = indexName;
|
|
205
217
|
}
|
|
206
218
|
|
|
207
|
-
let data;
|
|
208
|
-
|
|
209
219
|
try {
|
|
210
|
-
data = await docClient.send(new QueryCommand(params));
|
|
220
|
+
let data = await docClient.send(new QueryCommand(params));
|
|
211
221
|
|
|
212
|
-
let items = data.Items;
|
|
222
|
+
let items = data.Items ?? [];
|
|
213
223
|
|
|
214
224
|
while (data.LastEvaluatedKey) {
|
|
215
225
|
data = await docClient.send(
|
|
@@ -219,13 +229,15 @@ async function getRecordsByKey(tableName, keys, indexName) {
|
|
|
219
229
|
}),
|
|
220
230
|
);
|
|
221
231
|
|
|
222
|
-
|
|
232
|
+
if (data.Items) {
|
|
233
|
+
items = items.concat(data.Items);
|
|
234
|
+
}
|
|
223
235
|
}
|
|
224
236
|
|
|
225
|
-
return items;
|
|
237
|
+
return items as TReturn[];
|
|
226
238
|
} catch (err) {
|
|
227
239
|
console.log(err);
|
|
228
|
-
if (err
|
|
240
|
+
if (err instanceof Error && "statusCode" in err && err.statusCode === 400) {
|
|
229
241
|
return null;
|
|
230
242
|
}
|
|
231
243
|
|
|
@@ -234,9 +246,9 @@ async function getRecordsByKey(tableName, keys, indexName) {
|
|
|
234
246
|
}
|
|
235
247
|
|
|
236
248
|
// Dual purpose for compatibility. If indexName is provided, it will use query command to get the record; if not, use get command which is most efficient.
|
|
237
|
-
async function getRecordByKey(tableName, keys, indexName) {
|
|
249
|
+
async function getRecordByKey<T>(tableName: string, keys: Record<string, unknown>, indexName?: string) {
|
|
238
250
|
if (indexName) {
|
|
239
|
-
const records = await getRecordsByKey(tableName, keys, indexName);
|
|
251
|
+
const records = await getRecordsByKey<T>(tableName, keys, indexName);
|
|
240
252
|
|
|
241
253
|
if (records) {
|
|
242
254
|
return records[0];
|
|
@@ -244,25 +256,28 @@ async function getRecordByKey(tableName, keys, indexName) {
|
|
|
244
256
|
return null;
|
|
245
257
|
}
|
|
246
258
|
} else {
|
|
247
|
-
return readRecord(tableName, keys);
|
|
259
|
+
return readRecord<T>(tableName, keys);
|
|
248
260
|
}
|
|
249
261
|
}
|
|
250
262
|
|
|
251
|
-
function generateExpressionNames(keys) {
|
|
252
|
-
return keys.reduce((acc, key) => {
|
|
253
|
-
acc[`#${key}`] = key;
|
|
254
|
-
return acc;
|
|
255
|
-
}, {});
|
|
263
|
+
function generateExpressionNames(keys: string[]): QueryCommandInput["ExpressionAttributeNames"] {
|
|
264
|
+
return keys.reduce((acc, key) => ({ ...acc, [`#${key}`]: key }), {});
|
|
256
265
|
}
|
|
257
266
|
|
|
258
|
-
function generateExpressionValues(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}, {});
|
|
267
|
+
function generateExpressionValues(
|
|
268
|
+
keys: string[],
|
|
269
|
+
fields: Record<string, unknown>,
|
|
270
|
+
): QueryCommandInput["ExpressionAttributeValues"] {
|
|
271
|
+
return keys.reduce((acc, key) => ({ ...acc, [`:${key}`]: fields[key] }), {});
|
|
263
272
|
}
|
|
264
273
|
|
|
265
|
-
async function updateRecordByKey(
|
|
274
|
+
async function updateRecordByKey(
|
|
275
|
+
tableName: string,
|
|
276
|
+
idKey: Record<string, unknown>,
|
|
277
|
+
fields: Record<string, unknown>,
|
|
278
|
+
conditionExpressions = null,
|
|
279
|
+
verbose = false,
|
|
280
|
+
) {
|
|
266
281
|
if (verbose) {
|
|
267
282
|
console.log("update", tableName, idKey, fields);
|
|
268
283
|
}
|
|
@@ -273,14 +288,14 @@ async function updateRecordByKey(tableName, idKey, fields, conditionExpressions
|
|
|
273
288
|
const fieldsToDelete = Object.keys(fields).filter((f) => fields[f] === undefined);
|
|
274
289
|
const fieldsToUpdate = Object.keys(fields).filter((k) => !idKeyNames.includes(k) && !fieldsToDelete.includes(k));
|
|
275
290
|
|
|
276
|
-
let data;
|
|
291
|
+
let data: UpdateCommandOutput | undefined;
|
|
277
292
|
|
|
278
293
|
if (fieldsToDelete.length > 0) {
|
|
279
294
|
if (verbose) {
|
|
280
295
|
console.log("delete fields", tableName, fieldsToDelete);
|
|
281
296
|
}
|
|
282
297
|
|
|
283
|
-
const deleteParams = {
|
|
298
|
+
const deleteParams: UpdateCommandInput = {
|
|
284
299
|
TableName: tableName,
|
|
285
300
|
Key: idKey,
|
|
286
301
|
ExpressionAttributeNames: generateExpressionNames(fieldsToDelete),
|
|
@@ -302,7 +317,7 @@ async function updateRecordByKey(tableName, idKey, fields, conditionExpressions
|
|
|
302
317
|
|
|
303
318
|
const updateExpressions = fieldsToUpdate.map((key) => `#${key} = :${key}`);
|
|
304
319
|
|
|
305
|
-
const params = {
|
|
320
|
+
const params: UpdateCommandInput = {
|
|
306
321
|
TableName: tableName,
|
|
307
322
|
Key: idKey,
|
|
308
323
|
ExpressionAttributeNames: generateExpressionNames(fieldsToUpdate),
|
|
@@ -318,70 +333,10 @@ async function updateRecordByKey(tableName, idKey, fields, conditionExpressions
|
|
|
318
333
|
data = await docClient.send(new UpdateCommand(params));
|
|
319
334
|
}
|
|
320
335
|
|
|
321
|
-
return data
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
async function createTableIfNotExist(
|
|
325
|
-
tableName,
|
|
326
|
-
attributeDefinitions = [
|
|
327
|
-
{
|
|
328
|
-
AttributeName: "id",
|
|
329
|
-
AttributeType: "S",
|
|
330
|
-
},
|
|
331
|
-
],
|
|
332
|
-
keySchema = [
|
|
333
|
-
{
|
|
334
|
-
AttributeName: "id",
|
|
335
|
-
KeyType: "HASH",
|
|
336
|
-
},
|
|
337
|
-
],
|
|
338
|
-
otherOptions = {},
|
|
339
|
-
verbose = false,
|
|
340
|
-
) {
|
|
341
|
-
const dynamodb = getDynamoDB();
|
|
342
|
-
|
|
343
|
-
try {
|
|
344
|
-
const table = await dynamodb.send(new DescribeTableCommand({ TableName: tableName }));
|
|
345
|
-
|
|
346
|
-
if (verbose) {
|
|
347
|
-
console.log(`table ${tableName} already exist`, table);
|
|
348
|
-
}
|
|
349
|
-
return table;
|
|
350
|
-
} catch (_tableDoesNotExistErr) {
|
|
351
|
-
const createRequest = await dynamodb
|
|
352
|
-
.createTable({
|
|
353
|
-
TableName: tableName,
|
|
354
|
-
AttributeDefinitions: attributeDefinitions,
|
|
355
|
-
KeySchema: keySchema,
|
|
356
|
-
ProvisionedThroughput: {
|
|
357
|
-
ReadCapacityUnits: 5,
|
|
358
|
-
WriteCapacityUnits: 5,
|
|
359
|
-
},
|
|
360
|
-
...otherOptions,
|
|
361
|
-
})
|
|
362
|
-
.promise();
|
|
363
|
-
|
|
364
|
-
if (verbose) {
|
|
365
|
-
console.log(`table ${tableName} creating`);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
let isCreated = false;
|
|
369
|
-
|
|
370
|
-
while (!isCreated) {
|
|
371
|
-
await wait(5000);
|
|
372
|
-
|
|
373
|
-
const t = await dynamodb.send(new DescribeTableCommand({ TableName: tableName }));
|
|
374
|
-
|
|
375
|
-
console.log("waiting for table to become active");
|
|
376
|
-
|
|
377
|
-
isCreated = t.Table.TableStatus === "ACTIVE";
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
return createRequest;
|
|
381
|
-
}
|
|
336
|
+
return data?.Attributes;
|
|
382
337
|
}
|
|
383
338
|
|
|
384
|
-
async function batchDeleteRecords(tableName, keys) {
|
|
339
|
+
async function batchDeleteRecords(tableName: string, keys: Record<string, unknown>[]) {
|
|
385
340
|
const docClient = getDocClient();
|
|
386
341
|
|
|
387
342
|
for (let start = 0; start < keys.length; start += 25) {
|
|
@@ -399,37 +354,63 @@ async function batchDeleteRecords(tableName, keys) {
|
|
|
399
354
|
}
|
|
400
355
|
}
|
|
401
356
|
|
|
402
|
-
function getKeyName(keySchema, type) {
|
|
357
|
+
function getKeyName(keySchema: KeySchemaElement[], type: string) {
|
|
403
358
|
const key = keySchema.find((k) => k.KeyType === type);
|
|
404
359
|
|
|
405
|
-
return key
|
|
360
|
+
return key?.AttributeName;
|
|
406
361
|
}
|
|
407
362
|
|
|
408
|
-
function getIndexKeyName(globalSecondaryIndexes, indexName, type) {
|
|
363
|
+
function getIndexKeyName(globalSecondaryIndexes: GlobalSecondaryIndexDescription[], indexName: string, type: string) {
|
|
409
364
|
const idx = globalSecondaryIndexes.find((i) => i.IndexName === indexName);
|
|
410
365
|
|
|
411
|
-
return idx && getKeyName(idx.KeySchema, type);
|
|
366
|
+
return idx?.KeySchema && getKeyName(idx.KeySchema, type);
|
|
412
367
|
}
|
|
413
368
|
|
|
414
|
-
async function deleteRecordsByHashKey(
|
|
369
|
+
async function deleteRecordsByHashKey(
|
|
370
|
+
tableName: string,
|
|
371
|
+
indexName: string | undefined,
|
|
372
|
+
hashKeyValue: string,
|
|
373
|
+
verbose = false,
|
|
374
|
+
) {
|
|
415
375
|
const docClient = getDocClient();
|
|
416
376
|
|
|
417
377
|
const meta = await getDynamoDB().send(new DescribeTableCommand({ TableName: tableName }));
|
|
418
378
|
|
|
379
|
+
if (!meta.Table) {
|
|
380
|
+
throw new Error(`cannot find table ${tableName}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (indexName && !meta.Table.GlobalSecondaryIndexes) {
|
|
384
|
+
throw new Error(`cannot find global secondary indexes for table ${tableName}`);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (!meta.Table.KeySchema) {
|
|
388
|
+
throw new Error(`cannot find key schema for table ${tableName}`);
|
|
389
|
+
}
|
|
390
|
+
|
|
419
391
|
const hashKeyName = indexName
|
|
420
|
-
? getIndexKeyName(meta.Table.GlobalSecondaryIndexes
|
|
392
|
+
? getIndexKeyName(meta.Table.GlobalSecondaryIndexes!, indexName, "HASH")
|
|
421
393
|
: getKeyName(meta.Table.KeySchema, "HASH");
|
|
422
394
|
|
|
423
395
|
if (!hashKeyName) {
|
|
424
|
-
throw new Error(`cannot find
|
|
396
|
+
throw new Error(`cannot find hash key name for table ${tableName}`);
|
|
425
397
|
}
|
|
426
398
|
|
|
427
399
|
const mainHashKeyName = getKeyName(meta.Table.KeySchema, "HASH");
|
|
400
|
+
|
|
401
|
+
if (!mainHashKeyName) {
|
|
402
|
+
throw new Error(`cannot find main hash key name for table ${tableName}`);
|
|
403
|
+
}
|
|
404
|
+
|
|
428
405
|
const mainRangeKeyName = getKeyName(meta.Table.KeySchema, "RANGE");
|
|
429
406
|
|
|
407
|
+
if (!mainRangeKeyName) {
|
|
408
|
+
throw new Error(`cannot find main range key name for table ${tableName}`);
|
|
409
|
+
}
|
|
410
|
+
|
|
430
411
|
let totalDeleted = 0;
|
|
431
412
|
|
|
432
|
-
|
|
413
|
+
const params: QueryCommandInput = {
|
|
433
414
|
TableName: tableName,
|
|
434
415
|
KeyConditionExpression: "#hashKeyName = :hashKeyValue",
|
|
435
416
|
ExpressionAttributeNames: { "#hashKeyName": hashKeyName },
|
|
@@ -442,30 +423,7 @@ async function deleteRecordsByHashKey(tableName, indexName, hashKeyValue, verbos
|
|
|
442
423
|
|
|
443
424
|
let data = await docClient.send(new QueryCommand(params));
|
|
444
425
|
|
|
445
|
-
|
|
446
|
-
tableName,
|
|
447
|
-
data.Items.map((item) =>
|
|
448
|
-
mainRangeKeyName
|
|
449
|
-
? {
|
|
450
|
-
[mainHashKeyName]: item[mainHashKeyName],
|
|
451
|
-
[mainRangeKeyName]: item[mainRangeKeyName],
|
|
452
|
-
}
|
|
453
|
-
: {
|
|
454
|
-
[mainHashKeyName]: item[mainHashKeyName],
|
|
455
|
-
},
|
|
456
|
-
),
|
|
457
|
-
);
|
|
458
|
-
|
|
459
|
-
totalDeleted += data.Items.length;
|
|
460
|
-
|
|
461
|
-
while (data.LastEvaluatedKey) {
|
|
462
|
-
data = await docClient.send(
|
|
463
|
-
new QueryCommand({
|
|
464
|
-
...params,
|
|
465
|
-
ExclusiveStartKey: data.LastEvaluatedKey,
|
|
466
|
-
}),
|
|
467
|
-
);
|
|
468
|
-
|
|
426
|
+
if (data.Items) {
|
|
469
427
|
await batchDeleteRecords(
|
|
470
428
|
tableName,
|
|
471
429
|
data.Items.map((item) =>
|
|
@@ -483,6 +441,33 @@ async function deleteRecordsByHashKey(tableName, indexName, hashKeyValue, verbos
|
|
|
483
441
|
totalDeleted += data.Items.length;
|
|
484
442
|
}
|
|
485
443
|
|
|
444
|
+
while (data.LastEvaluatedKey) {
|
|
445
|
+
data = await docClient.send(
|
|
446
|
+
new QueryCommand({
|
|
447
|
+
...params,
|
|
448
|
+
ExclusiveStartKey: data.LastEvaluatedKey,
|
|
449
|
+
}),
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
if (data.Items) {
|
|
453
|
+
await batchDeleteRecords(
|
|
454
|
+
tableName,
|
|
455
|
+
data.Items.map((item) =>
|
|
456
|
+
mainRangeKeyName
|
|
457
|
+
? {
|
|
458
|
+
[mainHashKeyName]: item[mainHashKeyName],
|
|
459
|
+
[mainRangeKeyName]: item[mainRangeKeyName],
|
|
460
|
+
}
|
|
461
|
+
: {
|
|
462
|
+
[mainHashKeyName]: item[mainHashKeyName],
|
|
463
|
+
},
|
|
464
|
+
),
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
totalDeleted += data.Items.length;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
486
471
|
if (verbose) {
|
|
487
472
|
console.log(`successfully delete ${totalDeleted} items`);
|
|
488
473
|
}
|
|
@@ -490,16 +475,6 @@ async function deleteRecordsByHashKey(tableName, indexName, hashKeyValue, verbos
|
|
|
490
475
|
return totalDeleted;
|
|
491
476
|
}
|
|
492
477
|
|
|
493
|
-
async function transactWrite(items) {
|
|
494
|
-
const docClient = getDocClient();
|
|
495
|
-
|
|
496
|
-
return docClient.send(
|
|
497
|
-
new TransactWriteCommand({
|
|
498
|
-
TransactItems: items,
|
|
499
|
-
}),
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
478
|
export {
|
|
504
479
|
getDocClient,
|
|
505
480
|
// export common dynamodb commands to make it easier to use
|
|
@@ -517,6 +492,4 @@ export {
|
|
|
517
492
|
updateRecordByKey,
|
|
518
493
|
batchDeleteRecords,
|
|
519
494
|
deleteRecordsByHashKey,
|
|
520
|
-
createTableIfNotExist,
|
|
521
|
-
transactWrite,
|
|
522
495
|
};
|
package/env.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function ensureAndGet(envName: string, defaultValue?: string) {
|
|
2
|
+
return process.env[envName] || defaultValue;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function getEnvironment() {
|
|
6
|
+
return ensureAndGet("SKYNET_ENVIRONMENT", "dev");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getEnvOrThrow(envName: string) {
|
|
10
|
+
if (!process.env[envName]) {
|
|
11
|
+
throw new Error(`Must set environment variable ${envName}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return process.env[envName];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isProduction() {
|
|
18
|
+
return getEnvironment() === "prd";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isDev() {
|
|
22
|
+
return getEnvironment() === "dev";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { ensureAndGet, getEnvOrThrow, getEnvironment, isProduction, isDev };
|
|
@@ -7,19 +7,20 @@
|
|
|
7
7
|
// $ examples/api deploy
|
|
8
8
|
// $ examples/api --help
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import type { Request, Response, NextFunction } from "express";
|
|
11
|
+
import { api, SENSITIVE_VALUE } from "../app.ts";
|
|
11
12
|
|
|
12
13
|
// an example middleware
|
|
13
|
-
async function exampleMiddleware(req, res, next) {
|
|
14
|
+
async function exampleMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
14
15
|
console.log("before request!");
|
|
15
16
|
next();
|
|
16
17
|
console.log("after request!");
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
async function getProjectsHandler({ req, res, verbose }) {
|
|
20
|
+
async function getProjectsHandler({ req, res, verbose }: { req: Request; res: Response; verbose?: boolean }) {
|
|
20
21
|
console.log("receieved call", req.query, verbose);
|
|
21
22
|
|
|
22
|
-
const type = req.query
|
|
23
|
+
const type = req.query["type"];
|
|
23
24
|
|
|
24
25
|
if (!type) {
|
|
25
26
|
res.status(400).end("must provide type parameter");
|
|
@@ -29,7 +30,7 @@ async function getProjectsHandler({ req, res, verbose }) {
|
|
|
29
30
|
res.json([{ id: "proj-1", name: "Test Project 1" }]);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
async function createProjectHandler({ req, res, verbose }) {
|
|
33
|
+
async function createProjectHandler({ req, res, verbose }: { req: Request; res: Response; verbose?: boolean }) {
|
|
33
34
|
console.log("receieved call", req.body, verbose);
|
|
34
35
|
|
|
35
36
|
res.json({ success: true });
|
|
@@ -61,7 +62,7 @@ const app = api({
|
|
|
61
62
|
serve: {
|
|
62
63
|
prefix: "/example-api", // deployed to a sub path
|
|
63
64
|
port: 12345,
|
|
64
|
-
apiKey: process.env
|
|
65
|
+
apiKey: process.env["EXAMPLE_API_KEY"],
|
|
65
66
|
cpu: 600,
|
|
66
67
|
mem: 600,
|
|
67
68
|
instances: 3, // run 3 instances
|
|
@@ -7,15 +7,7 @@
|
|
|
7
7
|
// $ examples/indexer deploy --protocol eth
|
|
8
8
|
// $ examples/indexer --help
|
|
9
9
|
|
|
10
|
-
import { indexer, every
|
|
11
|
-
|
|
12
|
-
async function build({ protocol, verbose }) {
|
|
13
|
-
console.log("build called with", protocol, verbose);
|
|
14
|
-
|
|
15
|
-
if (protocol === "eth") {
|
|
16
|
-
throw new Error("use eth to test error handling");
|
|
17
|
-
}
|
|
18
|
-
}
|
|
10
|
+
import { indexer, every } from "../app.ts";
|
|
19
11
|
|
|
20
12
|
const app = indexer({
|
|
21
13
|
name: "example-indexer",
|
|
@@ -23,6 +15,7 @@ const app = indexer({
|
|
|
23
15
|
selector: {
|
|
24
16
|
// for more flags check meow documentation at https://github.com/sindresorhus/meow
|
|
25
17
|
protocol: {
|
|
18
|
+
isRequired: true,
|
|
26
19
|
type: "string",
|
|
27
20
|
description: "which chain to index",
|
|
28
21
|
},
|
|
@@ -33,7 +26,13 @@ const app = indexer({
|
|
|
33
26
|
},
|
|
34
27
|
|
|
35
28
|
build: {
|
|
36
|
-
func:
|
|
29
|
+
func: ({ protocol, verbose }) => {
|
|
30
|
+
console.log("build called with", protocol, verbose);
|
|
31
|
+
|
|
32
|
+
if (protocol === "eth") {
|
|
33
|
+
throw new Error("use eth to test error handling");
|
|
34
|
+
}
|
|
35
|
+
},
|
|
37
36
|
schedule: every(1).minute,
|
|
38
37
|
killTimeout: "90s",
|
|
39
38
|
cpu: 600,
|