@naanlang/naan 1.2.0 → 1.3.0
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 +1 -1
- package/bin/index.js +5 -2
- package/dist/env_web.js +11 -1
- package/dist/naan.min.js +5 -5
- package/frameworks/browser/https_request.nlg +2 -0
- package/frameworks/browser/sworker.js +23 -23
- package/frameworks/browser/terminals.nlg +132 -20
- package/frameworks/browser/workers.nlg +4 -1
- package/frameworks/browser/ws_client.nlg +9 -3
- package/frameworks/client/apiclient.nlg +8 -6
- package/frameworks/client/psm_client.nlg +4 -4
- package/frameworks/client/relaycon.nlg +5 -1
- package/frameworks/common/common.nlg +10 -4
- package/frameworks/node/apiserver.nlg +190 -4
- package/frameworks/node/worker.nlg +112 -10
- package/frameworks/node/ws_client.nlg +10 -3
- package/frameworks/project/build.nlg +13 -11
- package/frameworks/project/projects.nlg +7 -5
- package/frameworks/running/executors.nlg +124 -34
- package/frameworks/running/taskexec.nlg +5 -1
- package/lib/browser/env_web.js +17 -7
- package/lib/browser/env_webworker.js +22 -3
- package/lib/core/naanlib.js +5 -5
- package/lib/env_node.js +15 -4
- package/lib/env_nodeworker.js +21 -1
- package/package.json +8 -5
- package/plugins/serviceAws/aws/aws-sdk-node.min.js +2 -2
- package/plugins/serviceAws/aws/aws-sdk.min.js +3 -2
- package/plugins/serviceAws/aws_dynamo.nlg +19 -19
- package/plugins/serviceAws/dbt_aws.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 +60 -0
- package/plugins/serviceYC/yc_apigateway.nlg +205 -0
- package/plugins/serviceYC/yc_function.nlg +163 -0
- package/plugins/serviceYC/yc_vm.nlg +207 -0
- package/test/test_02_context.nlg +1 -1
- package/test/test_03_jsinterop.nlg +2 -2
- package/test/test_05_strings.nlg +7 -4
- package/test/test_07_lingo.nlg +1 -1
|
@@ -191,21 +191,23 @@ closure DynaConverter(local conv) {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
// datumNaanToDyn
|
|
194
|
-
// Convert a Naan datum to DynamoDB format.
|
|
194
|
+
// Convert a Naan datum to DynamoDB format. If no valid conversion is known then it becomes NULL
|
|
195
|
+
// in DynamoDB and a debug warning is logged. Note that this is not called for arbitrary symbols
|
|
196
|
+
// from genAttributeValueAdder() because it uses symbols to refer to actual attributes by name.
|
|
195
197
|
//
|
|
196
198
|
conv.datumNaanToDyn = function datumNaanToDyn(datum, local output, ntype, dtype) {
|
|
197
199
|
output = { }
|
|
198
200
|
if tuple(datum)
|
|
199
201
|
datum = datum.toarray
|
|
200
|
-
if
|
|
202
|
+
if Array.isArray(datum) {
|
|
201
203
|
dtype = "L" // heterogenous list
|
|
202
204
|
datum = datum.map(function(item) {
|
|
203
205
|
datumNaanToDyn(item)
|
|
204
206
|
})
|
|
205
207
|
} else {
|
|
206
208
|
ntype = typeof(datum)
|
|
207
|
-
if ntype == xobject
|
|
208
|
-
dtype = "B" //
|
|
209
|
+
if ntype == xobject && jsTypedArray(datum)
|
|
210
|
+
dtype = "B" // binary/blob
|
|
209
211
|
else if ntype == dictionary {
|
|
210
212
|
dtype = "M"
|
|
211
213
|
datum = recordNaanToDyn(datum)
|
|
@@ -217,21 +219,19 @@ closure DynaConverter(local conv) {
|
|
|
217
219
|
else if ntype == string
|
|
218
220
|
dtype = "S"
|
|
219
221
|
else if ntype == symbol {
|
|
220
|
-
if datum ===
|
|
221
|
-
dtype = "NULL"
|
|
222
|
-
datum = "true"
|
|
223
|
-
}
|
|
224
|
-
else if datum === true || datum === false
|
|
222
|
+
if datum === true || datum === false
|
|
225
223
|
dtype = "BOOL"
|
|
226
|
-
else if datum
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
} else
|
|
224
|
+
else if datum !== null {
|
|
225
|
+
debuglog("DynaConverter.datumNaanToDyn: nonstandard symbol", datum)
|
|
226
|
+
datum = null }
|
|
227
|
+
} else {
|
|
232
228
|
debuglog("DynaConverter.datumNaanToDyn: invalid type", ntype)
|
|
229
|
+
datum = null }
|
|
233
230
|
}
|
|
234
|
-
|
|
231
|
+
if datum === null
|
|
232
|
+
output["NULL"] = true
|
|
233
|
+
else
|
|
234
|
+
output[dtype] = datum
|
|
235
235
|
output
|
|
236
236
|
}
|
|
237
237
|
|
|
@@ -277,7 +277,7 @@ closure DynaConverter(local conv) {
|
|
|
277
277
|
// Convert a DynamoDB typed item dictionary to Naan format.
|
|
278
278
|
//
|
|
279
279
|
conv.recordDynToNaan = function recordDynToNaan(ditem, local output, key, data, dtype, dvalue) {
|
|
280
|
-
if
|
|
280
|
+
if Array.isArray(ditem) { // process an array
|
|
281
281
|
output = []
|
|
282
282
|
for data in ditem
|
|
283
283
|
output.push(recordDynToNaan(data))
|
|
@@ -429,7 +429,7 @@ closure DynaConverter(local conv) {
|
|
|
429
429
|
attval = genAttributeValueAdder(params)
|
|
430
430
|
attname = genAttributeNameAdder(params)
|
|
431
431
|
for `(rangename, ranges) in conds {
|
|
432
|
-
if !
|
|
432
|
+
if !Array.isArray(ranges)
|
|
433
433
|
ranges = [ranges]
|
|
434
434
|
for range in ranges {
|
|
435
435
|
rangex = false
|
|
@@ -930,7 +930,7 @@ closure DynaTable(dyna, tablename, options, local table) {
|
|
|
930
930
|
remaining -= processed
|
|
931
931
|
params.ExclusiveStartKey = data.LastEvaluatedKey
|
|
932
932
|
if processed && remaining > 0 && data.LastEvaluatedKey {
|
|
933
|
-
params.
|
|
933
|
+
params.Limit = remaining // set up for next iteration
|
|
934
934
|
true // continue looping
|
|
935
935
|
}
|
|
936
936
|
})
|
|
@@ -513,7 +513,7 @@ closure dawsTable(database, path, tableOptions, local table) {
|
|
|
513
513
|
resp._error = "JSON decoding failed"
|
|
514
514
|
} }
|
|
515
515
|
else if resp.contentType.startsWith("image/")
|
|
516
|
-
|
|
516
|
+
resp.content = readableStreamToArrayBuffer(resp.content)
|
|
517
517
|
else if jsTypedArray(resp.content)
|
|
518
518
|
resp.content = xnew(TextDecoder, "utf-8").decode(resp.content)
|
|
519
519
|
else if jsInstanceOf(resp.content, js.w.ReadableStream)
|
|
@@ -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,60 @@
|
|
|
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("naanlib:frameworks/common").LiveImport()
|
|
53
|
+
if js.g {
|
|
54
|
+
nodeHttp = js.r("http")
|
|
55
|
+
https = require("naanlib:frameworks/node/https_request.nlg") }
|
|
56
|
+
else
|
|
57
|
+
https = require("naanlib:frameworks/browser/https_request.nlg")
|
|
58
|
+
module.exports.YcRequest = YcRequest
|
|
59
|
+
})
|
|
60
|
+
} ();
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* yc_apigateway.nlg
|
|
3
|
+
* serviceYC
|
|
4
|
+
*
|
|
5
|
+
* Support for YC API Gateway and websockets.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2024 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* Gateway
|
|
16
|
+
*
|
|
17
|
+
* Handler for incoming websocket messages from the YC API Gateway.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
closure Gateway(local apig) {
|
|
22
|
+
global(https)
|
|
23
|
+
apig = new(object, this)
|
|
24
|
+
|
|
25
|
+
// open
|
|
26
|
+
//
|
|
27
|
+
// Initialize for a new gateway, but we don't know anything about it yet. This should ensure we
|
|
28
|
+
// can talk with the cloud, returning a result tuple. The first message that comes in will have
|
|
29
|
+
// headers that identify the gateway resource specifically.
|
|
30
|
+
//
|
|
31
|
+
apig.open = closure open() {
|
|
32
|
+
apig.ycvm = VmControl()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// close
|
|
36
|
+
//
|
|
37
|
+
// Close this handler because no more requests will come in.
|
|
38
|
+
//
|
|
39
|
+
apig.close = closure close() {
|
|
40
|
+
apig.ycvm = false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// requested
|
|
44
|
+
//
|
|
45
|
+
// A new websocket operation was requested. The ExpressJS request/response structures are passed.
|
|
46
|
+
//
|
|
47
|
+
// The requested return value for connect:
|
|
48
|
+
// {
|
|
49
|
+
// op: "connect"
|
|
50
|
+
// connID: <string>
|
|
51
|
+
// connAt: <integer>
|
|
52
|
+
// }
|
|
53
|
+
// The requested return value for disconnect:
|
|
54
|
+
// {
|
|
55
|
+
// op: "disconnect"
|
|
56
|
+
// connID: <string>
|
|
57
|
+
// code: <integer>
|
|
58
|
+
// reason: <string>
|
|
59
|
+
// }
|
|
60
|
+
// The requested return value for message:
|
|
61
|
+
// {
|
|
62
|
+
// op: "message"
|
|
63
|
+
// connID: <string>
|
|
64
|
+
// message: <string>
|
|
65
|
+
// contType: <string>
|
|
66
|
+
// }
|
|
67
|
+
//
|
|
68
|
+
// Example request headers from /wsgw endpoint:
|
|
69
|
+
// {
|
|
70
|
+
// user-agent : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
|
|
71
|
+
// accept-encoding : "gzip",
|
|
72
|
+
// accept-language : "en-US,en;q=0.9,ru;q=0.8",
|
|
73
|
+
// cache-control : "no-cache",
|
|
74
|
+
// host : "tutorbe-1.ru-central1.internal",
|
|
75
|
+
// origin : "https://d5di07c52f5hpi7h8kb8.apigw.yandexcloud.net",
|
|
76
|
+
// pragma : "no-cache",
|
|
77
|
+
// sec-websocket-extensions: "permessage-deflate; client_max_window_bits",
|
|
78
|
+
// uber-trace-id : "0000000000000000014a60f3326789e4:1a4ee8a332acff59:0:01",
|
|
79
|
+
// x-api-gateway-function-id: "d4e16asgl4t3i2j1m7t4",
|
|
80
|
+
// x-envoy-external-address: "64.201.120.56",
|
|
81
|
+
// x-envoy-original-path: "/?guid=1a5d070b-6aaf-4421-aa66-b13d15a7eaa3",
|
|
82
|
+
// x-forwarded-for : "64.201.120.56",
|
|
83
|
+
// x-forwarded-proto : "https",
|
|
84
|
+
// x-real-remote-address: "64.201.120.56",
|
|
85
|
+
// x-request-id : "ceaa9f57-4cb8-4fce-8081-c065b8073130",
|
|
86
|
+
// x-serverless-certificate-ids: "{}",
|
|
87
|
+
// x-serverless-gateway-id: "d5di07c52f5hpi7h8kb8",
|
|
88
|
+
// x-trace-id : "3e393eb7-4680-4d71-8b3d-824e5b768255",
|
|
89
|
+
// x-yc-apigateway-websocket-connected-at: "1725131521366",
|
|
90
|
+
// x-yc-apigateway-websocket-connection-id: "c05188b1jdifd7gsn7bqqochiv94l622u",
|
|
91
|
+
// x-yc-apigateway-websocket-event-type: "CONNECT",
|
|
92
|
+
// x-naanide-guid : "1a5d070b-6aaf-4421-aa66-b13d15a7eaa3",
|
|
93
|
+
// connection : "close"
|
|
94
|
+
// }
|
|
95
|
+
//
|
|
96
|
+
apig.requested = closure requested(req, res, local data, eventType) {
|
|
97
|
+
if !apig.gatewayID
|
|
98
|
+
apig.gatewayID = req.get("x-serverless-gateway-id")
|
|
99
|
+
data = {
|
|
100
|
+
connID: req.get("x-yc-apigateway-websocket-connection-id")
|
|
101
|
+
connAt: toint(req.get("x-yc-apigateway-websocket-connected-at"))
|
|
102
|
+
guid: req.get("x-naanide-guid") || undefined
|
|
103
|
+
}
|
|
104
|
+
eventType = req.get("x-yc-apigateway-websocket-event-type")
|
|
105
|
+
if eventType == "CONNECT" {
|
|
106
|
+
data.op = "connect"
|
|
107
|
+
}
|
|
108
|
+
else if eventType == "DISCONNECT" {
|
|
109
|
+
data.op = "disconnect"
|
|
110
|
+
data.code = req.get("X-Yc-Apigateway-Websocket-Disconnect-Status-Code")
|
|
111
|
+
data.reason = req.get("X-Yc-Apigateway-Websocket-Disconnect-Reason")
|
|
112
|
+
}
|
|
113
|
+
else if eventType == "MESSAGE" {
|
|
114
|
+
data.op = "message"
|
|
115
|
+
data.message = new(req.body)
|
|
116
|
+
data.contType = req.get('content-type')
|
|
117
|
+
} else
|
|
118
|
+
debuglog("serviceYC::Gateway.requested: invalid event type", eventType)
|
|
119
|
+
list(false, data)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// send
|
|
123
|
+
//
|
|
124
|
+
// Send a websocket message via the API Gateway.
|
|
125
|
+
//
|
|
126
|
+
apig.send = closure send(connID, msg, local error, authHeader, data) {
|
|
127
|
+
`(error, authHeader) = apig.ycvm.authHeader()
|
|
128
|
+
if !string(msg)
|
|
129
|
+
msg = JSONstringify(msg)
|
|
130
|
+
data = {
|
|
131
|
+
data: EncodeBase64(msg)
|
|
132
|
+
type: "TEXT" // allegedly "BINARY" is an option
|
|
133
|
+
}
|
|
134
|
+
if !error
|
|
135
|
+
`(error, data) = https.HttpsApiRequest("https://apigateway-connections.api.cloud.yandex.net/apigateways/websocket/v1/connections/${connID}:send", {
|
|
136
|
+
method: "POST"
|
|
137
|
+
headers: [ authHeader ]
|
|
138
|
+
putdata: data
|
|
139
|
+
debug: true
|
|
140
|
+
})
|
|
141
|
+
if error {
|
|
142
|
+
ErrorDebuglog("serviceYC::Gateway.send failed:", error)
|
|
143
|
+
list(error)
|
|
144
|
+
} else
|
|
145
|
+
list(false, data)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// get
|
|
149
|
+
//
|
|
150
|
+
// Get websocket connection status from the API Gateway.
|
|
151
|
+
//
|
|
152
|
+
apig.get = closure get(connID, local error, authHeader, data) {
|
|
153
|
+
`(error, authHeader) = apig.ycvm.authHeader()
|
|
154
|
+
if !error
|
|
155
|
+
`(error, data) = https.HttpsApiRequest("https://apigateway-connections.api.cloud.yandex.net/apigateways/websocket/v1/connections/${connID}", {
|
|
156
|
+
method: "GET"
|
|
157
|
+
headers: [ authHeader ]
|
|
158
|
+
})
|
|
159
|
+
if error {
|
|
160
|
+
ErrorDebuglog("serviceYC::Gateway.get failed:", error)
|
|
161
|
+
list(error)
|
|
162
|
+
} else
|
|
163
|
+
list(false, data)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// disconnect
|
|
167
|
+
//
|
|
168
|
+
// Disconnect a websocket connection from the API Gateway.
|
|
169
|
+
//
|
|
170
|
+
apig.disconnect = closure disconnect(connID, local error, authHeader, data) {
|
|
171
|
+
`(error, authHeader) = apig.ycvm.authHeader()
|
|
172
|
+
if !error
|
|
173
|
+
`(error, data) = https.HttpsApiRequest("https://apigateway-connections.api.cloud.yandex.net/apigateways/websocket/v1/connections/${connID}", {
|
|
174
|
+
method: "DELETE"
|
|
175
|
+
headers: [ authHeader ]
|
|
176
|
+
})
|
|
177
|
+
if error {
|
|
178
|
+
ErrorDebuglog("serviceYC::Gateway.disconnect failed:", error)
|
|
179
|
+
list(error)
|
|
180
|
+
} else
|
|
181
|
+
list(false, data)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// finis
|
|
185
|
+
|
|
186
|
+
apig
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
/*
|
|
191
|
+
* ycagInit
|
|
192
|
+
*
|
|
193
|
+
* Initialize the YC module.
|
|
194
|
+
*
|
|
195
|
+
*/
|
|
196
|
+
|
|
197
|
+
function ycagInit(local manifest) {
|
|
198
|
+
manifest = `(Gateway, ycagInit)
|
|
199
|
+
|
|
200
|
+
Naan.module.build(module.id, "yc_apigateway", function(modobj, compobj) {
|
|
201
|
+
require("./yc_vm.nlg")
|
|
202
|
+
compobj.manifest = manifest
|
|
203
|
+
module.exports.Gateway = Gateway
|
|
204
|
+
})
|
|
205
|
+
} ();
|