@naanlang/naan 1.0.6 → 1.0.8

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.
Files changed (45) hide show
  1. package/LICENSE.md +3 -4
  2. package/README.md +20 -2
  3. package/bin/index.js +2 -2
  4. package/dist/env_web.js +91 -18
  5. package/dist/naan.min.js +7 -7
  6. package/frameworks/browser/browser.nlg +92 -3
  7. package/frameworks/browser/https_request.nlg +2 -2
  8. package/frameworks/browser/sworker.js +17 -17
  9. package/frameworks/browser/terminals.nlg +14 -12
  10. package/frameworks/browser/workers.nlg +13 -6
  11. package/frameworks/client/psm_client.nlg +1 -0
  12. package/frameworks/common/common.nlg +39 -8
  13. package/frameworks/node/filesystem.nlg +32 -26
  14. package/frameworks/node/gitter.nlg +9 -4
  15. package/frameworks/node/https_request.nlg +19 -14
  16. package/frameworks/project/build.nlg +187 -74
  17. package/frameworks/project/projects.nlg +42 -17
  18. package/frameworks/running/running.nlg +33 -11
  19. package/frameworks/running/taskexec.nlg +1 -1
  20. package/frameworks/storage/file_manager.nlg +2 -1
  21. package/frameworks/storage/psm.nlg +20 -4
  22. package/frameworks/storage/psm_dbtables.nlg +13 -3
  23. package/lib/browser/env_web.js +93 -20
  24. package/lib/browser/env_webworker.js +10 -0
  25. package/lib/core/naanlib.js +7 -7
  26. package/package.json +2 -1
  27. package/plugins/serviceAws/aws/aws-sdk-node.min.js +1 -1
  28. package/plugins/serviceAws/aws/aws-sdk.min.js +1 -1
  29. package/plugins/serviceAws/aws/lambda_ws_init.nlg +2 -1
  30. package/plugins/serviceAws/aws_dynamo.nlg +45 -26
  31. package/plugins/serviceAws/aws_dynextra.nlg +157 -32
  32. package/plugins/serviceAws/aws_lambda.nlg +4 -8
  33. package/plugins/serviceAws/aws_s3.nlg +74 -28
  34. package/plugins/serviceAws/aws_sqs.nlg +113 -0
  35. package/plugins/serviceAws/dbt_aws.nlg +73 -24
  36. package/plugins/serviceAws/psm_aws.nlg +30 -11
  37. package/plugins/serviceAws/serviceAws.nlg +2 -4
  38. package/plugins/serviceYC/generic_api.yaml +36 -0
  39. package/plugins/serviceYC/naanide-relay-index.js +148 -0
  40. package/plugins/serviceYC/serviceYC.nlg +59 -0
  41. package/plugins/serviceYC/yc_function.nlg +161 -0
  42. package/test/test_01_core.nlg +2 -2
  43. package/test/test_02_context.nlg +2 -2
  44. package/test/test_05_strings.nlg +11 -1
  45. package/test/test_06_lingoparse.nlg +43 -14
@@ -13,17 +13,36 @@
13
13
 
14
14
  /*
15
15
  * S3bucket
16
- *
17
- * S3 buckets.
16
+ *
17
+ * Bucket ooptions:
18
+ * {
19
+ * cacheControl: "max-age=2592000" - default cache control, e.g. to "max-age=2592000"
20
+ * }
18
21
  *
19
22
  */
20
23
 
