@naanlang/naan 1.0.7 → 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.
- package/LICENSE.md +3 -4
- package/README.md +20 -2
- package/bin/index.js +2 -2
- package/dist/env_web.js +86 -17
- package/dist/naan.min.js +7 -7
- package/frameworks/browser/browser.nlg +26 -2
- package/frameworks/browser/sworker.js +17 -17
- package/frameworks/browser/workers.nlg +7 -0
- package/frameworks/client/psm_client.nlg +1 -0
- package/frameworks/common/common.nlg +33 -5
- package/frameworks/node/filesystem.nlg +32 -26
- package/frameworks/node/gitter.nlg +9 -4
- package/frameworks/node/https_request.nlg +19 -14
- package/frameworks/project/build.nlg +177 -68
- package/frameworks/project/projects.nlg +1 -1
- package/frameworks/running/running.nlg +32 -10
- package/frameworks/storage/psm.nlg +20 -4
- package/lib/browser/env_web.js +90 -21
- package/lib/core/naanlib.js +7 -7
- package/package.json +1 -1
- package/plugins/serviceAws/aws/aws-sdk-node.min.js +1 -1
- package/plugins/serviceAws/aws/aws-sdk.min.js +1 -1
- package/plugins/serviceAws/aws/lambda_ws_init.nlg +2 -1
- package/plugins/serviceAws/aws_dynamo.nlg +27 -20
- package/plugins/serviceAws/aws_dynextra.nlg +110 -5
- package/plugins/serviceAws/aws_lambda.nlg +4 -8
- package/plugins/serviceAws/aws_sqs.nlg +113 -0
- package/plugins/serviceAws/serviceAws.nlg +1 -1
- package/plugins/serviceYC/generic_api.yaml +36 -0
- package/plugins/serviceYC/naanide-relay-index.js +148 -0
- package/plugins/serviceYC/serviceYC.nlg +59 -0
- package/plugins/serviceYC/yc_function.nlg +161 -0
- package/test/test_01_core.nlg +2 -2
- package/test/test_02_context.nlg +2 -2
- package/test/test_05_strings.nlg +11 -1
- package/test/test_06_lingoparse.nlg +43 -14
|
@@ -652,7 +652,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
652
652
|
// Ensure that the table is ready before executing an operation. This returns an error tuple or
|
|
653
653
|
// false to proceed.
|
|
654
654
|
//
|
|
655
|
-
function preflightError(op) {
|
|
655
|
+
function preflightError(op, local result) {
|
|
656
656
|
if !dyna.aws
|
|
657
657
|
return (list(Error("DynaTable.".concat(op, ": not logged into AWS"))))
|
|
658
658
|
if !table.hashKey {
|
|
@@ -683,7 +683,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
683
683
|
//
|
|
684
684
|
// Get a record from the table with the specified hash value, and optional range value.
|
|
685
685
|
//
|
|
686
|
-
table.getRecord = closure getRecord(hashValue, rangeValue, local
|
|
686
|
+
table.getRecord = closure getRecord(hashValue, rangeValue, local params, pending) {
|
|
687
687
|
if preflightError(this)
|
|
688
688
|
return
|
|
689
689
|
params = {
|
|
@@ -708,7 +708,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
708
708
|
//
|
|
709
709
|
// Put a record in the table, replacing any existing record with the same key.
|
|
710
710
|
|
|
711
|
-
table.putRecord = closure putRecord(record, local
|
|
711
|
+
table.putRecord = closure putRecord(record, local params, pending) {
|
|
712
712
|
if preflightError(this)
|
|
713
713
|
return
|
|
714
714
|
params = {
|
|
@@ -737,13 +737,15 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
737
737
|
// set: [`(attribute, value)] // set attributes to values
|
|
738
738
|
// sum: [`(attribute, value)] // attributes += values
|
|
739
739
|
// diff: [`(attribute, value)] // attributes -= values
|
|
740
|
+
// add: [`(attribute, value)] // ADD(attributes, values) -- attrib need not exist
|
|
740
741
|
// remove: [attribute] // remove attributes from the record
|
|
741
742
|
// return: "all_old" | "all_new" | "updated_old" | "updated_new"
|
|
742
743
|
// // return the record as chosen, otherwise { }
|
|
743
744
|
// }
|
|
744
745
|
// The result is a standard result tuple `(error, record).
|
|
745
746
|
|
|
746
|
-
table.updateRecord = closure updateRecord(hashValue, rangeValue, options,
|
|
747
|
+
table.updateRecord = closure updateRecord(hashValue, rangeValue, options,
|
|
748
|
+
local error, params, actions, pending) {
|
|
747
749
|
if preflightError(this)
|
|
748
750
|
return
|
|
749
751
|
params = {
|
|
@@ -752,13 +754,10 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
752
754
|
params.Key = makeDynKey(list(hashValue, rangeValue))
|
|
753
755
|
if options.condition {
|
|
754
756
|
`(error) = dyna.conv.createKeyExpression(params, {
|
|
755
|
-
|
|
756
|
-
key: table.hashKey
|
|
757
|
-
"=": hashValue
|
|
758
|
-
}
|
|
757
|
+
ConditionExpression: options.condition
|
|
759
758
|
})
|
|
760
759
|
if error
|
|
761
|
-
return (list(Error("table.updateRecord:", error)))
|
|
760
|
+
return (list(Error("table.updateRecord condition:", error)))
|
|
762
761
|
}
|
|
763
762
|
dyna.conv.createUpdateExpression(params, options)
|
|
764
763
|
if options.return {
|
|
@@ -795,7 +794,8 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
795
794
|
// }
|
|
796
795
|
|
|
797
796
|
|
|
798
|
-
table.deleteRecord = closure deleteRecord(hashValue, rangeValue, options,
|
|
797
|
+
table.deleteRecord = closure deleteRecord(hashValue, rangeValue, options,
|
|
798
|
+
local params, pending) {
|
|
799
799
|
if preflightError(this)
|
|
800
800
|
return
|
|
801
801
|
params = {
|
|
@@ -804,13 +804,10 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
804
804
|
params.Key = makeDynKey(list(hashValue, rangeValue))
|
|
805
805
|
if options.condition {
|
|
806
806
|
`(error) = dyna.conv.createKeyExpression(params, {
|
|
807
|
-
|
|
808
|
-
key: table.hashKey
|
|
809
|
-
"=": hashValue
|
|
810
|
-
}
|
|
807
|
+
ConditionExpression: options.condition
|
|
811
808
|
})
|
|
812
809
|
if error
|
|
813
|
-
return (list(Error("table.deleteRecord:", error)))
|
|
810
|
+
return (list(Error("table.deleteRecord condition:", error)))
|
|
814
811
|
}
|
|
815
812
|
if options.return {
|
|
816
813
|
params.ReturnValues = {
|
|
@@ -871,7 +868,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
871
868
|
//
|
|
872
869
|
|
|
873
870
|
table.queryRecords = closure queryRecords(hashValue, options
|
|
874
|
-
local error,
|
|
871
|
+
local error, params, hashKey, op, expr, output, error)
|
|
875
872
|
{
|
|
876
873
|
if preflightError(this)
|
|
877
874
|
return
|
|
@@ -940,7 +937,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
940
937
|
//
|
|
941
938
|
|
|
942
939
|
table.scanRecords = closure scanRecords(options
|
|
943
|
-
local
|
|
940
|
+
local params, op, expr, output, error)
|
|
944
941
|
{
|
|
945
942
|
if preflightError(this)
|
|
946
943
|
return
|
|
@@ -1044,9 +1041,9 @@ closure DynamoDB(local dyna) {
|
|
|
1044
1041
|
dynexBatchGetRecords(dyna, request, options)
|
|
1045
1042
|
}
|
|
1046
1043
|
|
|
1047
|
-
//
|
|
1044
|
+
// batchWriteRecords
|
|
1048
1045
|
//
|
|
1049
|
-
dyna.batchWriteRecords = function
|
|
1046
|
+
dyna.batchWriteRecords = function loadBatchWriteRecords(request, options) {
|
|
1050
1047
|
require("./aws_dynextra.nlg")
|
|
1051
1048
|
dyna.batchWriteRecords = function bwr(request, options) {
|
|
1052
1049
|
dynexBatchWriteRecords(dyna, request, options)
|
|
@@ -1054,7 +1051,7 @@ closure DynamoDB(local dyna) {
|
|
|
1054
1051
|
dynexBatchWriteRecords(dyna, request, options)
|
|
1055
1052
|
}
|
|
1056
1053
|
|
|
1057
|
-
//
|
|
1054
|
+
// transactGetRecords
|
|
1058
1055
|
//
|
|
1059
1056
|
dyna.transactGetRecords = function loadTransactGetRecords(request, options) {
|
|
1060
1057
|
require("./aws_dynextra.nlg")
|
|
@@ -1064,6 +1061,16 @@ closure DynamoDB(local dyna) {
|
|
|
1064
1061
|
dynexTransactGetRecords(dyna, request, options)
|
|
1065
1062
|
}
|
|
1066
1063
|
|
|
1064
|
+
// transactWriteRecords
|
|
1065
|
+
//
|
|
1066
|
+
dyna.transactWriteRecords = function loadTransactWriteRecords(request, options) {
|
|
1067
|
+
require("./aws_dynextra.nlg")
|
|
1068
|
+
dyna.transactWriteRecords = function tgr(request, options) {
|
|
1069
|
+
dynexTransactWriteRecords(dyna, request, options)
|
|
1070
|
+
}
|
|
1071
|
+
dynexTransactWriteRecords(dyna, request, options)
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1067
1074
|
// finis
|
|
1068
1075
|
dyna
|
|
1069
1076
|
}
|
|
@@ -228,7 +228,6 @@ closure dynexBatchWriteRecords(dyna, request,
|
|
|
228
228
|
};
|
|
229
229
|
|
|
230
230
|
|
|
231
|
-
|
|
232
231
|
/*
|
|
233
232
|
* dynexTransactGetRecords
|
|
234
233
|
*
|
|
@@ -257,10 +256,10 @@ closure dynexTransactGetRecords(dyna, request,
|
|
|
257
256
|
for titem in request { // preflight all tables
|
|
258
257
|
tname = titem.table.name
|
|
259
258
|
if index[tname]
|
|
260
|
-
return (Error("DynaTable.
|
|
259
|
+
return (Error("DynaTable.transactGetRecords: duplicate table in request:", tname))
|
|
261
260
|
`(error) = titem.table.info()
|
|
262
261
|
if error
|
|
263
|
-
return (Error("DynaTable.
|
|
262
|
+
return (Error("DynaTable.transactGetRecords: table", tname, ":", error))
|
|
264
263
|
titem.data = []
|
|
265
264
|
index[tname] = titem
|
|
266
265
|
}
|
|
@@ -272,7 +271,7 @@ closure dynexTransactGetRecords(dyna, request,
|
|
|
272
271
|
params.TransactItems.push({
|
|
273
272
|
Get: {
|
|
274
273
|
TableName: tname
|
|
275
|
-
Key: makeDynKey(titem.keys.shift())
|
|
274
|
+
Key: titem.table.makeDynKey(titem.keys.shift())
|
|
276
275
|
}
|
|
277
276
|
})
|
|
278
277
|
}
|
|
@@ -297,6 +296,111 @@ closure dynexTransactGetRecords(dyna, request,
|
|
|
297
296
|
};
|
|
298
297
|
|
|
299
298
|
|
|
299
|
+
/*
|
|
300
|
+
* dynexTransactWriteRecords
|
|
301
|
+
*
|
|
302
|
+
* column positioning: // // !
|
|
303
|
+
* Write records to one or more tables using arrays of keys as a single transaction. The result is an
|
|
304
|
+
* error if anything failed or generic success if it worked. The request layout is as follows. Note
|
|
305
|
+
* that unlike the other dynex* calls, each table/key/operation item is separately specified.
|
|
306
|
+
*
|
|
307
|
+
* [
|
|
308
|
+
* {
|
|
309
|
+
* table: <DynaTable object> // table to act upon
|
|
310
|
+
* key: <hash-range tuple>
|
|
311
|
+
* put: {
|
|
312
|
+
* condition: <range-expression> // conditional expression
|
|
313
|
+
* item: <record-to-put>
|
|
314
|
+
* }
|
|
315
|
+
* update: {
|
|
316
|
+
* condition: <range-expression> // conditional expression
|
|
317
|
+
* set: [`(attribute, value)] // set attributes to values
|
|
318
|
+
* sum: [`(attribute, value)] // attributes += values
|
|
319
|
+
* diff: [`(attribute, value)] // attributes -= values
|
|
320
|
+
* add: [`(attribute, value)] // ADD(attributes, values) -- attrib need not exist
|
|
321
|
+
* remove: [attribute] // remove attributes from the record
|
|
322
|
+
* }
|
|
323
|
+
* delete: {
|
|
324
|
+
* condition: <range-expression> // conditional expression
|
|
325
|
+
* }
|
|
326
|
+
* check: {
|
|
327
|
+
* condition: <range-expression> // conditional expression
|
|
328
|
+
* return: "all_old" // optionally return record if condition fails
|
|
329
|
+
* }
|
|
330
|
+
* }
|
|
331
|
+
* { <next-item> }
|
|
332
|
+
* ]
|
|
333
|
+
* This returns a standard result tuple `(error, result). An error is only reported if we are
|
|
334
|
+
* unable to complete all operations.
|
|
335
|
+
*
|
|
336
|
+
*/
|
|
337
|
+
|
|
338
|
+
closure dynexTransactWriteRecords(dyna, request,
|
|
339
|
+
local index, titem, tname, error, params, opset, spec, op, pending, response) {
|
|
340
|
+
if !dyna.aws
|
|
341
|
+
return (list(Error("DynaTable.transactWriteRecords: not logged into AWS")))
|
|
342
|
+
request = new(request) // allow local modifications
|
|
343
|
+
index = { }
|
|
344
|
+
for titem in request { // preflight all tables
|
|
345
|
+
tname = titem.table.name
|
|
346
|
+
if !index[tname] {
|
|
347
|
+
`(error) = titem.table.info()
|
|
348
|
+
if error
|
|
349
|
+
return (Error("DynaTable.transactWriteRecords: table", tname, ":", error))
|
|
350
|
+
index[tname] = titem
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
params = {
|
|
354
|
+
TransactItems: []
|
|
355
|
+
}
|
|
356
|
+
for titem in request {
|
|
357
|
+
opset = {
|
|
358
|
+
TableName: titem.table.name
|
|
359
|
+
Key: titem.table.makeDynKey(titem.key)
|
|
360
|
+
}
|
|
361
|
+
if titem.check {
|
|
362
|
+
spec = titem.check
|
|
363
|
+
op = `ConditionCheck
|
|
364
|
+
if spec.return
|
|
365
|
+
opset.ReturnValues = "ALL_OLD"
|
|
366
|
+
} else if titem.put {
|
|
367
|
+
spec = titem.put
|
|
368
|
+
op = `Put
|
|
369
|
+
opset.Item = dyna.conv.recordNaanToDyn(spec.item)
|
|
370
|
+
opset.Key = undefined // key must be in item
|
|
371
|
+
} else if titem.update {
|
|
372
|
+
spec = titem.update
|
|
373
|
+
op = `Update
|
|
374
|
+
dyna.conv.createUpdateExpression(opset, spec)
|
|
375
|
+
} else if titem.delete {
|
|
376
|
+
spec = titem.delete
|
|
377
|
+
op = `Delete
|
|
378
|
+
}
|
|
379
|
+
if spec.condition {
|
|
380
|
+
`(error) = dyna.conv.createKeyExpression(opset, {
|
|
381
|
+
ConditionExpression: spec.condition
|
|
382
|
+
})
|
|
383
|
+
if error
|
|
384
|
+
return (list(Error("DynaTable.transactWriteRecords condition:", error)))
|
|
385
|
+
}
|
|
386
|
+
titem = { }
|
|
387
|
+
titem[op] = opset
|
|
388
|
+
params.TransactItems.push(titem)
|
|
389
|
+
}
|
|
390
|
+
pending = new(nonce)
|
|
391
|
+
dyna.aws.transactWriteItems(params, function(err, data) {
|
|
392
|
+
if err
|
|
393
|
+
debuglog("aws.transactWriteItems failed", err)
|
|
394
|
+
pending.signal(list(err, data))
|
|
395
|
+
})
|
|
396
|
+
`(error, response) = pending.wait()
|
|
397
|
+
if error
|
|
398
|
+
list(error)
|
|
399
|
+
else
|
|
400
|
+
list(false, response)
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
|
|
300
404
|
/*
|
|
301
405
|
* dynexInit
|
|
302
406
|
*
|
|
@@ -305,7 +409,8 @@ closure dynexTransactGetRecords(dyna, request,
|
|
|
305
409
|
*/
|
|
306
410
|
|
|
307
411
|
function dynexInit(local manifest) {
|
|
308
|
-
manifest = `(dynexBatchGetRecords, dynexBatchWriteRecords, dynexTransactGetRecords,
|
|
412
|
+
manifest = `(dynexBatchGetRecords, dynexBatchWriteRecords, dynexTransactGetRecords,
|
|
413
|
+
dynexTransactWriteRecords, dynexInit)
|
|
309
414
|
|
|
310
415
|
Naan.module.build(module.id, "aws_dynextra", function(modobj, compobj) {
|
|
311
416
|
compobj.manifest = manifest
|
|
@@ -46,10 +46,8 @@ closure Lambda(local lamb) {
|
|
|
46
46
|
params.Qualifier = qualifier
|
|
47
47
|
pending = new(nonce)
|
|
48
48
|
lamb.aws.getFunction(params, function(error, data) {
|
|
49
|
-
if error
|
|
50
|
-
|
|
51
|
-
debuglog("aws.getFunction failed", error)
|
|
52
|
-
}
|
|
49
|
+
if error
|
|
50
|
+
ErrorDebuglog("aws.getFunction failed", error)
|
|
53
51
|
pending.signal(list(error, data))
|
|
54
52
|
})
|
|
55
53
|
pending.wait()
|
|
@@ -69,10 +67,8 @@ closure Lambda(local lamb) {
|
|
|
69
67
|
}
|
|
70
68
|
pending = new(nonce)
|
|
71
69
|
lamb.aws.updateFunctionCode(params, function(error, data) {
|
|
72
|
-
if error
|
|
73
|
-
|
|
74
|
-
debuglog("aws.updateFunctionCode failed", error)
|
|
75
|
-
}
|
|
70
|
+
if error
|
|
71
|
+
ErrorDebuglog("aws.updateFunctionCode failed", error)
|
|
76
72
|
pending.signal(list(error, data))
|
|
77
73
|
})
|
|
78
74
|
pending.wait()
|
|
@@ -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
|
+
} ();
|
|
@@ -27,7 +27,7 @@ function sawsInit(local manifest) {
|
|
|
27
27
|
require("./aws_utils.nlg")
|
|
28
28
|
function sawsPostload() {
|
|
29
29
|
if js.g
|
|
30
|
-
awsSDK = js.r(JSpath.
|
|
30
|
+
awsSDK = js.r(JSpath.resolve(js.d, module.locpath, "aws/aws-sdk-node.min.js"))
|
|
31
31
|
else
|
|
32
32
|
awsSDK = JsLoadScript(JSpath.join(module.locpath, "./aws/aws-sdk.min.js"), "awsSdk")
|
|
33
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
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* index.js
|
|
3
|
+
* naanide-relay
|
|
4
|
+
*
|
|
5
|
+
* Yandex Cloud Function for proxying API requests from NaanIDE in the browser.
|
|
6
|
+
*
|
|
7
|
+
* This function acts as a relay (mitm) proxy so that a browser may access Yandex Cloud APIs without
|
|
8
|
+
* needing a transient access token and without CORS problems. Security is provided by a pre-shared
|
|
9
|
+
* static key, configured as an environment variable in the YCF and known to the browser code.
|
|
10
|
+
*
|
|
11
|
+
* The request to the proxy should be precisely what is intended for the target API, except that the
|
|
12
|
+
* following headers should be included:
|
|
13
|
+
*
|
|
14
|
+
* x-naanide-auth: <pre-shared-key>
|
|
15
|
+
* x-naanide-ep: <target-url-with-path-and-query-variables>
|
|
16
|
+
* x-naanide-hdrs: <optional headers>
|
|
17
|
+
*
|
|
18
|
+
* The response in the browser contains some remapped headers, but is otherwise the API's response.
|
|
19
|
+
*
|
|
20
|
+
* Assuming the pre-shared key is "foo", the following gets a list of YDB databases in a folder:
|
|
21
|
+
|
|
22
|
+
curl -H "x-naanide-auth: foo" \
|
|
23
|
+
-H "x-naanide-ep: https://ydb.api.cloud.yandex.net/ydb/v1/databases?folderId=b1gjjahqm5u9tjolom9l" \
|
|
24
|
+
'https://functions.yandexcloud.net/d4e5gicpajcqdbj91jt8'
|
|
25
|
+
|
|
26
|
+
*
|
|
27
|
+
* column positioning: // // !
|
|
28
|
+
*
|
|
29
|
+
* Copyright (c) 2022 by Richard C. Zulch
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* index.handler
|
|
36
|
+
*
|
|
37
|
+
* Forward a request and respond with the result.
|
|
38
|
+
*
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
module.exports.handler = async function (event, context) {
|
|
42
|
+
"use strict";
|
|
43
|
+
const authorizationKey = process.env.NAANIDE_AUTH_KEY;
|
|
44
|
+
|
|
45
|
+
//
|
|
46
|
+
// OPTIONS request
|
|
47
|
+
//
|
|
48
|
+
// Allow our headers for CORS
|
|
49
|
+
//
|
|
50
|
+
|
|
51
|
+
if (event.httpMethod == "OPTIONS")
|
|
52
|
+
return {
|
|
53
|
+
statusCode: 200,
|
|
54
|
+
headers: {
|
|
55
|
+
"Access-Control-Allow-Headers": "*"
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
//
|
|
60
|
+
// Failed authentication
|
|
61
|
+
//
|
|
62
|
+
|
|
63
|
+
if (event.headers["X-Naanide-Auth"] != authorizationKey) {
|
|
64
|
+
return {
|
|
65
|
+
statusCode: 401
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//
|
|
70
|
+
// Authenticated
|
|
71
|
+
//
|
|
72
|
+
|
|
73
|
+
let https = require("https");
|
|
74
|
+
let url = event.headers["X-Naanide-Ep"];
|
|
75
|
+
let headers = event.headers["X-Naanide-Hdrs"];
|
|
76
|
+
if (headers) {
|
|
77
|
+
try {
|
|
78
|
+
headers = JSON.parse(headers);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.log("X-Naanide-Hdrs parse error", e);
|
|
81
|
+
headers = false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!headers)
|
|
85
|
+
headers = { };
|
|
86
|
+
if (!headers[Object.keys(headers).find(key => key.toLowerCase() === "authorization")])
|
|
87
|
+
headers.Authorization = context.token.token_type.concat(" ", context.token.access_token);
|
|
88
|
+
let options = {
|
|
89
|
+
method: event.httpMethod,
|
|
90
|
+
headers: headers
|
|
91
|
+
};
|
|
92
|
+
let chunks = [];
|
|
93
|
+
let content = false;
|
|
94
|
+
let finalResponse = {
|
|
95
|
+
statusCode: 500
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
//
|
|
99
|
+
// forward_request
|
|
100
|
+
//
|
|
101
|
+
async function forward_request() {
|
|
102
|
+
return new Promise(function(resolve, reject) {
|
|
103
|
+
let req = https.request(url, options, function(resp) {
|
|
104
|
+
finalResponse.statusCode = resp.statusCode;
|
|
105
|
+
// data event
|
|
106
|
+
resp.on("data", function (chunk) {
|
|
107
|
+
chunks.push(chunk);
|
|
108
|
+
});
|
|
109
|
+
// end event
|
|
110
|
+
resp.on("end", function () {
|
|
111
|
+
if (resp.complete)
|
|
112
|
+
{
|
|
113
|
+
let content = Buffer.concat(chunks);
|
|
114
|
+
let contentType = resp.headers["content-type"];
|
|
115
|
+
finalResponse.body = content.toString();
|
|
116
|
+
if (resp.headers["x-request-id"]) {
|
|
117
|
+
resp.headers["x-upstream-request-id"] = resp.headers["x-request-id"];
|
|
118
|
+
delete resp.headers["x-request-id"];
|
|
119
|
+
}
|
|
120
|
+
finalResponse.headers = resp.headers;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log("request terminated before full response received");
|
|
124
|
+
finalResponse.statusCode = 502;
|
|
125
|
+
}
|
|
126
|
+
resolve();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
// error event
|
|
130
|
+
req.on("error", function (err) {
|
|
131
|
+
console.log("upstream request error:", err);
|
|
132
|
+
finalResponse.statusCode = 502;
|
|
133
|
+
reject(err);
|
|
134
|
+
});
|
|
135
|
+
console.log("event.body", event.body, event.isBase64Encoded);
|
|
136
|
+
if (event.body) {
|
|
137
|
+
if (event.isBase64Encoded)
|
|
138
|
+
req.write(decodeURIComponent(escape(atob(event.body))));
|
|
139
|
+
else
|
|
140
|
+
req.write(event.body);
|
|
141
|
+
}
|
|
142
|
+
req.end();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await forward_request();
|
|
147
|
+
return finalResponse;
|
|
148
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* serviceYC.nlg
|
|
3
|
+
* serviceYC
|
|
4
|
+
*
|
|
5
|
+
* ServiceYC module configuration and management for browser and NodeJS.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2022 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* YcRequest
|
|
16
|
+
*
|
|
17
|
+
* Make a request to a Yandex Cloud API using a Yandex Cloud Function acting as a relay proxy.
|
|
18
|
+
* This works like frameworks/browser/HttpsApiRequest and frameworks/node/HttpsApiRequest except
|
|
19
|
+
* that it takes a credentials argument for access to the relay. Technically this is not required
|
|
20
|
+
* when we are going from node, but I'm keeping it for consistency.
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
closure YcRequest(url, creds, options, local headers) {
|
|
25
|
+
if !options
|
|
26
|
+
options = { }
|
|
27
|
+
else
|
|
28
|
+
options = new(options)
|
|
29
|
+
headers = [
|
|
30
|
+
list("x-naanide-auth", creds.relayAuthKey)
|
|
31
|
+
list("x-naanide-ep", url)
|
|
32
|
+
]
|
|
33
|
+
if options.headers
|
|
34
|
+
headers.push(list("x-naanide-hdrs", options.headers.join("\n")))
|
|
35
|
+
options.headers = headers
|
|
36
|
+
https.HttpsApiRequest(creds.relayURL, options)
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
* seycInit
|
|
42
|
+
*
|
|
43
|
+
* Initialize the YC module.
|
|
44
|
+
*
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
function seycInit(local manifest) {
|
|
48
|
+
manifest = `(YcRequest, seycInit)
|
|
49
|
+
|
|
50
|
+
Naan.module.build(module.id, "serviceYC", function(modobj, compobj) {
|
|
51
|
+
compobj.manifest = manifest
|
|
52
|
+
require("frameworks/common").LiveImport()
|
|
53
|
+
if js.g
|
|
54
|
+
https = require("frameworks/node/https_request.nlg")
|
|
55
|
+
else
|
|
56
|
+
https = require("frameworks/browser/https_request.nlg")
|
|
57
|
+
module.exports.YcRequest = YcRequest
|
|
58
|
+
})
|
|
59
|
+
} ();
|