@naanlang/naan 1.0.2 → 1.0.3
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/LICENSE.md +1 -1
- package/README.md +3 -3
- package/bin/index.js +2 -2
- package/dist/env_web.js +149 -20
- package/dist/naan.min.js +5 -5
- package/frameworks/browser/sworker.js +309 -91
- package/frameworks/browser/terminals.nlg +22 -9
- package/frameworks/browser/workers.nlg +19 -11
- package/frameworks/client/apiclient.nlg +17 -4
- package/frameworks/client/psm_client.nlg +116 -53
- package/frameworks/common/common.nlg +12 -2
- package/frameworks/node/apiserver.nlg +25 -13
- package/frameworks/node/filesystem.nlg +173 -30
- package/frameworks/node/psm_server.nlg +95 -32
- package/frameworks/project/build.nlg +40 -23
- package/frameworks/project/projects.nlg +47 -26
- package/frameworks/running/debugnub.nlg +2 -5
- package/frameworks/storage/csv.nlg +120 -0
- package/frameworks/storage/dbt_pouch.nlg +41 -25
- package/frameworks/storage/psm.nlg +108 -28
- package/frameworks/storage/psm_dbtables.nlg +7 -3
- package/frameworks/storage/resources.nlg +30 -2
- package/lib/browser/env_web.js +147 -18
- package/lib/browser/env_webworker.js +1 -1
- package/lib/browser/require.js +1 -1
- package/lib/core/naanlib.js +5 -5
- package/package.json +1 -1
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +1 -1
- package/plugins/serviceAws/aws_dynamo.nlg +625 -141
- package/plugins/serviceAws/aws_dynextra.nlg +294 -0
- package/plugins/serviceAws/dbt_aws.nlg +31 -11
- package/plugins/serviceAws/psm_aws.nlg +42 -26
- package/plugins/serviceAws/serviceAws.nlg +1 -0
- package/plugins/serviceGitHub/psm_github.nlg +41 -25
- package/plugins/serviceGitLab/psm_gitlab.nlg +41 -25
|
@@ -6,26 +6,164 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2021 by Richard C. Zulch
|
|
9
|
+
* Copyright (c) 2021-2022 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
/*
|
|
15
|
-
*
|
|
15
|
+
* TimeKeys
|
|
16
16
|
*
|
|
17
|
-
* DynamoDB
|
|
17
|
+
* TimeKeys implement a time-ordered key paradigm for DynamoDB and similar databases. Each item
|
|
18
|
+
* stored under a timeKey is assigned an ID comprising a timestamp and a random suffix derived from
|
|
19
|
+
* a version-4 UUID. The ID has a 26-character format: <13-digit timestamp>-<12-character-random>.
|
|
20
|
+
* A prefix option inserts a fixed-length string before the timestamp, enabling categorization when
|
|
21
|
+
* multiple datatypes inhabit the same keyspace. From the ID is derived a partition key in the form
|
|
22
|
+
* YYYYDDD, where DDD is the day of the year from 1 to 365. TimeKeys have the following useful
|
|
23
|
+
* properties:
|
|
24
|
+
* - IDs sort in date order
|
|
25
|
+
* - IDs allow direct access
|
|
26
|
+
* - Over time IDs become distributed over partitions
|
|
27
|
+
* - Can iterate partitions to query longer timeframes
|
|
28
|
+
*
|
|
29
|
+
* The principal challenge is that IDs can cause a hot partition problem if you try to create them
|
|
30
|
+
* too quickly, because they all share the same day. Likewise if you access recently-created IDs
|
|
31
|
+
* frequently. In that case a separate table for today's items might be preferable.
|
|
32
|
+
* A potential challenge is that rarely-created keys will be sparsely distributed among the
|
|
33
|
+
* potential partition keys. For data is not created every day, you may want to cache the available
|
|
34
|
+
* days, or choose a different key paradigm.
|
|
18
35
|
*
|
|
19
36
|
*/
|
|
20
37
|
|
|
21
|
-
closure
|
|
22
|
-
|
|
23
|
-
|
|
38
|
+
closure TimeKeys(prefix, baseMS, local timekeys, prelen) {
|
|
39
|
+
if !UUID.proc
|
|
40
|
+
throw("TimeKeys requires UUID()")
|
|
41
|
+
timekeys = new(object, this)
|
|
42
|
+
if !prefix
|
|
43
|
+
prefix = ""
|
|
44
|
+
prelen = prefix.length
|
|
45
|
+
if !baseMS
|
|
46
|
+
baseMS = 1640995200000 // 01/01/2022 UTC - earliest valid date
|
|
47
|
+
|
|
48
|
+
// utcYearStartMS
|
|
49
|
+
//
|
|
50
|
+
// Get the millisecond timestamp for the start of the given UTC year.
|
|
51
|
+
//
|
|
52
|
+
function utcYearStartMS(year) {
|
|
53
|
+
Date("01/01/".concat(year, "GMT")).getTime()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// makePartID
|
|
57
|
+
//
|
|
58
|
+
// Return the partition ID for a millisecond timestamp, in the form YYYYDDD where DDD is the day
|
|
59
|
+
// of the year from 001 to 365. If the timestamp is invalid it returns false.
|
|
60
|
+
//
|
|
61
|
+
timekeys.makePartID = function makePartID(msec, local tsdate, yyyy, doy) {
|
|
62
|
+
if msec >= baseMS {
|
|
63
|
+
tsdate = Date(msec)
|
|
64
|
+
yyyy = tsdate.getUTCFullYear()
|
|
65
|
+
doy = Math.floor((msec - utcYearStartMS(yyyy))/(1000*3600*24))
|
|
66
|
+
prefix.concat(yyyy.tostring, "000".concat(doy+1).slice(-3))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// comparePartID
|
|
71
|
+
//
|
|
72
|
+
// Compare two partID values, returning -1, 0, 1 if the first is <, ==, or > to the second.
|
|
73
|
+
//
|
|
74
|
+
timekeys.comparePartID = function comparePartID(a, b) {
|
|
75
|
+
a = a.slice(prelen)
|
|
76
|
+
b = b.slice(prelen)
|
|
77
|
+
if (a < b)
|
|
78
|
+
-1
|
|
79
|
+
else if a > b
|
|
80
|
+
1
|
|
81
|
+
else
|
|
82
|
+
0
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// adjustPartID
|
|
86
|
+
//
|
|
87
|
+
// Increment or decrement the partition key by the specified number of days returning the next
|
|
88
|
+
// partition key in the specified direction, or false if there are no more keys. Dates in the
|
|
89
|
+
// future are considered invalid and return false.
|
|
90
|
+
//
|
|
91
|
+
timekeys.adjustPartID = function adjustPartID(partID, adjust, local year, doy, dayms, msec) {
|
|
92
|
+
partID = partID.slice(prelen)
|
|
93
|
+
yyyy = toint(partID.substring(0,4))
|
|
94
|
+
doy = toint(partID.substring(4,7))
|
|
95
|
+
dayms = 1000*3600*24
|
|
96
|
+
msec = utcYearStartMS(yyyy) + (doy-1) * dayms
|
|
97
|
+
msec += adjust * dayms
|
|
98
|
+
if msec < Date.now()
|
|
99
|
+
makePartID(msec)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// partIDfromTimeID
|
|
103
|
+
//
|
|
104
|
+
// Return the partID for a timeID.
|
|
105
|
+
//
|
|
106
|
+
timekeys.partIDfromTimeID = function partIDfromTimeID(timeID) {
|
|
107
|
+
makePartID(timeIDtimestamp(timeID))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// newTimeID
|
|
111
|
+
//
|
|
112
|
+
// Return a new, unique timeID for the present moment.
|
|
113
|
+
//
|
|
114
|
+
timekeys.newTimeID = function newTimeID(now) {
|
|
115
|
+
if !now
|
|
116
|
+
now = Date.now()
|
|
117
|
+
prefix.concat(now.tostring, UUID().slice(-13))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// isTimeID
|
|
121
|
+
//
|
|
122
|
+
// Return true iff this looks like our timeID with the right prefix, as opposed to a timestamp.
|
|
123
|
+
//
|
|
124
|
+
timekeys.isTimeID = function isTimeID(tstid) {
|
|
125
|
+
string(tstid) && tstid.startsWith(prefix) && tstid.length-prelen == 26
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// timeIDtimestamp
|
|
129
|
+
//
|
|
130
|
+
// Return the numeric millisecond timestamp for a timeID.
|
|
131
|
+
//
|
|
132
|
+
timekeys.timeIDtimestamp = function timeIDtimestamp(timeID) {
|
|
133
|
+
toint(timeID.slice(prelen, prelen+13))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// makeTimeKey
|
|
137
|
+
//
|
|
138
|
+
// Make a range key from a timestamp, creating the earliest possible range key, which will sort
|
|
139
|
+
// earlier than all possible timeIDs for that timestamp.
|
|
140
|
+
//
|
|
141
|
+
timekeys.makeTimeKey = function makeTimeKey(timestamp) {
|
|
142
|
+
if !integer(timestamp) || timestamp < baseMS
|
|
143
|
+
timestamp = baseMS
|
|
144
|
+
prefix.concat(timestamp.tostring, "-", space.repeat(12))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// finis
|
|
148
|
+
|
|
149
|
+
timekeys
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
* DynaConverter
|
|
155
|
+
*
|
|
156
|
+
* Translate between Naan and DynamoDB.
|
|
157
|
+
*
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
closure DynaConverter(local conv) {
|
|
161
|
+
conv = new(object, this)
|
|
24
162
|
|
|
25
163
|
// fieldTypeDynToNaan
|
|
26
164
|
// Convert field type from DynamoDB to Naan
|
|
27
165
|
//
|
|
28
|
-
function fieldTypeDynToNaan(dtype) {
|
|
166
|
+
conv.fieldTypeDynToNaan = function fieldTypeDynToNaan(dtype) {
|
|
29
167
|
if dtype == "S",
|
|
30
168
|
"utf8"
|
|
31
169
|
else if dtype == "N",
|
|
@@ -39,7 +177,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
39
177
|
// fieldTypeNaanToDyn
|
|
40
178
|
// Convert field type from Naan to DynamoDB
|
|
41
179
|
//
|
|
42
|
-
function fieldTypeNaanToDyn(ntype) {
|
|
180
|
+
conv.fieldTypeNaanToDyn = function fieldTypeNaanToDyn(ntype) {
|
|
43
181
|
if ntype == "utf8",
|
|
44
182
|
"S"
|
|
45
183
|
else if ntype == "numeric",
|
|
@@ -53,7 +191,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
53
191
|
// datumNaanToDyn
|
|
54
192
|
// Convert a Naan datum to DynamoDB format.
|
|
55
193
|
//
|
|
56
|
-
function datumNaanToDyn(datum, local
|
|
194
|
+
conv.datumNaanToDyn = function datumNaanToDyn(datum, local ntype, dtype) {
|
|
57
195
|
output = { }
|
|
58
196
|
if tuple(datum)
|
|
59
197
|
datum = datum.toarray
|
|
@@ -84,11 +222,12 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
84
222
|
else if datum === true || datum === false
|
|
85
223
|
dtype = "BOOL"
|
|
86
224
|
else if datum === undefined
|
|
87
|
-
|
|
225
|
+
return (undefined) // Zen!
|
|
88
226
|
else
|
|
89
|
-
|
|
227
|
+
return (datum.tostring)
|
|
228
|
+
|
|
90
229
|
} else
|
|
91
|
-
debuglog("
|
|
230
|
+
debuglog("DynaConverter.datumNaanToDyn: invalid type", ntype)
|
|
92
231
|
}
|
|
93
232
|
output[dtype] = datum
|
|
94
233
|
output
|
|
@@ -97,7 +236,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
97
236
|
// recordNaanToDyn
|
|
98
237
|
// Convert a Naan dictionary to DynamoDB format.
|
|
99
238
|
//
|
|
100
|
-
function recordNaanToDyn(nitem, local output, key, data) {
|
|
239
|
+
conv.recordNaanToDyn = function recordNaanToDyn(nitem, local output, key, data) {
|
|
101
240
|
output = { }
|
|
102
241
|
for `(key, data) in nitem
|
|
103
242
|
output[key] = datumNaanToDyn(data)
|
|
@@ -107,7 +246,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
107
246
|
// datumDynToNaan
|
|
108
247
|
// Convert a DynamoDB typed attribute to Naan format.
|
|
109
248
|
//
|
|
110
|
-
function datumDynToNaan(dtype, dvalue, local output) {
|
|
249
|
+
conv.datumDynToNaan = function datumDynToNaan(dtype, dvalue, local output) {
|
|
111
250
|
if dtype == "BS" || dtype == "SS"
|
|
112
251
|
dvalue
|
|
113
252
|
else if dtype == "NS"
|
|
@@ -136,45 +275,219 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
136
275
|
else if dtype == "B" || dtype == "S"
|
|
137
276
|
dvalue
|
|
138
277
|
else {
|
|
139
|
-
debuglog("
|
|
278
|
+
debuglog("DynaConverter.datumDynToNaan: unknown type", dtype, typeof(dvalue), dvalue)
|
|
140
279
|
false }
|
|
141
280
|
}
|
|
142
281
|
|
|
143
282
|
// recordDynToNaan
|
|
144
283
|
// Convert a DynamoDB typed item dictionary to Naan format.
|
|
145
284
|
//
|
|
146
|
-
function recordDynToNaan(ditem, local output, key, data, dtype, dvalue) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
285
|
+
conv.recordDynToNaan = function recordDynToNaan(ditem, local output, key, data, dtype, dvalue) {
|
|
286
|
+
if ditem.constructor === Array.prototype.constructor || array(ditem) { // process an array
|
|
287
|
+
output = []
|
|
288
|
+
for data in ditem
|
|
289
|
+
output.push(recordDynToNaan(data))
|
|
290
|
+
}
|
|
291
|
+
else { // process a single expression
|
|
292
|
+
output = { }
|
|
293
|
+
for `(key, data) in ditem {
|
|
294
|
+
dtype = data.*.0
|
|
295
|
+
dvalue = data[dtype]
|
|
296
|
+
output[key] = datumDynToNaan(dtype, dvalue)
|
|
297
|
+
}
|
|
152
298
|
}
|
|
153
299
|
output
|
|
154
300
|
}
|
|
155
|
-
|
|
301
|
+
|
|
302
|
+
// genAttributeAdder
|
|
156
303
|
//
|
|
157
|
-
//
|
|
304
|
+
// Return a function that, when called repeatedly, will add attribute definitions to the
|
|
305
|
+
// specified parameter block. Symbols are interpreted as literal names, e.g. attribute names, and
|
|
306
|
+
// are not defined as values here.
|
|
158
307
|
//
|
|
159
|
-
|
|
160
|
-
|
|
308
|
+
closure genAttributeAdder(params, local adex) {
|
|
309
|
+
function attrib(expr, local adef, atype, key, data) {
|
|
310
|
+
if symbol(expr) && !member(expr, `(true, false, null))
|
|
311
|
+
return (expr.tostring) // literal attribute name
|
|
312
|
+
if !params.ExpressionAttributeValues
|
|
313
|
+
params.ExpressionAttributeValues = { }
|
|
314
|
+
if !adex
|
|
315
|
+
adex = (keys(params.ExpressionAttributeValues).length || 0) + 1
|
|
316
|
+
adef = datumNaanToDyn(expr)
|
|
317
|
+
atype = keys(adef).0
|
|
318
|
+
for `(key, data) in params.ExpressionAttributeValues
|
|
319
|
+
if data[atype] == adef[atype]
|
|
320
|
+
return (key) // found matching attribute
|
|
321
|
+
key = ":v".concat(adex++)
|
|
322
|
+
params.ExpressionAttributeValues[key] = adef
|
|
323
|
+
key
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
161
327
|
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
328
|
+
// createKeyExpression
|
|
329
|
+
//
|
|
330
|
+
// Update the specified parameter block with one or more condition expressions, preserving any
|
|
331
|
+
// that are already present. Specify each condition with a dictionary as follows:
|
|
332
|
+
// {
|
|
333
|
+
// [KeyConditionExpression: <condition-dictionary>]
|
|
334
|
+
// [FilterExpression: <condition-dictionary]
|
|
335
|
+
// [etc.]
|
|
336
|
+
// }
|
|
337
|
+
// If successful the parameter block is updated, otherwise it may be partly updated. The return
|
|
338
|
+
// value is a standard (error, data) tuple where the data is just { ok: true } if no error.
|
|
339
|
+
// This will parse only one expression per dictionary, but as many such conditions as you
|
|
340
|
+
// you wish to use by specifying an array. This does not check for semantics enforced by the
|
|
341
|
+
// API. E.g. Key conditions can only have one condition expression for the sort key, and only
|
|
342
|
+
// equality is allowed for the partition key. Only filter expressions can use the <> operator.
|
|
343
|
+
//
|
|
344
|
+
// condition dictionary:
|
|
345
|
+
// {
|
|
346
|
+
// key: <keyname, e.g. partition key name> // required
|
|
347
|
+
// "=": <expression> // exclusive of other comparisons
|
|
348
|
+
// ">": <expression> // exclusive of other comparisons
|
|
349
|
+
// "<": <expression> // exclusive of other comparisons
|
|
350
|
+
// "<>": <expression> // exclusive of other comparisons
|
|
351
|
+
// beginsWith: <string-expression> // exclusive of other comparisons
|
|
352
|
+
// ">=": <expression> // with <= or alone
|
|
353
|
+
// "<=": <expression> // with >= or alone
|
|
354
|
+
// }
|
|
355
|
+
//
|
|
356
|
+
|
|
357
|
+
conv.createKeyExpression = function createKeyExpression(params, conds
|
|
358
|
+
local error, attrib, rangename, range, keyname, op, expr, rangex, arg1, arg2) {
|
|
359
|
+
|
|
360
|
+
// dupeop
|
|
361
|
+
//
|
|
362
|
+
// Return a duplicate operation error.
|
|
363
|
+
function dupeop() {
|
|
364
|
+
list(Error("key expression cannot combine", op, "with", rangex.0))
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
//
|
|
368
|
+
// build the expression
|
|
369
|
+
//
|
|
370
|
+
attrib = genAttributeAdder(params)
|
|
371
|
+
for `(rangename, ranges) in conds {
|
|
372
|
+
if !array(ranges)
|
|
373
|
+
ranges = [ranges]
|
|
374
|
+
for range in ranges {
|
|
375
|
+
rangex = false
|
|
376
|
+
keyname = tostring(range.key)
|
|
377
|
+
if !keyname
|
|
378
|
+
return (list(Error("key expression requires key name")))
|
|
379
|
+
for `(op, expr) in range {
|
|
380
|
+
if op == "key"
|
|
381
|
+
continue
|
|
382
|
+
expr = attrib(expr)
|
|
383
|
+
if op == ">=" {
|
|
384
|
+
if rangex.0 == "<="
|
|
385
|
+
rangex = list("between", expr, rangex.1)
|
|
386
|
+
else if rangex.0
|
|
387
|
+
return (dupeop())
|
|
388
|
+
else
|
|
389
|
+
rangex = list(op, expr) }
|
|
390
|
+
else if op == "<=" {
|
|
391
|
+
if rangex.0 == ">="
|
|
392
|
+
rangex = list("between", rangex.1, expr)
|
|
393
|
+
else if rangex.0
|
|
394
|
+
return (dupeop())
|
|
395
|
+
else
|
|
396
|
+
rangex = list(op, expr) }
|
|
397
|
+
else if rangex.0
|
|
398
|
+
return (dupeop())
|
|
399
|
+
else
|
|
400
|
+
rangex = list(op, expr)
|
|
401
|
+
}
|
|
402
|
+
if rangex {
|
|
403
|
+
arg1 = rangex.1
|
|
404
|
+
if rangex.0 == "between" {
|
|
405
|
+
arg2 = rangex.2
|
|
406
|
+
expr = keyname.concat(" between ", arg1, " and ", arg2)
|
|
407
|
+
}
|
|
408
|
+
else if rangex.0 == "beginsWith"
|
|
409
|
+
expr = "begins_with(".concat(keyname, ", ", arg1, ")")
|
|
410
|
+
else
|
|
411
|
+
expr = keyname.concat(space, rangex.0, space, arg1)
|
|
412
|
+
if params[rangename]
|
|
413
|
+
params[rangename] = params[rangename].concat(" and ", expr)
|
|
414
|
+
else
|
|
415
|
+
params[rangename] = expr
|
|
416
|
+
}
|
|
173
417
|
}
|
|
174
|
-
}
|
|
418
|
+
}
|
|
419
|
+
list(false, { ok: true })
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// createUpdateExpression
|
|
423
|
+
//
|
|
424
|
+
// Update the specified parameter block with one or more update expressions, preserving any that
|
|
425
|
+
// are already present. Specify the updates with a dictionary as follows:
|
|
426
|
+
// {
|
|
427
|
+
// set: [`(attribute, value)] // set attributes to values
|
|
428
|
+
// sum: [`(attribute, value)] // attributes += values
|
|
429
|
+
// diff: [`(attribute, value)] // attributes -= values
|
|
430
|
+
// remove: [attribute] // remove attributes from the record
|
|
431
|
+
// }
|
|
432
|
+
// If successful the parameter block is updated, otherwise it may be partly updated. The return
|
|
433
|
+
// value is a standard (error, data) tuple where the data is just { ok: true } if no error.
|
|
434
|
+
//
|
|
435
|
+
conv.createUpdateExpression = function createUpdateExpression(params, updates,
|
|
436
|
+
local attrib, output, item) {
|
|
437
|
+
|
|
438
|
+
if params.UpdateExpression
|
|
439
|
+
return (list(Error("conv.createUpdateExpression: UpdateExpression already exists")))
|
|
440
|
+
attrib = genAttributeAdder(params)
|
|
441
|
+
output = {
|
|
442
|
+
SET: []
|
|
443
|
+
REMOVE: []
|
|
444
|
+
}
|
|
445
|
+
for item in updates.set
|
|
446
|
+
output.SET.push(strcat(item.0, "=", attrib(item.1)))
|
|
447
|
+
for item in updates.sum
|
|
448
|
+
output.SET.push(strcat(item.0, "=", item.0, "+", attrib(item.1)))
|
|
449
|
+
for item in updates.diff
|
|
450
|
+
output.SET.push(strcat(item.0, "=", item.0, "-", attrib(item.1)))
|
|
451
|
+
for item in updates.remove
|
|
452
|
+
output.REMOVE.push(tostring(item))
|
|
453
|
+
params.UpdateExpression = ""
|
|
454
|
+
for `(key, value) in output
|
|
455
|
+
params.UpdateExpression = params.UpdateExpression.concat(key, space, value.join(", "))
|
|
456
|
+
list(false, { ok: true })
|
|
175
457
|
}
|
|
176
458
|
|
|
459
|
+
// genBackoffDelayer
|
|
177
460
|
//
|
|
461
|
+
// Return a function that, when called repeatedly, will sleep for an exponentially increasing
|
|
462
|
+
// maximum time interval starting with 50 msec and doubling each time. The actual delay varies by
|
|
463
|
+
// a random jitter of up to 100% of the delay. The function returns the maximum milliseconds of
|
|
464
|
+
// the next delay, so the caller can give up if this becomes too large.
|
|
465
|
+
//
|
|
466
|
+
conv.genBackoffDelayer = closure genBackoffDelayer(local delay) {
|
|
467
|
+
delay = 50
|
|
468
|
+
function delayer() {
|
|
469
|
+
sleep(delay * Math.random()) // jitter up to 100%
|
|
470
|
+
delay *= 2
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// finis
|
|
475
|
+
|
|
476
|
+
conv
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
/*
|
|
481
|
+
* DynaTable
|
|
482
|
+
*
|
|
483
|
+
* DynamoDB table.
|
|
484
|
+
*
|
|
485
|
+
*/
|
|
486
|
+
|
|
487
|
+
closure DynaTable(dyna, tablename, local table) {
|
|
488
|
+
table = new(object, this)
|
|
489
|
+
table.name = tablename
|
|
490
|
+
|
|
178
491
|
// info
|
|
179
492
|
//
|
|
180
493
|
// Return info about the table, or an error, in standard (error, dictionary) tuple format.
|
|
@@ -203,6 +516,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
203
516
|
if error {
|
|
204
517
|
dynaTableError = error
|
|
205
518
|
debuglog("aws.describeTable failed", tablename, error)
|
|
519
|
+
error = Error("table.info(".concat(tablename, ") failed"), error)
|
|
206
520
|
} else {
|
|
207
521
|
data = data.Table
|
|
208
522
|
output = {
|
|
@@ -219,18 +533,17 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
219
533
|
output.range = key.AttributeName
|
|
220
534
|
for key in data.AttributeDefinitions
|
|
221
535
|
if key.AttributeName == output.hash
|
|
222
|
-
output.hashType = fieldTypeDynToNaan(key.AttributeType)
|
|
536
|
+
output.hashType = dyna.conv.fieldTypeDynToNaan(key.AttributeType)
|
|
223
537
|
else if key.AttributeName == output.range
|
|
224
|
-
output.rangeType = fieldTypeDynToNaan(key.AttributeType)
|
|
225
|
-
table.hashKey = output.hash
|
|
226
|
-
table.rangeKey = output.range
|
|
538
|
+
output.rangeType = dyna.conv.fieldTypeDynToNaan(key.AttributeType)
|
|
539
|
+
table.hashKey = compress(output.hash) // hash attribute name
|
|
540
|
+
table.rangeKey = compress(output.range) // range attribute name or false
|
|
227
541
|
}
|
|
228
542
|
pending.signal(list(error, output))
|
|
229
543
|
})
|
|
230
544
|
pending.wait()
|
|
231
545
|
}
|
|
232
546
|
|
|
233
|
-
//
|
|
234
547
|
// create
|
|
235
548
|
//
|
|
236
549
|
// Create the table with specified parameters, blocking until the table is ready for use.
|
|
@@ -242,11 +555,11 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
242
555
|
// range: <name> // primary range key attribute name (optional)
|
|
243
556
|
// rangeType: <name> // string | numeric (if range specified)
|
|
244
557
|
// }
|
|
245
|
-
|
|
558
|
+
//
|
|
246
559
|
table.create = closure create(parameters, local hashType, rangeType, params, pending, error, data, start) {
|
|
247
560
|
if !dyna.aws
|
|
248
561
|
return (list(Error("DynaTable.create: not logged into AWS")))
|
|
249
|
-
hashType = fieldTypeNaanToDyn(parameters.hashType)
|
|
562
|
+
hashType = dyna.conv.fieldTypeNaanToDyn(parameters.hashType)
|
|
250
563
|
if !parameters.hash || !hashType
|
|
251
564
|
return (list(Error("DynoTable.create: primary key hash/hashType required")))
|
|
252
565
|
params = {
|
|
@@ -265,7 +578,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
265
578
|
]
|
|
266
579
|
}
|
|
267
580
|
if parameters.range {
|
|
268
|
-
rangeType = fieldTypeNaanToDyn(parameters.rangeType)
|
|
581
|
+
rangeType = dyna.conv.fieldTypeNaanToDyn(parameters.rangeType)
|
|
269
582
|
if !rangeType
|
|
270
583
|
return (list(Error("DynoTable.create: range requires rangeType")))
|
|
271
584
|
params.AttributeDefinitions.push({
|
|
@@ -282,6 +595,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
282
595
|
if error {
|
|
283
596
|
dynaTableError = error
|
|
284
597
|
debuglog("aws.createTable failed", tablename, error)
|
|
598
|
+
error = Error("table.create(".concat(tablename, ") failed"), error)
|
|
285
599
|
}
|
|
286
600
|
pending.signal(list(error, data))
|
|
287
601
|
})
|
|
@@ -293,7 +607,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
293
607
|
`(error, data) = info()
|
|
294
608
|
if data.status == "ACTIVE"
|
|
295
609
|
break
|
|
296
|
-
if milliseconds() - start >
|
|
610
|
+
if milliseconds() - start > dyna.timeout { // 20 seconds
|
|
297
611
|
if !error
|
|
298
612
|
error = data.status
|
|
299
613
|
return(list(Error("DynoTable.create: timeout waiting for new table to become active", error)))
|
|
@@ -302,27 +616,81 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
302
616
|
}
|
|
303
617
|
list(false, data)
|
|
304
618
|
}
|
|
305
|
-
|
|
619
|
+
|
|
620
|
+
// preflightError
|
|
306
621
|
//
|
|
307
|
-
//
|
|
622
|
+
// Ensure that the table is ready before executing an operation. This returns an error tuple or
|
|
623
|
+
// false to proceed.
|
|
308
624
|
//
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
625
|
+
function preflightError(op) {
|
|
626
|
+
if !dyna.aws
|
|
627
|
+
return (list(Error("DynaTable.".concat(op, ": not logged into AWS"))))
|
|
312
628
|
if !table.hashKey {
|
|
313
629
|
result = info()
|
|
314
630
|
if result.0
|
|
315
631
|
return(result)
|
|
316
632
|
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// makeDynKey
|
|
636
|
+
//
|
|
637
|
+
// Make a dynamoDB key dictionary for the table using the specified primary key, which can be a
|
|
638
|
+
// tuple of hash/range or just a hash if only a hash is needed for this table.
|
|
639
|
+
//
|
|
640
|
+
conv.makeDynKey = function makeDynKey(primaryKey, local hashValue, rangeValue, key) {
|
|
641
|
+
if tuple(primaryKey)
|
|
642
|
+
`(hashValue, rangeValue) = primaryKey
|
|
643
|
+
else
|
|
644
|
+
hashValue = primaryKey
|
|
645
|
+
key = { }
|
|
646
|
+
key[table.hashKey] = dyna.conv.datumNaanToDyn(hashValue)
|
|
647
|
+
if table.rangeKey
|
|
648
|
+
key[table.rangeKey] = dyna.conv.datumNaanToDyn(rangeValue)
|
|
649
|
+
key
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// getRecord
|
|
653
|
+
//
|
|
654
|
+
// Get a record from the table with the specified hash value, and optional range value.
|
|
655
|
+
//
|
|
656
|
+
table.getRecord = closure getRecord(hashValue, rangeValue, local result, params, pending) {
|
|
657
|
+
if preflightError(this)
|
|
658
|
+
return
|
|
659
|
+
params = {
|
|
660
|
+
TableName: tablename
|
|
661
|
+
}
|
|
662
|
+
params.Key = makeDynKey(list(hashValue, rangeValue))
|
|
663
|
+
pending = new(nonce)
|
|
664
|
+
dyna.aws.getItem(params, function(error, data, local output) {
|
|
665
|
+
if error {
|
|
666
|
+
dynaTableError = error
|
|
667
|
+
debuglog("aws.getItem failed", tablename, error)
|
|
668
|
+
error = Error("table.getRecord(".concat(tablename, ") failed"), error)
|
|
669
|
+
} else
|
|
670
|
+
data = dyna.conv.recordDynToNaan(data.Item)
|
|
671
|
+
pending.signal(list(error, data))
|
|
672
|
+
})
|
|
673
|
+
pending.wait()
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
//
|
|
677
|
+
// putRecord
|
|
678
|
+
//
|
|
679
|
+
// Put an record in the table, replacing any existing record with the same key.
|
|
680
|
+
|
|
681
|
+
table.putRecord = closure putRecord(record, local result, params, pending) {
|
|
682
|
+
if preflightError(this)
|
|
683
|
+
return
|
|
317
684
|
params = {
|
|
318
685
|
TableName: tablename
|
|
319
|
-
Item: recordNaanToDyn(record)
|
|
686
|
+
Item: dyna.conv.recordNaanToDyn(record)
|
|
320
687
|
}
|
|
321
688
|
pending = new(nonce)
|
|
322
689
|
dyna.aws.putItem(params, function(error, data, local output) {
|
|
323
690
|
if error {
|
|
324
691
|
dynaTableError = error
|
|
325
692
|
debuglog("aws.putItem failed", tablename, error)
|
|
693
|
+
error = Error("table.putRecord(".concat(tablename, ") failed"), error)
|
|
326
694
|
}
|
|
327
695
|
pending.signal(list(error, data))
|
|
328
696
|
})
|
|
@@ -330,113 +698,197 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
330
698
|
}
|
|
331
699
|
|
|
332
700
|
//
|
|
333
|
-
//
|
|
701
|
+
// updateRecord
|
|
334
702
|
//
|
|
335
|
-
//
|
|
703
|
+
// Update a record in the table. This will update the existing record if it exists, or add a new
|
|
704
|
+
// one. Options include conditional updates, and adding or removing attributes as follows:
|
|
705
|
+
// {
|
|
706
|
+
// condition: <range-expression> // conditional expression as in createKeyExpression()
|
|
707
|
+
// set: [`(attribute, value)] // set attributes to values
|
|
708
|
+
// sum: [`(attribute, value)] // attributes += values
|
|
709
|
+
// diff: [`(attribute, value)] // attributes -= values
|
|
710
|
+
// remove: [attribute] // remove attributes from the record
|
|
711
|
+
// return: "all_old" | "all_new" | "updated_old" | "updated_new"
|
|
712
|
+
// // return the record as chosen, otherwise { }
|
|
713
|
+
// }
|
|
714
|
+
// The result is a standard result tuple `(error, record).
|
|
336
715
|
|
|
337
|
-
table.
|
|
338
|
-
if
|
|
339
|
-
|
|
340
|
-
if result.0
|
|
341
|
-
return(result)
|
|
342
|
-
}
|
|
716
|
+
table.updateRecord = closure updateRecord(hashValue, rangeValue, options, local error, result, params, actions, pending) {
|
|
717
|
+
if preflightError(this)
|
|
718
|
+
return
|
|
343
719
|
params = {
|
|
344
720
|
TableName: tablename
|
|
345
|
-
|
|
721
|
+
Item: dyna.conv.recordNaanToDyn(record)
|
|
722
|
+
}
|
|
723
|
+
params.Key = makeDynKey(list(hashValue, rangeValue))
|
|
724
|
+
if options.condition {
|
|
725
|
+
`(error) = dyna.conv.createKeyExpression(params, {
|
|
726
|
+
KeyConditionExpression: {
|
|
727
|
+
key: table.hashKey
|
|
728
|
+
"=": hashValue
|
|
729
|
+
}
|
|
730
|
+
})
|
|
731
|
+
if error
|
|
732
|
+
return (list(Error("table.updateRecord:", error)))
|
|
733
|
+
}
|
|
734
|
+
dyna.conv.createUpdateExpression(params, `SET, options)
|
|
735
|
+
if options.return {
|
|
736
|
+
params.ReturnValues = {
|
|
737
|
+
all_old: "ALL_OLD"
|
|
738
|
+
all_new: "ALL_NEW"
|
|
739
|
+
updated_old: "UPDATED_OLD"
|
|
740
|
+
updated_new: "UPDATED_NEW"
|
|
741
|
+
}
|
|
742
|
+
if !params.ReturnValues
|
|
743
|
+
params.ReturnValues = "NONE"
|
|
346
744
|
}
|
|
347
|
-
params.Key[table.hashKey] = datumNaanToDyn(hashValue)
|
|
348
|
-
if table.rangeKey
|
|
349
|
-
params.Key[table.rangeKey] = datumNaanToDyn(rangeValue)
|
|
350
745
|
pending = new(nonce)
|
|
351
|
-
dyna.aws.
|
|
746
|
+
dyna.aws.putItem(params, function(error, data) {
|
|
352
747
|
if error {
|
|
353
748
|
dynaTableError = error
|
|
354
|
-
debuglog("aws.
|
|
355
|
-
|
|
356
|
-
|
|
749
|
+
debuglog("aws.putItem failed", tablename, error)
|
|
750
|
+
error = Error("table.updateRecord(".concat(tablename, ") failed"), error)
|
|
751
|
+
}
|
|
752
|
+
else
|
|
753
|
+
data = dyna.conv.recordDynToNaan(data.Attributes)
|
|
357
754
|
pending.signal(list(error, data))
|
|
358
755
|
})
|
|
359
756
|
pending.wait()
|
|
360
757
|
}
|
|
361
758
|
|
|
759
|
+
//
|
|
760
|
+
// dynQueryPager
|
|
761
|
+
//
|
|
762
|
+
// Perform a paged query. This implements paging conventions specific to DynamoDB. The callback
|
|
763
|
+
// should return the number of items found in the data, or false to stop looping.
|
|
764
|
+
//
|
|
765
|
+
closure dynQueryPager(procname, params, remaining, doneCB) {
|
|
766
|
+
if !remaining
|
|
767
|
+
remaining = 10 // default retrieve limit
|
|
768
|
+
params.Limit = remaining
|
|
769
|
+
QueryPager(dyna.aws, procname, params, function(data, local processed) {
|
|
770
|
+
processed = doneCB(data)
|
|
771
|
+
if processed
|
|
772
|
+
remaining -= processed
|
|
773
|
+
if processed && remaining > 0 && data.LastEvaluatedKey {
|
|
774
|
+
params.limit = remaining // set up for next iteration
|
|
775
|
+
params.ExclusiveStartKey = data.LastEvaluatedKey
|
|
776
|
+
true // continue looping
|
|
777
|
+
}
|
|
778
|
+
})
|
|
779
|
+
}
|
|
780
|
+
|
|
362
781
|
//
|
|
363
782
|
// queryRecords
|
|
364
783
|
//
|
|
365
784
|
// Options:
|
|
366
785
|
// {
|
|
367
786
|
// index: <string> // index name in table
|
|
368
|
-
// range: <expression
|
|
787
|
+
// range: <key-expression> // range of keys to retrieve
|
|
788
|
+
// filter: <key-expression> // conditions for items to return
|
|
789
|
+
// attmap: <dictionary> // map attribute names to #shortcuts
|
|
369
790
|
// reverse: <boolean> // reverse order
|
|
791
|
+
// project: <array> // project only specified attributes
|
|
792
|
+
// exstart: <exclusive start key> // for paging
|
|
370
793
|
// limit: <number> // maximum count of results
|
|
371
794
|
// }
|
|
372
795
|
//
|
|
373
|
-
// range dictionary:
|
|
374
|
-
// {
|
|
375
|
-
// "=": <expression> // exclusive of other comparisons
|
|
376
|
-
// ">": <expression> // exclusive of other comparisons
|
|
377
|
-
// "<": <expression> // exclusive of other comparisons
|
|
378
|
-
// beginsWith: <string-expression> // exclusive of other comparisons
|
|
379
|
-
// ">=": <expression> // with <= or alone
|
|
380
|
-
// "<=": <expression> // with >= or alone
|
|
381
|
-
// }
|
|
382
|
-
//
|
|
383
796
|
|
|
384
797
|
table.queryRecords = closure queryRecords(hashValue, options
|
|
385
|
-
local result, params, op, expr,
|
|
798
|
+
local error, result, params, op, expr, output, error)
|
|
386
799
|
{
|
|
387
|
-
if
|
|
388
|
-
|
|
389
|
-
if result.0
|
|
390
|
-
return(result)
|
|
391
|
-
}
|
|
800
|
+
if preflightError(this)
|
|
801
|
+
return
|
|
392
802
|
params = {
|
|
393
803
|
TableName: tablename
|
|
394
|
-
ExpressionAttributeValues: {
|
|
395
|
-
":v1": datumNaanToDyn(hashValue)
|
|
396
|
-
}
|
|
397
|
-
KeyConditionExpression: strcat(table.hashKey, " = :v1")
|
|
398
804
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if rangex.0 == "<="
|
|
404
|
-
rangex = list("between", expr, rangex.1)
|
|
405
|
-
else if rangex.0
|
|
406
|
-
return (dupeop())
|
|
407
|
-
else
|
|
408
|
-
rangex = list(op, expr) }
|
|
409
|
-
else if op == "<=" {
|
|
410
|
-
if rangex.0 == ">="
|
|
411
|
-
rangex = list("between", rangex.1, expr)
|
|
412
|
-
else if rangex.0
|
|
413
|
-
return (dupeop())
|
|
414
|
-
else
|
|
415
|
-
rangex = list(op, expr) }
|
|
416
|
-
else if rangex.0
|
|
417
|
-
return (dupeop())
|
|
418
|
-
else
|
|
419
|
-
rangex = list(op, expr)
|
|
420
|
-
}
|
|
421
|
-
if rangex {
|
|
422
|
-
params.ExpressionAttributeValues[":v2"] = datumNaanToDyn(rangex.1)
|
|
423
|
-
if rangex.0 == "between" {
|
|
424
|
-
params.ExpressionAttributeValues[":v3"] = datumNaanToDyn(rangex.2)
|
|
425
|
-
expr = strcat(" between :v2 and :v3")
|
|
426
|
-
}
|
|
427
|
-
else
|
|
428
|
-
expr = strcat(space, rangex.0, " :v2")
|
|
429
|
-
params.KeyConditionExpression = params.KeyConditionExpression.concat(" and ", table.rangeKey, expr)
|
|
805
|
+
dyna.conv.createKeyExpression(params, {
|
|
806
|
+
KeyConditionExpression: {
|
|
807
|
+
key: table.hashKey
|
|
808
|
+
"=": hashValue
|
|
430
809
|
}
|
|
810
|
+
})
|
|
811
|
+
if options.index
|
|
812
|
+
params.IndexName = options.index
|
|
813
|
+
if options.range {
|
|
814
|
+
`(error) = dyna.conv.createKeyExpression(params, {
|
|
815
|
+
KeyConditionExpression: options.range
|
|
816
|
+
})
|
|
817
|
+
if error
|
|
818
|
+
return (list(Error("table.queryRecords range:", error)))
|
|
431
819
|
}
|
|
820
|
+
if options.filter {
|
|
821
|
+
`(error) = dyna.conv.createKeyExpression(params, {
|
|
822
|
+
FilterExpression: options.filter
|
|
823
|
+
})
|
|
824
|
+
if error
|
|
825
|
+
return (list(Error("table.queryRecords filter:", error)))
|
|
826
|
+
}
|
|
827
|
+
if options.attmap
|
|
828
|
+
params.ExpressionAttributeNames = attmap
|
|
829
|
+
if options.reverse
|
|
830
|
+
params.ScanIndexForward = false
|
|
831
|
+
if options.project
|
|
832
|
+
params.ProjectionExpression = options.project.join(", ")
|
|
833
|
+
if options.exstart
|
|
834
|
+
params.ExclusiveStartKey = options.exstart
|
|
432
835
|
output = []
|
|
433
|
-
`(error) =
|
|
836
|
+
`(error) = dynQueryPager(`query, params, options.limit, function(data) {
|
|
837
|
+
if data.Count > 0
|
|
838
|
+
output = output.concat(dyna.conv.recordDynToNaan(data.Items))
|
|
839
|
+
data.Count
|
|
840
|
+
})
|
|
841
|
+
if error {
|
|
842
|
+
error = Error("table.queryRecords(".concat(tablename, ") failed"), error)
|
|
843
|
+
output = false
|
|
844
|
+
}
|
|
845
|
+
list(error, output)
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
//
|
|
849
|
+
// scanRecords
|
|
850
|
+
//
|
|
851
|
+
// Options:
|
|
852
|
+
// {
|
|
853
|
+
// index: <string> // index name in table
|
|
854
|
+
// filter: <key-expression> // conditions for items to return
|
|
855
|
+
// attmap: <dictionary> // map attribute names to #shortcuts
|
|
856
|
+
// project: <array> // project only specified attributes
|
|
857
|
+
// exstart: <exclusive start key> // for paging
|
|
858
|
+
// limit: <number> // maximum count of results
|
|
859
|
+
// }
|
|
860
|
+
//
|
|
861
|
+
|
|
862
|
+
table.scanRecords = closure scanRecords(options
|
|
863
|
+
local result, params, op, expr, output, error)
|
|
864
|
+
{
|
|
865
|
+
if preflightError(this)
|
|
866
|
+
return
|
|
867
|
+
params = {
|
|
868
|
+
TableName: tablename
|
|
869
|
+
}
|
|
870
|
+
if options.index
|
|
871
|
+
params.IndexName = options.index
|
|
872
|
+
if options.filter
|
|
873
|
+
dyna.conv.createKeyExpression(params, {
|
|
874
|
+
FilterExpression: options.filter
|
|
875
|
+
})
|
|
876
|
+
if options.attmap
|
|
877
|
+
params.ExpressionAttributeNames = attmap
|
|
878
|
+
if options.project
|
|
879
|
+
params.ProjectionExpression = options.project.join(", ")
|
|
880
|
+
if options.exstart
|
|
881
|
+
params.ExclusiveStartKey = options.exstart
|
|
882
|
+
output = []
|
|
883
|
+
`(error) = dynQueryPager(`scan, params, options.limit, function(data) {
|
|
434
884
|
if data.Count >= 0
|
|
435
|
-
output = output.concat(recordDynToNaan(data.Items))
|
|
885
|
+
output = output.concat(dyna.conv.recordDynToNaan(data.Items))
|
|
436
886
|
data.Count
|
|
437
887
|
})
|
|
438
|
-
if error
|
|
888
|
+
if error {
|
|
889
|
+
error = Error("table.scanRecords(".concat(tablename, ") failed"), error)
|
|
439
890
|
output = false
|
|
891
|
+
}
|
|
440
892
|
list(error, output)
|
|
441
893
|
}
|
|
442
894
|
|
|
@@ -455,35 +907,67 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
455
907
|
|
|
456
908
|
closure DynamoDB(local dyna) {
|
|
457
909
|
dyna = new(object, this)
|
|
910
|
+
dyna.timeout = 20000 // default 20-second timeout
|
|
911
|
+
dyna.conv = DynaConverter()
|
|
912
|
+
dyna.multi = DynaMultiTable(dyna)
|
|
458
913
|
|
|
459
|
-
//
|
|
460
914
|
// login
|
|
461
915
|
//
|
|
462
|
-
dyna.login =
|
|
916
|
+
dyna.login = function login(creds) {
|
|
463
917
|
dyna.aws = xnew(awsSDK.DynamoDB, {
|
|
464
918
|
apiVersion: "2012-08-10",
|
|
465
919
|
accessKeyId: creds.keyID,
|
|
466
920
|
secretAccessKey: creds.keySecret,
|
|
467
921
|
region: creds.region })
|
|
922
|
+
list(false, { ok: true })
|
|
468
923
|
}
|
|
469
924
|
|
|
470
|
-
//
|
|
471
925
|
// table
|
|
472
926
|
//
|
|
473
927
|
// Return a new table access object for the table of the specified name. The table may not
|
|
474
928
|
// yet exist; see DynaTable for methods.
|
|
475
929
|
//
|
|
476
|
-
|
|
477
|
-
// {
|
|
478
|
-
// hash: <name> // primary hash key attribute name (required)
|
|
479
|
-
// hashType: <type> // string | numeric (required)
|
|
480
|
-
// range: <name> // primary range key attribute name (optional)
|
|
481
|
-
// rangeType: <name> // string | numeric (required if range specified)
|
|
482
|
-
// }
|
|
483
|
-
//
|
|
484
|
-
dyna.table = closure table(name) {
|
|
930
|
+
dyna.table = function table(name) {
|
|
485
931
|
DynaTable(dyna, name)
|
|
486
932
|
}
|
|
933
|
+
|
|
934
|
+
// timekeys
|
|
935
|
+
//
|
|
936
|
+
// Return a TimeKeys object with a default no prefix, and baseMS of 1/1/2022.
|
|
937
|
+
//
|
|
938
|
+
dyna.timekeys = function timekeys(prefix, baseMS) {
|
|
939
|
+
TimeKeys(prefix, baseMS)
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// batchGetRecords
|
|
943
|
+
//
|
|
944
|
+
dyna.batchGetRecords = function loadBatchGetRecords(request, options) {
|
|
945
|
+
require("./aws_dynextra.nlg")
|
|
946
|
+
dyna.batchGetRecords = function bgr(request, options) {
|
|
947
|
+
dynexBatchGetRecords(dyna, request, options)
|
|
948
|
+
}
|
|
949
|
+
dynexBatchGetRecords(dyna, request, options)
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// dynexBatchWriteRecords
|
|
953
|
+
//
|
|
954
|
+
dyna.batchWriteRecords = function batchWriteRecords(request, options) {
|
|
955
|
+
require("./aws_dynextra.nlg")
|
|
956
|
+
dyna.batchWriteRecords = function bwr(request, options) {
|
|
957
|
+
dynexBatchWriteRecords(dyna, request, options)
|
|
958
|
+
}
|
|
959
|
+
dynexBatchWriteRecords(dyna, request, options)
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// dynexTransactGetRecords
|
|
963
|
+
//
|
|
964
|
+
dyna.transactGetRecords = function loadTransactGetRecords(request, options) {
|
|
965
|
+
require("./aws_dynextra.nlg")
|
|
966
|
+
dyna.transactGetRecords = function tgr(request, options) {
|
|
967
|
+
dynexTransactGetRecords(dyna, request, options)
|
|
968
|
+
}
|
|
969
|
+
dynexTransactGetRecords(dyna, request, options)
|
|
970
|
+
}
|
|
487
971
|
|
|
488
972
|
// finis
|
|
489
973
|
dyna
|
|
@@ -491,19 +975,19 @@ closure DynamoDB(local dyna) {
|
|
|
491
975
|
|
|
492
976
|
|
|
493
977
|
/*
|
|
494
|
-
*
|
|
978
|
+
* dynaInit
|
|
495
979
|
*
|
|
496
|
-
* Initialize the DynamoDB
|
|
980
|
+
* Initialize the DynamoDB component.
|
|
497
981
|
*
|
|
498
982
|
*/
|
|
499
983
|
|
|
500
|
-
function
|
|
501
|
-
manifest = `(DynaTable, DynamoDB,
|
|
984
|
+
function dynaInit(local manifest) {
|
|
985
|
+
manifest = `(TimeKeys, DynaConverter, DynaTable, DynamoDB, dynaInit)
|
|
502
986
|
|
|
503
987
|
Naan.module.build(module.id, "aws_dynamo", function(modobj, compobj) {
|
|
504
|
-
require("./serviceAws.nlg")
|
|
505
988
|
compobj.manifest = manifest
|
|
989
|
+
modobj.exports.TimeKeys = TimeKeys
|
|
506
990
|
modobj.exports.DynamoDB = DynamoDB
|
|
991
|
+
require("./serviceAws.nlg")
|
|
507
992
|
})
|
|
508
|
-
|
|
509
993
|
} ();
|