21
- closure S3bucket(s3, name, local bucket) {
24
+ closure S3bucket(s3, name, bucketOptions, local bucket) {
22
25
  bucket = new(object, this)
23
26
  bucket.name = name
24
-
27
+
28
+ //
29
+ // head
30
+ //
31
+ bucket.head = closure head(key, local pending) {
32
+ pending = new(nonce)
33
+ s3.aws.headObject({
34
+ Bucket: bucket.name
35
+ Key: key
36
+ }, function (error, data) {
37
+ if error
38
+ error = Error("S3bucket.head failed:", error.name, "key:", key, "in", bucket.name)
39
+ pending.signal(list(error, data))
40
+ })
41
+ pending.wait()
42
+ }
43
+
25
44
  //
26
- // list
45
+ // listObjects
27
46
  //
28
47
  bucket.listObjects = closure listObjects(local pending) {
29
48
  pending = new(nonce)
@@ -31,31 +50,46 @@ closure S3bucket(s3, name, local bucket) {
31
50
  Bucket: bucket.name
32
51
  }, function (error, data) {
33
52
  if error
34
- error = Error("S3bucket.list failed", error)
53
+ error = Error("S3bucket.list failed:", error.name, "for", bucket.name)
35
54
  pending.signal(list(error, data))
36
55
  })
37
56
  pending.wait()
38
57
  }
39
58
 
40
59
  //
41
- // upload
60
+ // put
42
61
  //
43
- bucket.upload = closure upload(key, data, local pending) {
44
- pending = new(nonce)
45
- s3.aws.upload({
62
+ // options:
63
+ // {
64
+ // contentType: "text/plain" - override contentType if specified
65
+ // cacheControl: "max-age=2592000" - default cache control, e.g. to "max-age=2592000"
66
+ // metadata: { <items> } - optional dictionary of keys to store
67
+ // }
68
+ //
69
+ bucket.put = closure put(key, data, options, local params, pending) {
70
+ if !contentType
71
+ contentType = "application/octet-stream"
72
+ params = {
46
73
  Bucket: bucket.name
47
- ACL: "public-read"
48
- Body: data
49
74
  Key: key
50
- ContentType: "application/octet-stream" // force download from browser
51
- })
52
- .on("httpUploadProgress", function(evt) {
53
- debuglog("upload progress", totuple(evt))
54
- })
55
- .send(function(error, data) {
75
+ ContentMD5: HashMD5_base64(data)
76
+ Body: data
77
+ }
78
+ if options.contentType
79
+ params.ContentType = options.contentType
80
+ if options.metadata
81
+ params.Metadata = options.metadata
82
+ else
83
+ params.Metadata = "application/octet-stream"
84
+ if options.cacheControl
85
+ params.CacheControl = options.cacheControl
86
+ else if bucket.cacheControl
87
+ params.CacheControl = bucket.cacheControl
88
+ pending = new(nonce)
89
+ s3.aws.putObject(params, function(error, resp) {
56
90
  if error
57
- error = Error("S3bucket.upload failed", error)
58
- pending.signal(list(error, data))
91
+ error = Error("S3bucket.put failed:", error.name, "key:", key, "in", bucket.name)
92
+ pending.signal(list(error, resp))
59
93
  })
60
94
  pending.wait()
61
95
  }
@@ -88,6 +122,8 @@ closure S3(local s3) {
88
122
  accessKeyId: creds.keyID
89
123
  secretAccessKey: creds.keySecret
90
124
  }
125
+ if creds.endpoint
126
+ params.endpoint = creds.endpoint
91
127
  }
92
128
  s3.aws = xnew(awsSDK.S3, params)
93
129
  if awsSDK.S3Client
@@ -111,12 +147,23 @@ closure S3(local s3) {
111
147
  // This obtains a pre-signed URL for the specified command and its parameters. Options control
112
148
  // how the URL is generated.
113
149
  //
150
+ // CommandParams:
151
+ // {
152
+ // Bucket: <string> // bucket name
153
+ // Key: <string> // object being operated upon
154
+ // ContentType: <string> // video/mp4 or whatever
155
+ // ResponseCacheControl: <string> // reportedly for getObject only
156
+ // ACL: <string> // e.g. "public-read"
157
+ // Body: <data> // fiik
158
+ // }
159
+ //
114
160
  // Options:
115
161
  // {
116
- // expiresIn: <seconds> // default expiration is 900 (15 minutes)
162
+ // expiresIn: <seconds> // default expiration is 900 (15 minutes)
163
+ // signingDate: <epoch-secs> // reference time for URL being created
117
164
  // }
118
165
  //
119
- s3.getSignedURL = closure getSignedURL(cmdname, cmdparams, local command, options, pending) {
166
+ s3.getSignedURL = closure getSignedURL(cmdname, cmdparams, options, local command, pending) {
120
167
  if s3.client {
121
168
  if cmdname == "putObject"
122
169
  cmdname = "PutObjectCommand"
@@ -124,16 +171,15 @@ closure S3(local s3) {
124
171
  cmdname = "GetObjectCommand"
125
172
  else
126
173
  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
174
  command = xnew(awsSDK.s3Commands[cmdname], cmdparams)
134
175
  await(awsSDK.getSignedUrl(s3.client, command, options))
135
176
  } else {
177
+ debuglog("s3.getSignedURL: warning: old API")
136
178
  pending = new(nonce)
179
+ if options.expiresIn {
180
+ cmdparams = new(cmdparams)
181
+ cmdparams.Expires = options.expiresIn
182
+ }
137
183
  s3.aws.getSignedUrl(cmdname, cmdparams, function(error, data) {
138
184
  pending.signal(list(error, data))
139
185
  })
@@ -0,0 +1,113 @@
1
+ /*
2
+ * aws_sqs.nlg
3
+ * serviceAws
4
+ *
5
+ * Access to SQS (Simple Queue Service) for AWS.
6
+ *
7
+ * column positioning: // // !
8
+ *
9
+ * Copyright (c) 2023 by Richard C. Zulch
10
+ *
11
+ */
12
+
13
+
14
+ /*
15
+ * SqsMQ
16
+ *
17
+ * SqsMQ, a container for holding the queue name and sending messages to it.
18
+ *
19
+ */
20
+
21
+ closure SqsMQ(sqs, queueUrl, local sqsq) {
22
+ sqsq = new(object, this)
23
+
24
+ //
25
+ // sendMessage
26
+ //
27
+ sqsq.sendMessage = closure sendMessage(msgBody) {
28
+ sqs.sendMessage(queueUrl, msgBody)
29
+ }
30
+
31
+ // finis
32
+ sqsq
33
+ }
34
+
35
+
36
+ /*
37
+ * SQS
38
+ *
39
+ * SQS service functions.
40
+ *
41
+ */
42
+
43
+ closure SQS(local sqs) {
44
+ sqs = new(object, this)
45
+
46
+ //
47
+ // login
48
+ //
49
+ sqs.login = closure login(creds, local params) {
50
+ if !awsSDK
51
+ return (list(Error("SQS.login: no AWS SDK available")))
52
+ params = {
53
+ }
54
+ if creds {
55
+ params.region = creds.region
56
+ params.credentials = {
57
+ accessKeyId: creds.keyID
58
+ secretAccessKey: creds.keySecret
59
+ }
60
+ if creds.sqs_endpoint
61
+ params.endpoint = creds.sqs_endpoint
62
+ }
63
+ sqs.aws = xnew(awsSDK.SQS, params)
64
+ `(false, { ok: true })
65
+ }
66
+
67
+ //
68
+ // sendMessage
69
+ //
70
+ sqs.sendMessage = closure sendMessage(queueUrl, msgBody, local params, pending) {
71
+ debuglog("SQS.sendMessage:", awsSDK, sqs.aws, sqs.aws.sendMessage)
72
+ params = {
73
+ MessageBody: msgBody
74
+ QueueUrl: queueUrl
75
+ }
76
+ pending = new(nonce)
77
+ sqs.aws.sendMessage(params, function(error, data) {
78
+ if error
79
+ ErrorDebuglog("aws.sendMessage failed", error)
80
+ pending.signal(list(error, data))
81
+ })
82
+ pending.wait()
83
+ }
84
+
85
+ //
86
+ // mq
87
+ //
88
+ sqs.mq = function(queueUrl) {
89
+ SqsMQ(sqs, queueUrl)
90
+ }
91
+
92
+ // finis
93
+ sqs
94
+ }
95
+
96
+
97
+ /*
98
+ * sqsInit
99
+ *
100
+ * Initialize the SQS module.
101
+ *
102
+ */
103
+
104
+ function sqsInit(local manifest) {
105
+ manifest = `(SqsMQ, SQS, sqsInit)
106
+
107
+ Naan.module.build(module.id, "aws_sqs", function(modobj, compobj) {
108
+ require("./serviceAws.nlg")
109
+ compobj.manifest = manifest
110
+ modobj.exports.SQS = SQS
111
+ })
112
+
113
+ } ();
@@ -37,6 +37,19 @@ closure dawsBucket(s3b, local bucket, s3) {
37
37
  closure asyncResult args {
38
38
  future(function() { apply(pop(args), args) }).run()
39
39
  }
40
+
41
+ // hashFromETag
42
+ //
43
+ // Return the MD5 hash for an ETag
44
+ //
45
+ function hashFromETag(etag) {
46
+ if etag.startsWith("W/")
47
+ etag = etag.slice(2) // "weak" from gzip with NGINX, apparently
48
+ if etag.length == 34 // multipart S3 ETag will is longer and will be ignored
49
+ etag.substring(1,33)
50
+ else
51
+ undefined
52
+ }
40
53
 
41
54
  // close
42
55
  //
@@ -65,8 +78,8 @@ closure dawsBucket(s3b, local bucket, s3) {
65
78
  function s3GetCB(error, resp) {
66
79
  if error
67
80
  error = Error("can't get object", error, key)
68
- else if resp.ETag.length == 34
69
- resp.md5 = resp.ETag.substring(1,33) // ETags have to have quotes, but our hash doesn't
81
+ else
82
+ resp.md5 = hashFromETag(resp.ETag)
70
83
  callback(error, resp)
71
84
  }
72
85
 
@@ -79,12 +92,18 @@ closure dawsBucket(s3b, local bucket, s3) {
79
92
  // put
80
93
  //
81
94
  // Put an object in the bucket. Parameters include:
82
- // key - the object name, which may not be empty or start with "/"
83
- // data - optional content to store
84
- // metadata - optional dictionary of keys to store
95
+ // key - the object name, which may not be empty or start with "/"
96
+ // data - optional content to store
97
+ // options:
98
+ // {
99
+ // contentType: "text/plain" - override contentType if specified
100
+ // cacheControl: "max-age=2592000" - default cache control, e.g. to "max-age=2592000"
101
+ // metadata: { <items> } - optional dictionary of keys to store
102
+ // }
85
103
 
86
- bucket.put = closure put(key, data, metadata, callback, local params, mimeType, md5) {
87
- if !string(key) || length(key) == 0 || key.substring(0,1) == "/" || metadata && !dictionary(metadata)
104
+ bucket.put = closure put(key, data, options, callback,
105
+ local params, mimeType, md5) {
106
+ if !string(key) || length(key) == 0 || key.substring(0,1) == "/"
88
107
  return (asyncResult(callback, Error("invalid arguments")))
89
108
  if !s3
90
109
  return (asyncResult(callback, Error("database closed")))
@@ -128,9 +147,13 @@ closure dawsBucket(s3b, local bucket, s3) {
128
147
  mimeType = mimeType.concat("; charset=UTF-8")
129
148
  params.ContentType = mimeType
130
149
  }
150
+ if options.contentType
151
+ params.ContentType = options.contentType
131
152
  }
132
- if metadata
133
- params.Metadata = metadata
153
+ if options.metadata
154
+ params.Metadata = options.metadata
155
+ if options.cacheControl
156
+ params.CacheControl = options.cacheControl
134
157
  if data
135
158
  params.Body = data
136
159
  else
@@ -142,11 +165,10 @@ closure dawsBucket(s3b, local bucket, s3) {
142
165
  error = Error("can't put object", error, key)
143
166
  else {
144
167
  resp.length = length(data)
145
- if resp.ETag.length == 34 { // multipart S3 ETag will is longer and will be ignored
146
- resp.md5 = resp.ETag.substring(1,33) // ETags have to have quotes, but our hash doesn't
147
- if md5 != resp.md5 {
148
- error = Error("hash match failure", md5, resp.md5) // has match failed on round-trip
149
- resp = false } } }
168
+ resp.md5 = hashFromETag(resp.ETag)
169
+ if resp.md5 && md5 != resp.md5 {
170
+ error = Error("hash match failure", md5, resp.md5) // has match failed on round-trip
171
+ resp = false } }
150
172
  callback(error, resp)
151
173
  })
152
174
  }
@@ -285,12 +307,31 @@ closure dawsBucket(s3b, local bucket, s3) {
285
307
  *
286
308
  */
287
309
 
288
- closure dawsTable(database, path, local table) {
310
+ closure dawsTable(database, path, tableOptions, local table) {
289
311
  table = new(object, this)
290
312
  table.type = database.type
291
313
  table.db = database
292
314
  table.path = path
293
315
  table.s3prefix = makePrefix(path)
316
+
317
+ // readableStreamToArrayBuffer
318
+ //
319
+ // For browsers, convert a ReadableStream to an array buffer.
320
+ //
321
+ function readableStreamToArrayBuffer(stream, local result, reader, error, data, chunk) {
322
+ result = xnew(js.w.Uint8Array, 0)
323
+ reader = stream.getReader()
324
+ loop {
325
+ `(error, data) = await(reader.read())
326
+ if error || data.done
327
+ break
328
+ chunk = xnew(js.w.Uint8Array, result.length + data.value.length)
329
+ chunk.set(result)
330
+ chunk.set(data.value, result.length)
331
+ result = chunk
332
+ }
333
+ result
334
+ }
294
335
 
295
336
  // makePrefix
296
337
  //
@@ -344,7 +385,7 @@ closure dawsTable(database, path, local table) {
344
385
  if resp.ContentType
345
386
  doc.contentType = resp.ContentType
346
387
  if resp.md5
347
- doc.md5 = resp.md5
388
+ doc._md5 = resp.md5
348
389
  doc
349
390
  }
350
391
 
@@ -394,11 +435,14 @@ closure dawsTable(database, path, local table) {
394
435
  if content
395
436
  doc.content = content
396
437
  metadata = metadata2s3(metadata) // filter out any invalid metadata
397
- database.bucket.put(id, content, metadata, function(error, resp) {
438
+ database.bucket.put(id, content, {
439
+ metadata: metadata
440
+ cacheControl: tableOptions.cacheControl
441
+ }, function(error, resp) {
398
442
  if error
399
443
  error = Error("add failed", error, id, { status: error.$metadata.httpStatusCode })
400
444
  else {
401
- doc.md5 = resp.md5 // update the original doc
445
+ doc._md5 = resp.md5 // update the original doc
402
446
  doc.length = resp.length }
403
447
  callback(error, doc)
404
448
  })
@@ -411,14 +455,17 @@ closure dawsTable(database, path, local table) {
411
455
  table.update = closure update(doc, callback, local metadata) {
412
456
  if !callback
413
457
  return (syncAdapter(update, doc))
414
- if !doc._id || !doc._id.startsWith(table.s3prefix) || !doc.md5
458
+ if !doc._id || !doc._id.startsWith(table.s3prefix) || !doc._md5
415
459
  return (asyncResult(callback, Error("invalid argument")))
416
460
  metadata = metadata2s3(doc)
417
- database.bucket.put(doc._id, doc.content, metadata, function(error, resp) {
461
+ database.bucket.put(doc._id, doc.content, {
462
+ metadata: metadata
463
+ cacheControl: tableOptions.cacheControl
464
+ }, function(error, resp) {
418
465
  if error
419
466
  error = Error("update failed", error, doc._id, { status: error.$metadata.httpStatusCode })
420
467
  else {
421
- doc.md5 = resp.md5 // update the original doc
468
+ doc._md5 = resp.md5 // update the original doc
422
469
  doc.modified = undefined // we don't know exact modify date
423
470
  doc.length = resp.length }
424
471
  callback(error, doc)
@@ -433,7 +480,7 @@ closure dawsTable(database, path, local table) {
433
480
  table.delete = closure delete(doc, callback) {
434
481
  if !callback
435
482
  return (syncAdapter(delete, doc))
436
- if !doc._id || !doc._id.startsWith(table.s3prefix) || !doc.md5
483
+ if !doc._id || !doc._id.startsWith(table.s3prefix) || !(doc._md5 || doc._id.slice(-1) == "/")
437
484
  return (asyncResult(callback, Error("invalid argument")))
438
485
  database.bucket.delete(doc._id, function(error, resp) {
439
486
  if error
@@ -469,6 +516,8 @@ closure dawsTable(database, path, local table) {
469
516
  { } // already "decoded", e.g. base64
470
517
  else if jsTypedArray(resp.content)
471
518
  resp.content = xnew(TextDecoder, "utf-8").decode(resp.content)
519
+ else if jsInstanceOf(resp.content, js.w.ReadableStream)
520
+ resp.content = xnew(TextDecoder, "utf-8").decode(readableStreamToArrayBuffer(resp.content))
472
521
  resp._id = id }
473
522
  callback(error, resp)
474
523
  })
@@ -748,12 +797,12 @@ closure S3DB(s3b, local database, result) {
748
797
  //
749
798
  // Return a table-access object for the table.
750
799
 
751
- database.table = function table(path) {
800
+ database.table = function table(path, options) {
752
801
  if !database.bucket
753
802
  return (list(Error("database closed")))
754
803
  if !string(path)
755
804
  return (list(Error("invalid table path")))
756
- dawsTable(database, path)
805
+ dawsTable(database, path, options)
757
806
  }
758
807
 
759
808
  // finis
@@ -47,7 +47,11 @@ closure psmaConnector(psm, local connClassID, connector, watch) {
47
47
  {
48
48
  label: "Bucket"
49
49
  placeholder: "e.g. myBucketName"
50
- key: "bucketName" }
50
+ key: "bucketName" },
51
+ {
52
+ label: "Endpoint"
53
+ placeholder: "custom URL"
54
+ key: "endpoint" }
51
55
  ]
52
56
  }
53
57
 
@@ -59,6 +63,7 @@ closure psmaConnector(psm, local connClassID, connector, watch) {
59
63
  `(error, creds) = connector.vault.accessResource(resID)
60
64
  if creds && (!resource.region || resource.region == creds.region)
61
65
  && (!resource.bucketName || resource.bucketName == creds.bucketName)
66
+ && (!resource.endpoint || resource.endpoint == creds.endpoint)
62
67
  && (!resource.label || resource.label == creds.label)
63
68
  return (resID)
64
69
  }
@@ -75,6 +80,7 @@ closure psmaConnector(psm, local connClassID, connector, watch) {
75
80
  // keySecret - AWS IAM keySecret
76
81
  // region - AWS region
77
82
  // bucketName - AWS bucketName
83
+ // endpoint - custom URL
78
84
  // hidden - [optional] don't enumerate to user
79
85
  // locked - [optional] don't allow user delete
80
86
  // The return value is an (error, resID) tuple. Error, if non-false, is a dictionary of field
@@ -90,6 +96,8 @@ closure psmaConnector(psm, local connClassID, connector, watch) {
90
96
  region: resource.region,
91
97
  bucketName: resource.bucketName
92
98
  }
99
+ if resource.endpoint
100
+ creds.endpoint = resource.endpoint
93
101
  errors = { }
94
102
  if resource.label
95
103
  creds.label = resource.label
@@ -107,6 +115,8 @@ closure psmaConnector(psm, local connClassID, connector, watch) {
107
115
  errors.region = "required field"
108
116
  if badField(resource.bucketName)
109
117
  errors.bucketName = "required field"
118
+ if resource.endpoint && badField(resource.endpoint)
119
+ errors.endpoint = "invalid field"
110
120
  if length(errors) > 0
111
121
  error = errors
112
122
  else {
@@ -164,6 +174,8 @@ closure psmaConnector(psm, local connClassID, connector, watch) {
164
174
  resource.region = creds.region
165
175
  if string(creds.bucketName)
166
176
  resource.bucketName = creds.bucketName
177
+ if string(creds.endpoint)
178
+ resource.endpoint = creds.endpoint
167
179
  list(false, resource)
168
180
  }
169
181
 
@@ -209,22 +221,28 @@ closure psmaConnector(psm, local connClassID, connector, watch) {
209
221
  //
210
222
  // Return an S3 access object, which is the credentials to access an AWS S3 bucket.
211
223
 
212
- connector.access = function access(resID, local error, creds, s3b) {
224
+ connector.access = function access(resID, local error, creds, params, s3b) {
213
225
  `(error, creds) = connector.vault.accessResource(resID)
214
226
  if error
215
227
  return (list(error)) // can't access that resource
216
228
  if !awsSDK.S3
217
229
  return (list(Error("S3 connector: no AWS SDK available")))
230
+ params = {
231
+ region: creds.region
232
+ credentials: {
233
+ accessKeyId: creds.keyID
234
+ secretAccessKey: creds.keySecret
235
+ }
236
+ }
218
237
  s3b = {
219
- s3: xnew(awsSDK.S3, {
220
- region: creds.region
221
- credentials: {
222
- accessKeyId: creds.keyID
223
- secretAccessKey: creds.keySecret
224
- }})
225
238
  bucketName: creds.bucketName
226
239
  region: creds.region
227
240
  }
241
+ if creds.endpoint {
242
+ params.endpoint = creds.endpoint
243
+ s3b.endpoint = creds.endpoint
244
+ }
245
+ s3b.s3 = xnew(awsSDK.S3, params)
228
246
  list(false, s3b)
229
247
  }
230
248
 
@@ -232,10 +250,11 @@ closure psmaConnector(psm, local connClassID, connector, watch) {
232
250
  //
233
251
  // Connect to an AWS bucket filesystem, returning a view.
234
252
 
235
- connector.connect = function connect(resID, service, args, local error, s3b, db, table) {
253
+ connector.connect = function connect(resID, service, args,
254
+ local error, s3b, db, rootpath, table) {
236
255
  `(error, s3b) = connector.access(resID)
237
256
  if !error
238
- `(error, db) = S3DB(s3b) // get S3 bucket as a database
257
+ `(error, db) = S3DB(s3b, args.1) // get S3 bucket as a database
239
258
  if error
240
259
  list(error)
241
260
  else if service == "NideDB"
@@ -244,7 +263,7 @@ closure psmaConnector(psm, local connClassID, connector, watch) {
244
263
  rootpath = args.0 // connect to a NideFS filesystem
245
264
  if !rootpath
246
265
  return (Error("mandatory rootpath missing"))
247
- `(error, table) = db.table(rootpath)
266
+ `(error, table) = db.table(rootpath, args.1) // args.1 is table options; see dawsTable
248
267
  if error
249
268
  list(error)
250
269
  else
@@ -26,10 +26,8 @@ function sawsInit(local manifest) {
26
26
  require("frameworks/common").LiveImport()
27
27
  require("./aws_utils.nlg")
28
28
  function sawsPostload() {
29
- if js.lambda
30
- awsSDK = js.r("/opt/aws-sdk-node.min.js") // SDK is a layer
31
- else if js.g
32
- awsSDK = js.r("aws-sdk-node.min") // SDK must be in search path
29
+ if js.g
30
+ awsSDK = js.r(JSpath.resolve(js.d, module.locpath, "aws/aws-sdk-node.min.js"))
33
31
  else
34
32
  awsSDK = JsLoadScript(JSpath.join(module.locpath, "./aws/aws-sdk.min.js"), "awsSdk")
35
33
  module.exports.awsSDK = awsSDK
@@ -0,0 +1,36 @@
1
+ openapi: 3.0.0
2
+ info:
3
+ title: tutor-backend API
4
+ version: 1.0.0
5
+ servers:
6
+ - url: https://d5dhrgakpe39e11lqefr.apigw.yandexcloud.net
7
+ paths:
8
+ /{proxy+}:
9
+ options:
10
+ parameters:
11
+ - name: proxy
12
+ in: path
13
+ required: true
14
+ schema:
15
+ type: string
16
+ x-yc-apigateway-integration:
17
+ type: dummy
18
+ http_code: 200
19
+ http_headers:
20
+ Access-Control-Allow-Headers: "*"
21
+ Access-Control-Allow-Methods: "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT"
22
+ Access-Control-Allow-Origin: "*"
23
+ content:
24
+ "application/json": ""
25
+ x-yc-apigateway-any-method:
26
+ parameters:
27
+ - name: proxy
28
+ in: path
29
+ required: true
30
+ schema:
31
+ type: string
32
+ x-yc-apigateway-integration:
33
+ type: cloud_functions
34
+ function_id: d4ehjjcfk8ffe43o5t7j
35
+ tag: $latest
36
+ service_account_id: ajeuftvbtshn348ert80