@naanlang/naan 1.0.2 → 1.0.5
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 +10 -8
- package/bin/index.js +2 -2
- package/dist/env_web.js +172 -22
- package/dist/naan.min.js +6 -6
- package/frameworks/browser/https_request.nlg +120 -0
- package/frameworks/browser/sworker.js +299 -91
- package/frameworks/browser/terminals.nlg +28 -13
- package/frameworks/browser/workers.nlg +27 -12
- package/frameworks/client/apiclient.nlg +17 -4
- package/frameworks/client/psm_client.nlg +184 -81
- package/frameworks/common/common.nlg +105 -25
- package/frameworks/common/preferences.nlg +1 -0
- package/frameworks/common/watching.nlg +1 -0
- package/frameworks/node/apiserver.nlg +31 -15
- package/frameworks/node/filesystem.nlg +230 -46
- package/frameworks/node/gitter.nlg +27 -6
- package/frameworks/node/https_request.nlg +119 -0
- package/frameworks/node/node.nlg +1 -0
- package/frameworks/node/psm_server.nlg +113 -37
- package/frameworks/project/build.nlg +40 -23
- package/frameworks/project/proj_cliser.nlg +11 -3
- package/frameworks/project/proj_console.nlg +13 -4
- package/frameworks/project/proj_folder.nlg +10 -1
- package/frameworks/project/proj_lambda.nlg +13 -4
- package/frameworks/project/proj_static.nlg +11 -3
- package/frameworks/project/projects.nlg +128 -40
- package/frameworks/running/debugnub.nlg +2 -5
- package/frameworks/running/executors.nlg +1 -0
- 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 +170 -20
- package/lib/browser/env_webworker.js +1 -1
- package/lib/browser/require.js +1 -1
- package/lib/core/naanlib.js +6 -6
- package/package.json +2 -1
- package/plugins/serviceAws/aws/aws-sdk-node.min.js +2 -0
- package/plugins/serviceAws/aws/aws-sdk.min.js +2 -88
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +8 -7
- package/plugins/serviceAws/aws_dynamo.nlg +715 -148
- package/plugins/serviceAws/aws_dynextra.nlg +294 -0
- package/plugins/serviceAws/aws_lambda.nlg +11 -4
- package/plugins/serviceAws/aws_s3.nlg +44 -14
- package/plugins/serviceAws/dbt_aws.nlg +58 -45
- package/plugins/serviceAws/psm_aws.nlg +61 -40
- package/plugins/serviceAws/serviceAws.nlg +7 -4
- package/plugins/serviceGitHub/psm_github.nlg +41 -25
- package/plugins/serviceGitLab/psm_gitlab.nlg +41 -25
- package/test/harness.nlg +3 -1
- package/test/test_01_core.nlg +5 -5
- package/test/test_02_context.nlg +4 -4
- package/test/test_03_jsinterop.nlg +9 -3
- package/plugins/serviceAws/aws_cognito.nlg +0 -76
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* aws_dynextra.nlg
|
|
3
|
+
* serviceAws
|
|
4
|
+
*
|
|
5
|
+
* Extra DynamoDB services for AWS.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2022 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* dynexBatchGetRecords
|
|
16
|
+
*
|
|
17
|
+
* Retrieve records from one or more tables using arrays of keys. The results are put back in a
|
|
18
|
+
* dictionary with the same shape as the source. The result is a standard `(error, data) tuple.
|
|
19
|
+
* The request/response layout is as follows:
|
|
20
|
+
* [
|
|
21
|
+
* {
|
|
22
|
+
* table: <DynaTable object> // table to act upon
|
|
23
|
+
* keys: [<hash-range tuple>] // records to get
|
|
24
|
+
* data: [<retrieved records>] // received records in reply
|
|
25
|
+
* },
|
|
26
|
+
* { <next-table> }
|
|
27
|
+
* ]
|
|
28
|
+
* The operations are processed in parallel batches, and the final result is a standard result
|
|
29
|
+
* tuple `(error, result). An error is only reported if we are unable to complete all operations.
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
function dynexBatchGetRecords(dyna, request,
|
|
34
|
+
local index, titem, tname, error, batches, activity, params, batchsize, primary) {
|
|
35
|
+
if !dyna.aws
|
|
36
|
+
return (list(Error("DynaTable.batchGetRecords: not logged into AWS")))
|
|
37
|
+
request = new(request) // allow local modifications
|
|
38
|
+
index = { }
|
|
39
|
+
for titem in request { // preflight all tables
|
|
40
|
+
tname = titem.table.tablename
|
|
41
|
+
if index[tname]
|
|
42
|
+
return (Error("DynaTable.batchGetRecords: duplicate table in request:", tname))
|
|
43
|
+
`(error) = titem.table.info()
|
|
44
|
+
if error
|
|
45
|
+
return (Error("DynaTable.batchGetRecords: table", tname, ":", error))
|
|
46
|
+
titem.data = []
|
|
47
|
+
index[tname] = titem
|
|
48
|
+
}
|
|
49
|
+
batches = []
|
|
50
|
+
activity = true
|
|
51
|
+
while activity { // loop until requests empty
|
|
52
|
+
params = {
|
|
53
|
+
RequestItems: { }
|
|
54
|
+
}
|
|
55
|
+
batchsize = 0
|
|
56
|
+
while :fillbatch activity { // loop until batch full
|
|
57
|
+
activity = false
|
|
58
|
+
for `(tname, titem) in index {
|
|
59
|
+
if titem.keys.length > 0 {
|
|
60
|
+
primary = titem.table.makeDynKey(titem.keys.unshift())
|
|
61
|
+
if params.RequestItems[tname].Keys
|
|
62
|
+
params.RequestItems[tname].Keys.push(primary)
|
|
63
|
+
else
|
|
64
|
+
params.RequestItems[tname] = {
|
|
65
|
+
Keys: [primary]
|
|
66
|
+
}
|
|
67
|
+
activity = true
|
|
68
|
+
if ++batchsize >= 100
|
|
69
|
+
break :fillbatch // our batch is full
|
|
70
|
+
} else
|
|
71
|
+
item.keys = undefined // clear for return result
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if batchsize > 0
|
|
75
|
+
batches.push(params)
|
|
76
|
+
}
|
|
77
|
+
asyncArray(batches, 10, closure bgr(params, local delayer, pending, err, response, table) {
|
|
78
|
+
delayer = dyna.conv.genBackoffDelayer()
|
|
79
|
+
loop {
|
|
80
|
+
pending = new(nonce)
|
|
81
|
+
dyna.aws.batchGetItem(params, function(err, data) {
|
|
82
|
+
if err
|
|
83
|
+
debuglog("aws.batchGetItem failed", err)
|
|
84
|
+
pending.signal(err, data)
|
|
85
|
+
})
|
|
86
|
+
`(err, response) = pending.wait()
|
|
87
|
+
for `(tname, table) in response.Responses
|
|
88
|
+
for titem in table
|
|
89
|
+
index[tname].data.push(dyna.conv.recordDynToNaan(titem))
|
|
90
|
+
if response.UnprocessedKeys
|
|
91
|
+
params.RequestItems = response.UnprocessedKeys // still more to do
|
|
92
|
+
else
|
|
93
|
+
break // total success or failure
|
|
94
|
+
if delayer() > dyna.timeout {
|
|
95
|
+
if !error // too slow
|
|
96
|
+
error = err
|
|
97
|
+
break
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}).wait()
|
|
101
|
+
if error
|
|
102
|
+
list(error)
|
|
103
|
+
else
|
|
104
|
+
list(false, request)
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
* dynexBatchWriteRecords
|
|
110
|
+
*
|
|
111
|
+
* Put arrays of records into one or more tables, replacing or deleting any existing records with
|
|
112
|
+
* the same keys. This returns arrays of results coresponding to the input records, where each
|
|
113
|
+
* is a standard `(error, data) tuple. The request layout is as follows:
|
|
114
|
+
* [
|
|
115
|
+
* {
|
|
116
|
+
* table: <DynaTable object> // table to act upon
|
|
117
|
+
* delete: [<key tuples/records>] // records to delete (needing only keys)
|
|
118
|
+
* put: [<records>] // records to put
|
|
119
|
+
* },
|
|
120
|
+
* { <next-table> }
|
|
121
|
+
* ]
|
|
122
|
+
* The operations are processed in parallel batches, and the final result is a standard result
|
|
123
|
+
* tuple `(error, result). An error is only reported if we are unable to complete all operations.
|
|
124
|
+
*
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
closure dynexBatchWriteRecords(dyna, request, local batches, error, activity) {
|
|
128
|
+
if !dyna.aws
|
|
129
|
+
return (list(Error("DynaTable.batchWriteRecords: not logged into AWS")))
|
|
130
|
+
|
|
131
|
+
//
|
|
132
|
+
// addOp
|
|
133
|
+
// Add a put or delete operation to the parameters
|
|
134
|
+
//
|
|
135
|
+
function addOp(params, table, del, record, local request) {
|
|
136
|
+
if del { // deleting a record
|
|
137
|
+
if tuple(record)
|
|
138
|
+
record = table.makeDynKey(record) // caller provided hash/range key pair
|
|
139
|
+
else
|
|
140
|
+
request = { DeleteRequest: { Key: dyna.conv.recordNaanToDyn(record) } }
|
|
141
|
+
}
|
|
142
|
+
else // putting a record
|
|
143
|
+
request = { PutRequest: { Item: dyna.conv.recordNaanToDyn(record) } }
|
|
144
|
+
if params.RequestItems[table.tablename]
|
|
145
|
+
params.RequestItems[table.tablename].push(request)
|
|
146
|
+
else
|
|
147
|
+
params.RequestItems[table.tablename] = [request]
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// transform into batches
|
|
151
|
+
//
|
|
152
|
+
// This is tricky because it wants each batch to use multiple tables for maximum parallelism,
|
|
153
|
+
// just in case AWS isn't already optimizing this. It takes a request from each table and
|
|
154
|
+
// combines them into a single batch repeatedly, until the batch is full or the tables in the
|
|
155
|
+
// request have no more entries.
|
|
156
|
+
//
|
|
157
|
+
request = new(request) // allow local modifications
|
|
158
|
+
batches = []
|
|
159
|
+
activity = true
|
|
160
|
+
while activity
|
|
161
|
+
let (params, batchsize, table) {
|
|
162
|
+
params = {
|
|
163
|
+
RequestItems: { }
|
|
164
|
+
}
|
|
165
|
+
batchsize = 0
|
|
166
|
+
while :fillbatch activity { // loop until no activity or batch full
|
|
167
|
+
activity = false
|
|
168
|
+
for table in request {
|
|
169
|
+
if table.delete.length > 0
|
|
170
|
+
addOp(params, table, true, table.delete.unshift())
|
|
171
|
+
else if table.put.length > 0
|
|
172
|
+
addOp(params, table, false, table.put.unshift())
|
|
173
|
+
else
|
|
174
|
+
continue
|
|
175
|
+
activity = true
|
|
176
|
+
if ++batchsize >= 25
|
|
177
|
+
break :fillbatch // our batch is full
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if batchsize > 0
|
|
181
|
+
batches.push(params)
|
|
182
|
+
} ()
|
|
183
|
+
asyncArray(batches, 10, closure bwr(params, local delayer, pending, err, response) {
|
|
184
|
+
delayer = dyna.conv.genBackoffDelayer()
|
|
185
|
+
loop {
|
|
186
|
+
pending = new(nonce)
|
|
187
|
+
dyna.aws.batchWriteItem(params, function(err, data) {
|
|
188
|
+
if err
|
|
189
|
+
debuglog("aws.batchWriteItem failed", err)
|
|
190
|
+
pending.signal(err, data)
|
|
191
|
+
})
|
|
192
|
+
`(err, response) = pending.wait()
|
|
193
|
+
if response.UnprocessedKeys
|
|
194
|
+
params.RequestItems = response.UnprocessedKeys // still more to do
|
|
195
|
+
else
|
|
196
|
+
break // total success or failure
|
|
197
|
+
if delayer() > dyna.timeout {
|
|
198
|
+
if !error // too slow
|
|
199
|
+
error = err
|
|
200
|
+
break
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}).wait()
|
|
204
|
+
if error
|
|
205
|
+
list(error)
|
|
206
|
+
else
|
|
207
|
+
list(false, { ok: true })
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
/*
|
|
213
|
+
* dynexTransactGetRecords
|
|
214
|
+
*
|
|
215
|
+
* Retrieve records from one or more tables using arrays of keys as a single transaction. The
|
|
216
|
+
* results are put back in an array with the same shape as the source. The request/response
|
|
217
|
+
* layout is as follows:
|
|
218
|
+
* [
|
|
219
|
+
* {
|
|
220
|
+
* table: <DynaTable object> // table to act upon
|
|
221
|
+
* keys: [<hash-range tuple>] // records to get
|
|
222
|
+
* data: [<retrieved records>] // received records in reply
|
|
223
|
+
* },
|
|
224
|
+
* { <next-table> }
|
|
225
|
+
* ]
|
|
226
|
+
* This returns a standard result tuple `(error, result). An error is only reported if we are
|
|
227
|
+
* unable to complete all operations.
|
|
228
|
+
*
|
|
229
|
+
*/
|
|
230
|
+
|
|
231
|
+
closure dynexTransactGetRecords(dyna, request,
|
|
232
|
+
local index, titem, tname, error, params, pending, response) {
|
|
233
|
+
if !dyna.aws
|
|
234
|
+
return (list(Error("DynaTable.transactGetRecords: not logged into AWS")))
|
|
235
|
+
request = new(request) // allow local modifications
|
|
236
|
+
index = { }
|
|
237
|
+
for titem in request { // preflight all tables
|
|
238
|
+
tname = titem.table.tablename
|
|
239
|
+
if index[tname]
|
|
240
|
+
return (Error("DynaTable.batchGetRecords: duplicate table in request:", tname))
|
|
241
|
+
`(error) = titem.table.info()
|
|
242
|
+
if error
|
|
243
|
+
return (Error("DynaTable.batchGetRecords: table", tname, ":", error))
|
|
244
|
+
titem.data = []
|
|
245
|
+
index[tname] = titem
|
|
246
|
+
}
|
|
247
|
+
params = {
|
|
248
|
+
TransactItems: []
|
|
249
|
+
}
|
|
250
|
+
for `(tname, titem) in index {
|
|
251
|
+
while titem.keys.length > 0 {
|
|
252
|
+
params.TransactItems.push({
|
|
253
|
+
Get: {
|
|
254
|
+
TableName: tname
|
|
255
|
+
Key: makeDynKey(titem.keys.unshift())
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
}
|
|
259
|
+
titem.keys = undefined
|
|
260
|
+
}
|
|
261
|
+
pending = new(nonce)
|
|
262
|
+
dyna.aws.transactGetItems(params, function(err, data) {
|
|
263
|
+
if err
|
|
264
|
+
debuglog("aws.transactGetItems failed", err)
|
|
265
|
+
pending.signal(err, data)
|
|
266
|
+
})
|
|
267
|
+
`(error, response) = pending.wait()
|
|
268
|
+
if error
|
|
269
|
+
list(error)
|
|
270
|
+
else {
|
|
271
|
+
for titem in response.Responses {
|
|
272
|
+
tname = titem.Item.*.0
|
|
273
|
+
index[tname].data.push(dyna.conv.recordDynToNaan(titem.Item[tname]))
|
|
274
|
+
}
|
|
275
|
+
list(false, request)
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
/*
|
|
281
|
+
* dynexInit
|
|
282
|
+
*
|
|
283
|
+
* Initialize the component.
|
|
284
|
+
*
|
|
285
|
+
*/
|
|
286
|
+
|
|
287
|
+
function dynexInit(local manifest) {
|
|
288
|
+
manifest = `(dynexBatchGetRecords, dynexBatchWriteRecords, dynexTransactGetRecords, dynexInit)
|
|
289
|
+
|
|
290
|
+
Naan.module.build(module.id, "aws_dynextra", function(modobj, compobj) {
|
|
291
|
+
compobj.manifest = manifest
|
|
292
|
+
require("./serviceAws.nlg")
|
|
293
|
+
})
|
|
294
|
+
} ();
|
|
@@ -26,10 +26,13 @@ closure Lambda(local lamb) {
|
|
|
26
26
|
//
|
|
27
27
|
lamb.login = closure login(creds) {
|
|
28
28
|
lamb.aws = xnew(awsSDK.Lambda, {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
region: creds.region
|
|
30
|
+
credentials: {
|
|
31
|
+
accessKeyId: creds.keyID
|
|
32
|
+
secretAccessKey: creds.keySecret
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
list(false, { ok: true })
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
//
|
|
@@ -55,8 +58,12 @@ closure Lambda(local lamb) {
|
|
|
55
58
|
//
|
|
56
59
|
// updateFunctionCode
|
|
57
60
|
//
|
|
61
|
+
// Note that as of 2022-05-17 AWS SDK v3 requires that the zipfile be a Uint8Array, not an
|
|
62
|
+
// ArrayBuffer or anything simple like that. What a #$*#(! waste of time.
|
|
63
|
+
//
|
|
58
64
|
lamb.updateFunctionCode = closure updateFunctionCode(name, zipfile, local params, pending) {
|
|
59
65
|
params = {
|
|
66
|
+
Architectures: [ "x86_64" ]
|
|
60
67
|
FunctionName: name
|
|
61
68
|
ZipFile: zipfile
|
|
62
69
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2022 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -81,15 +81,17 @@ closure S3(local s3) {
|
|
|
81
81
|
s3.login = closure login(creds, local params) {
|
|
82
82
|
if !awsSDK
|
|
83
83
|
return (list(Error("S3.login: no AWS SDK available")))
|
|
84
|
-
params = {
|
|
85
|
-
apiVersion: "2012-08-10"
|
|
86
|
-
}
|
|
84
|
+
params = { }
|
|
87
85
|
if creds {
|
|
88
|
-
params.accessKeyId = creds.keyID,
|
|
89
|
-
params.secretAccessKey = creds.keySecret,
|
|
90
86
|
params.region = creds.region
|
|
87
|
+
params.credentials = {
|
|
88
|
+
accessKeyId: creds.keyID
|
|
89
|
+
secretAccessKey: creds.keySecret
|
|
90
|
+
}
|
|
91
91
|
}
|
|
92
92
|
s3.aws = xnew(awsSDK.S3, params)
|
|
93
|
+
if awsSDK.S3Client
|
|
94
|
+
s3.client = xnew(awsSDK.S3Client, params)
|
|
93
95
|
`(false, { ok: true })
|
|
94
96
|
}
|
|
95
97
|
|
|
@@ -99,16 +101,44 @@ closure S3(local s3) {
|
|
|
99
101
|
// Return a new bucket access object for the table of the specified name. The bucket may not
|
|
100
102
|
// yet exist; see S3bucket for methods.
|
|
101
103
|
//
|
|
102
|
-
|
|
104
|
+
s3.bucket = closure bucket(name) {
|
|
105
|
+
S3bucket(s3, name)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//
|
|
109
|
+
// getSignedURL
|
|
110
|
+
//
|
|
111
|
+
// This obtains a pre-signed URL for the specified command and its parameters. Options control
|
|
112
|
+
// how the URL is generated.
|
|
113
|
+
//
|
|
114
|
+
// Options:
|
|
103
115
|
// {
|
|
104
|
-
//
|
|
105
|
-
// hashType: <type> // string | numeric (required)
|
|
106
|
-
// range: <name> // primary range key attribute name (optional)
|
|
107
|
-
// rangeType: <name> // string | numeric (required if range specified)
|
|
116
|
+
// expiresIn: <seconds> // default expiration is 900 (15 minutes)
|
|
108
117
|
// }
|
|
109
118
|
//
|
|
110
|
-
s3.
|
|
111
|
-
|
|
119
|
+
s3.getSignedURL = closure getSignedURL(cmdname, cmdparams, local command, options, pending) {
|
|
120
|
+
if s3.client {
|
|
121
|
+
if cmdname == "putObject"
|
|
122
|
+
cmdname = "PutObjectCommand"
|
|
123
|
+
else if cmdname == "getObject"
|
|
124
|
+
cmdname = "GetObjectCommand"
|
|
125
|
+
else
|
|
126
|
+
return (list(Error("S3bucket.getSignedURL: command not present:", cmdname)))
|
|
127
|
+
if cmdparams.Expires {
|
|
128
|
+
cmdparams = new(cmdparams)
|
|
129
|
+
options.expiresIn = cmdparams.Expires
|
|
130
|
+
cmdparams.Expires = undefined
|
|
131
|
+
} else
|
|
132
|
+
options = { }
|
|
133
|
+
command = xnew(awsSDK.s3Commands[cmdname], cmdparams)
|
|
134
|
+
await(awsSDK.getSignedUrl(s3.client, command, options))
|
|
135
|
+
} else {
|
|
136
|
+
pending = new(nonce)
|
|
137
|
+
s3.aws.getSignedUrl(cmdname, cmdparams, function(error, data) {
|
|
138
|
+
pending.signal(list(error, data))
|
|
139
|
+
})
|
|
140
|
+
pending.wait()
|
|
141
|
+
}
|
|
112
142
|
}
|
|
113
143
|
|
|
114
144
|
// finis
|
|
@@ -126,7 +156,7 @@ closure S3(local s3) {
|
|
|
126
156
|
function s3Init(local manifest) {
|
|
127
157
|
manifest = `(S3bucket, S3, s3Init)
|
|
128
158
|
|
|
129
|
-
Naan.module.build(module.id, "
|
|
159
|
+
Naan.module.build(module.id, "aws_s3", function(modobj, compobj) {
|
|
130
160
|
require("./serviceAws.nlg")
|
|
131
161
|
compobj.manifest = manifest
|
|
132
162
|
modobj.exports.S3 = S3
|
|
@@ -6,23 +6,11 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2022 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
/*
|
|
15
|
-
* dawsValidateName
|
|
16
|
-
*
|
|
17
|
-
* True iff the specified name is valid here.
|
|
18
|
-
*
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
function dawsValidateName(key, path) {
|
|
22
|
-
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
|
|
26
14
|
/*
|
|
27
15
|
* dawsBucket
|
|
28
16
|
*
|
|
@@ -33,12 +21,12 @@ function dawsValidateName(key, path) {
|
|
|
33
21
|
*
|
|
34
22
|
*/
|
|
35
23
|
|
|
36
|
-
closure dawsBucket(
|
|
24
|
+
closure dawsBucket(s3b, local bucket, s3) {
|
|
37
25
|
bucket = new(object, this)
|
|
38
|
-
|
|
39
|
-
bucket.name =
|
|
40
|
-
bucket.region =
|
|
41
|
-
if
|
|
26
|
+
s3 = s3b.s3
|
|
27
|
+
bucket.name = s3b.bucketName
|
|
28
|
+
bucket.region = s3b.region
|
|
29
|
+
if !s3 || !string(bucket.region) || bucket.region.length == 0 || !string(bucket.name) || bucket.bucket.length == 0
|
|
42
30
|
return (list(Error("invalid S3 parameter")))
|
|
43
31
|
|
|
44
32
|
// asyncResult
|
|
@@ -56,7 +44,7 @@ closure dawsBucket(s3, local bucket) {
|
|
|
56
44
|
// that the bucket is still open before beginning (or continuing) any operations.
|
|
57
45
|
|
|
58
46
|
bucket.close = function close() {
|
|
59
|
-
bucket.s3 = false
|
|
47
|
+
bucket.s3 = s3 = false // just remove access
|
|
60
48
|
list(false, { })
|
|
61
49
|
}
|
|
62
50
|
|
|
@@ -70,6 +58,7 @@ closure dawsBucket(s3, local bucket) {
|
|
|
70
58
|
if !s3
|
|
71
59
|
return (asyncResult(callback, Error("database closed")))
|
|
72
60
|
params = {
|
|
61
|
+
Bucket: bucket.name
|
|
73
62
|
Key: key
|
|
74
63
|
}
|
|
75
64
|
|
|
@@ -100,6 +89,7 @@ closure dawsBucket(s3, local bucket) {
|
|
|
100
89
|
if !s3
|
|
101
90
|
return (asyncResult(callback, Error("database closed")))
|
|
102
91
|
params = {
|
|
92
|
+
Bucket: bucket.name
|
|
103
93
|
Key: key
|
|
104
94
|
}
|
|
105
95
|
if data {
|
|
@@ -110,6 +100,7 @@ closure dawsBucket(s3, local bucket) {
|
|
|
110
100
|
mimeType = {
|
|
111
101
|
"css": "text/css",
|
|
112
102
|
"csv": "text/csv",
|
|
103
|
+
"gif": "image/gif",
|
|
113
104
|
"htm": "text/html",
|
|
114
105
|
"html": "text/html",
|
|
115
106
|
"ico": "image/x-icon",
|
|
@@ -170,6 +161,7 @@ closure dawsBucket(s3, local bucket) {
|
|
|
170
161
|
if !s3
|
|
171
162
|
return (asyncResult(callback, Error("database closed")))
|
|
172
163
|
params = {
|
|
164
|
+
Bucket: bucket.name
|
|
173
165
|
Key: key
|
|
174
166
|
}
|
|
175
167
|
s3.deleteObject(params, function(error, resp) {
|
|
@@ -251,7 +243,9 @@ closure dawsBucket(s3, local bucket) {
|
|
|
251
243
|
|
|
252
244
|
if !s3
|
|
253
245
|
return (asyncResult(callback, Error("database closed")))
|
|
254
|
-
params = {
|
|
246
|
+
params = {
|
|
247
|
+
Bucket: bucket.name
|
|
248
|
+
}
|
|
255
249
|
if string(prefix)
|
|
256
250
|
params.Prefix = prefix
|
|
257
251
|
if onelevel
|
|
@@ -259,6 +253,8 @@ closure dawsBucket(s3, local bucket) {
|
|
|
259
253
|
listOne()
|
|
260
254
|
}
|
|
261
255
|
|
|
256
|
+
// finis
|
|
257
|
+
|
|
262
258
|
list(false, bucket)
|
|
263
259
|
|
|
264
260
|
};
|
|
@@ -400,7 +396,7 @@ closure dawsTable(database, path, local table) {
|
|
|
400
396
|
metadata = metadata2s3(metadata) // filter out any invalid metadata
|
|
401
397
|
database.bucket.put(id, content, metadata, function(error, resp) {
|
|
402
398
|
if error
|
|
403
|
-
error = Error("add failed", error, id, { status: error.
|
|
399
|
+
error = Error("add failed", error, id, { status: error.$metadata.httpStatusCode })
|
|
404
400
|
else {
|
|
405
401
|
doc.md5 = resp.md5 // update the original doc
|
|
406
402
|
doc.length = resp.length }
|
|
@@ -420,7 +416,7 @@ closure dawsTable(database, path, local table) {
|
|
|
420
416
|
metadata = metadata2s3(doc)
|
|
421
417
|
database.bucket.put(doc._id, doc.content, metadata, function(error, resp) {
|
|
422
418
|
if error
|
|
423
|
-
error = Error("update failed", error, doc._id, { status: error.
|
|
419
|
+
error = Error("update failed", error, doc._id, { status: error.$metadata.httpStatusCode })
|
|
424
420
|
else {
|
|
425
421
|
doc.md5 = resp.md5 // update the original doc
|
|
426
422
|
doc.modified = undefined // we don't know exact modify date
|
|
@@ -441,7 +437,7 @@ closure dawsTable(database, path, local table) {
|
|
|
441
437
|
return (asyncResult(callback, Error("invalid argument")))
|
|
442
438
|
database.bucket.delete(doc._id, function(error, resp) {
|
|
443
439
|
if error
|
|
444
|
-
error = Error("delete failed", error, doc._id, { status: error.
|
|
440
|
+
error = Error("delete failed", error, doc._id, { status: error.$metadata.httpStatusCode })
|
|
445
441
|
callback(error, resp)
|
|
446
442
|
})
|
|
447
443
|
}
|
|
@@ -458,7 +454,7 @@ closure dawsTable(database, path, local table) {
|
|
|
458
454
|
return (asyncResult(callback, id)) // error from makeID
|
|
459
455
|
database.bucket.get(id, false, function(error, resp) {
|
|
460
456
|
if error
|
|
461
|
-
error = Error("get failed", error, id, { status: error.
|
|
457
|
+
error = Error("get failed", error, id, { status: error.$metadata.httpStatusCode })
|
|
462
458
|
else {
|
|
463
459
|
resp.key = key
|
|
464
460
|
resp = s3resp2doc(resp)
|
|
@@ -491,7 +487,7 @@ closure dawsTable(database, path, local table) {
|
|
|
491
487
|
return (asyncResult(callback, id)) // error from makeID
|
|
492
488
|
database.bucket.get(id, true, function(error, resp) {
|
|
493
489
|
if error
|
|
494
|
-
error = Error("md5 failed", error, id, { status: error.
|
|
490
|
+
error = Error("md5 failed", error, id, { status: error.$metadata.httpStatusCode })
|
|
495
491
|
else if resp._md5
|
|
496
492
|
resp = resp._md5
|
|
497
493
|
else
|
|
@@ -502,7 +498,10 @@ closure dawsTable(database, path, local table) {
|
|
|
502
498
|
|
|
503
499
|
// head
|
|
504
500
|
//
|
|
505
|
-
// Return the header of the specified record, i.e. everything except the content.
|
|
501
|
+
// Return the header of the specified record, i.e. everything except the content. This operates
|
|
502
|
+
// on directories (with names ending in slash) differently because AWS does not support calling
|
|
503
|
+
// headObject upon them. We simply want to confirm that the directory exists, so we list objects
|
|
504
|
+
// that have the name without the slash and we get a common prefix if it exists.
|
|
506
505
|
|
|
507
506
|
table.head = closure head(key, callback, local id) {
|
|
508
507
|
if !callback
|
|
@@ -510,15 +509,30 @@ closure dawsTable(database, path, local table) {
|
|
|
510
509
|
id = makeID(key)
|
|
511
510
|
if !string(id)
|
|
512
511
|
return (asyncResult(callback, id)) // error from makeID
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
resp.
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
512
|
+
if id.slice(-1) == "/"
|
|
513
|
+
database.bucket.list(id.slice(0,-1), true, function blcb(error, resp) {
|
|
514
|
+
if error
|
|
515
|
+
error = Error("head failed", error, id, { status: error.$metadata.httpStatusCode })
|
|
516
|
+
else if id != resp.CommonPrefixes.0.Prefix {
|
|
517
|
+
error = Error("head not found", id, resp.CommonPrefixes.0.Prefix, { status: 404 })
|
|
518
|
+
resp = false
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
resp.key = key
|
|
522
|
+
resp = s3resp2doc(resp)
|
|
523
|
+
resp._id = id }
|
|
524
|
+
callback(error, resp)
|
|
525
|
+
})
|
|
526
|
+
else
|
|
527
|
+
database.bucket.get(id, true, function bgcb(error, resp) {
|
|
528
|
+
if error
|
|
529
|
+
error = Error("head failed", error, id, { status: error.$metadata.httpStatusCode })
|
|
530
|
+
else {
|
|
531
|
+
resp.key = key
|
|
532
|
+
resp = s3resp2doc(resp)
|
|
533
|
+
resp._id = id }
|
|
534
|
+
callback(error, resp)
|
|
535
|
+
})
|
|
522
536
|
}
|
|
523
537
|
|
|
524
538
|
// copy
|
|
@@ -539,7 +553,7 @@ closure dawsTable(database, path, local table) {
|
|
|
539
553
|
error = Error("copy failed", {
|
|
540
554
|
source: srckey
|
|
541
555
|
destination: destkey
|
|
542
|
-
status: error.
|
|
556
|
+
status: error.$metadata.httpStatusCode
|
|
543
557
|
}, error)
|
|
544
558
|
callback(error, resp)
|
|
545
559
|
})
|
|
@@ -631,7 +645,7 @@ closure dawsTable(database, path, local table) {
|
|
|
631
645
|
s3prefix = table.s3prefix
|
|
632
646
|
database.bucket.list(s3prefix, !options.deep, function(error, objects, local obj, key) {
|
|
633
647
|
if error
|
|
634
|
-
error = Error("records list failed", error, options.prefix, { status: error.
|
|
648
|
+
error = Error("records list failed", error, options.prefix, { status: error.$metadata.httpStatusCode })
|
|
635
649
|
else {
|
|
636
650
|
for obj in objects.Contents {
|
|
637
651
|
if obj.Key.startsWith(table.s3prefix)
|
|
@@ -674,13 +688,13 @@ closure dawsTable(database, path, local table) {
|
|
|
674
688
|
*
|
|
675
689
|
*/
|
|
676
690
|
|
|
677
|
-
closure S3DB(
|
|
691
|
+
closure S3DB(s3b, local database, result) {
|
|
678
692
|
database = new(object, this)
|
|
679
|
-
result = dawsBucket(
|
|
693
|
+
result = dawsBucket(s3b)
|
|
680
694
|
if result.0
|
|
681
695
|
return (result) // can't access bucket
|
|
682
696
|
database.bucket = result.1
|
|
683
|
-
database.
|
|
697
|
+
database.s3b = s3b
|
|
684
698
|
database.type = "Amazon S3"
|
|
685
699
|
|
|
686
700
|
// isopen
|
|
@@ -700,7 +714,6 @@ closure S3DB(s3, local database, result) {
|
|
|
700
714
|
return (list(Error("database closed")))
|
|
701
715
|
database.bucket.close()
|
|
702
716
|
database.bucket = false
|
|
703
|
-
database.s3 = false
|
|
704
717
|
list(false, { ok: true })
|
|
705
718
|
}
|
|
706
719
|
|
|
@@ -712,10 +725,10 @@ closure S3DB(s3, local database, result) {
|
|
|
712
725
|
if !database.bucket
|
|
713
726
|
return (list(Error("database closed")))
|
|
714
727
|
result = {
|
|
715
|
-
type:
|
|
728
|
+
type: database.type
|
|
716
729
|
info: {
|
|
717
|
-
region:
|
|
718
|
-
bucket:
|
|
730
|
+
region: s3b.region
|
|
731
|
+
bucket: s3b.bucketName
|
|
719
732
|
}
|
|
720
733
|
}
|
|
721
734
|
list(false, result)
|
|
@@ -758,7 +771,7 @@ closure S3DB(s3, local database, result) {
|
|
|
758
771
|
|
|
759
772
|
function dawsInit(local manifest) {
|
|
760
773
|
|
|
761
|
-
manifest = `(
|
|
774
|
+
manifest = `(dawsBucket, dawsTable, S3DB, dawsInit)
|
|
762
775
|
|
|
763
776
|
Naan.module.build(module.id, "dbt_aws", function(modobj, compobj) {
|
|
764
777
|
require("./serviceAws.nlg")
|