@blokjs/trigger-grpc 0.6.18 → 0.6.19
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/dist/GRpcTrigger.js +15 -0
- package/dist/GRpcTrigger.js.map +1 -1
- package/package.json +7 -6
- package/.env.example +0 -8
- package/CHANGELOG.md +0 -143
- package/__tests__/unit/GRpcTrigger.test.ts +0 -313
- package/__tests__/unit/GrpcClient.test.ts +0 -138
- package/__tests__/unit/GrpcServer.test.ts +0 -127
- package/__tests__/unit/MessageDecode.test.ts +0 -314
- package/__tests__/unit/NanoSDK.test.ts +0 -162
- package/__tests__/unit/Nodes.test.ts +0 -16
- package/buf.gen.yaml +0 -11
- package/buf.yaml +0 -9
- package/localhost+2-key.pem +0 -28
- package/localhost+2.pem +0 -27
- package/proto/workflow.proto +0 -33
- package/src/GRpcTrigger.ts +0 -265
- package/src/GrpcClient.ts +0 -87
- package/src/GrpcServer.ts +0 -63
- package/src/MessageDecode.ts +0 -142
- package/src/NanoSDK.ts +0 -146
- package/src/Nodes.ts +0 -10
- package/src/Workflows.ts +0 -5
- package/src/gen/workflow_pb.ts +0 -142
- package/src/index.ts +0 -22
- package/src/opentelemetry_metrics.ts +0 -26
- package/src/server.ts +0 -8
- package/src/types/Message.ts +0 -0
- package/src/types/RuntimeWorkflow.ts +0 -7
- package/src/types/Workflows.ts +0 -7
- package/tsconfig.json +0 -34
- package/vitest.config.ts +0 -29
- package/workflows/countries.json +0 -31
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
// Use vi.hoisted for mocks referenced inside vi.mock factories
|
|
5
|
-
const { mockCall } = vi.hoisted(() => {
|
|
6
|
-
const mockCall = vi.fn().mockResolvedValue({
|
|
7
|
-
Message: Buffer.from(JSON.stringify({ result: "ok" })).toString("base64"),
|
|
8
|
-
Encoding: "BASE64",
|
|
9
|
-
Type: "JSON",
|
|
10
|
-
});
|
|
11
|
-
return { mockCall };
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
vi.mock("../../src/GrpcClient", () => {
|
|
15
|
-
class MockGrpcClient {
|
|
16
|
-
call = mockCall;
|
|
17
|
-
}
|
|
18
|
-
return {
|
|
19
|
-
default: MockGrpcClient,
|
|
20
|
-
HttpVersionEnum: { HTTP1: "1.1", HTTP2: "2" },
|
|
21
|
-
TransportEnum: { GRPC: "grpc", GRPC_WEB: "grpc-web", CONNECT: "connect" },
|
|
22
|
-
};
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
vi.mock("../../src/MessageDecode", () => {
|
|
26
|
-
class MockMessageDecode {
|
|
27
|
-
responseDecode() {
|
|
28
|
-
return { result: "ok" };
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return { default: MockMessageDecode };
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
import NanoSDK, { NanoSDKClient } from "../../src/NanoSDK";
|
|
35
|
-
|
|
36
|
-
describe("NanoSDK", () => {
|
|
37
|
-
let sdk: NanoSDK;
|
|
38
|
-
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
sdk = new NanoSDK();
|
|
41
|
-
vi.clearAllMocks();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
afterEach(() => {
|
|
45
|
-
vi.restoreAllMocks();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe("createClient()", () => {
|
|
49
|
-
it("should create client with explicit host and token", () => {
|
|
50
|
-
const client = sdk.createClient("localhost:8433", "my-token");
|
|
51
|
-
expect(client).toBeDefined();
|
|
52
|
-
expect(client).toBeInstanceOf(NanoSDKClient);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("should create client with host only (empty token)", () => {
|
|
56
|
-
const client = sdk.createClient("myhost:9090");
|
|
57
|
-
expect(client).toBeDefined();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("should read nanosdk.json when no host provided", () => {
|
|
61
|
-
vi.spyOn(fs, "existsSync").mockReturnValue(true);
|
|
62
|
-
vi.spyOn(fs, "readFileSync").mockReturnValue(JSON.stringify({ host: "127.0.0.1:5000", token: "file-token" }));
|
|
63
|
-
|
|
64
|
-
const client = sdk.createClient();
|
|
65
|
-
expect(fs.existsSync).toHaveBeenCalled();
|
|
66
|
-
expect(fs.readFileSync).toHaveBeenCalled();
|
|
67
|
-
expect(client).toBeDefined();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("should use default host when no host and no nanosdk.json", () => {
|
|
71
|
-
vi.spyOn(fs, "existsSync").mockReturnValue(false);
|
|
72
|
-
|
|
73
|
-
const client = sdk.createClient();
|
|
74
|
-
expect(client).toBeDefined();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("should throw for host without port and without protocol", () => {
|
|
78
|
-
expect(() => sdk.createClient("invalidhost")).toThrow(
|
|
79
|
-
"Invalid host format. The host must have the format domain:port",
|
|
80
|
-
);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("should accept host with http:// prefix", () => {
|
|
84
|
-
const client = sdk.createClient("http://localhost");
|
|
85
|
-
expect(client).toBeDefined();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("should accept host with https:// prefix", () => {
|
|
89
|
-
const client = sdk.createClient("https://myserver");
|
|
90
|
-
expect(client).toBeDefined();
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe("NanoSDKClient", () => {
|
|
96
|
-
let client: NanoSDKClient;
|
|
97
|
-
|
|
98
|
-
beforeEach(() => {
|
|
99
|
-
vi.clearAllMocks();
|
|
100
|
-
const sdk = new NanoSDK();
|
|
101
|
-
client = sdk.createClient("localhost:8433", "test-token");
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
describe("python3()", () => {
|
|
105
|
-
it("should build correct request for Python3 node", async () => {
|
|
106
|
-
const result = await client.python3("my-node", { url: "http://example.com" });
|
|
107
|
-
expect(mockCall).toHaveBeenCalled();
|
|
108
|
-
const callArgs = mockCall.mock.calls[0];
|
|
109
|
-
const request = callArgs[0];
|
|
110
|
-
expect(request.Name).toBe("my-node");
|
|
111
|
-
expect(request.Encoding).toBe("BASE64");
|
|
112
|
-
expect(request.Type).toBe("JSON");
|
|
113
|
-
const decoded = JSON.parse(Buffer.from(request.Message, "base64").toString("utf-8"));
|
|
114
|
-
expect(decoded.workflow.steps[0].type).toBe("runtime.python3");
|
|
115
|
-
expect(decoded.workflow.steps[0].node).toBe("my-node");
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("should pass inputs to the workflow node config", async () => {
|
|
119
|
-
const inputs = { url: "http://api.com", method: "POST" };
|
|
120
|
-
await client.python3("api-call", inputs);
|
|
121
|
-
const request = mockCall.mock.calls[0][0];
|
|
122
|
-
const decoded = JSON.parse(Buffer.from(request.Message, "base64").toString("utf-8"));
|
|
123
|
-
expect(decoded.workflow.nodes.node.inputs).toEqual(inputs);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("should return decoded response", async () => {
|
|
127
|
-
const result = await client.python3("my-node", {});
|
|
128
|
-
expect(result).toEqual({ result: "ok" });
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
describe("nodejs()", () => {
|
|
133
|
-
it("should build correct request for Node.js node", async () => {
|
|
134
|
-
await client.nodejs("my-node", { data: "test" });
|
|
135
|
-
const request = mockCall.mock.calls[0][0];
|
|
136
|
-
const decoded = JSON.parse(Buffer.from(request.Message, "base64").toString("utf-8"));
|
|
137
|
-
expect(decoded.workflow.steps[0].type).toBe("module");
|
|
138
|
-
expect(decoded.workflow.steps[0].node).toBe("my-node");
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("should use custom type parameter", async () => {
|
|
142
|
-
await client.nodejs("my-node", {}, "local");
|
|
143
|
-
const request = mockCall.mock.calls[0][0];
|
|
144
|
-
const decoded = JSON.parse(Buffer.from(request.Message, "base64").toString("utf-8"));
|
|
145
|
-
expect(decoded.workflow.steps[0].type).toBe("local");
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("should include http trigger in workflow", async () => {
|
|
149
|
-
await client.nodejs("my-node", {});
|
|
150
|
-
const request = mockCall.mock.calls[0][0];
|
|
151
|
-
const decoded = JSON.parse(Buffer.from(request.Message, "base64").toString("utf-8"));
|
|
152
|
-
expect(decoded.workflow.trigger.http).toBeDefined();
|
|
153
|
-
expect(decoded.workflow.trigger.http.method).toBe("GET");
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it("should pass authorization header", async () => {
|
|
157
|
-
await client.nodejs("my-node", {});
|
|
158
|
-
const callOpts = mockCall.mock.calls[0][1];
|
|
159
|
-
expect(callOpts.headers.Authorization).toBe("Bearer test-token");
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import nodes from "../../src/Nodes";
|
|
3
|
-
|
|
4
|
-
describe("Nodes", () => {
|
|
5
|
-
it("should export an object with node keys", () => {
|
|
6
|
-
expect(typeof nodes).toBe("object");
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it("should have @blokjs/api-call key", () => {
|
|
10
|
-
expect(nodes["@blokjs/api-call"]).toBeDefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("should have @blokjs/if-else key", () => {
|
|
14
|
-
expect(nodes["@blokjs/if-else"]).toBeDefined();
|
|
15
|
-
});
|
|
16
|
-
});
|
package/buf.gen.yaml
DELETED
package/buf.yaml
DELETED
package/localhost+2-key.pem
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
-----BEGIN PRIVATE KEY-----
|
|
2
|
-
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNjfXRKBwXU0IH
|
|
3
|
-
ftJJGiR5w6Ccf2JfPZJgDdarhVgJjL2cy9d4AwIq4ydiuU+H8MnguzdGrFaCgYOB
|
|
4
|
-
GfgEI4dOAGr1AInRVVHRWKvwGdLYm59dBRi5PQuU2EKVonQNsZy/qcOJSBzBSSAx
|
|
5
|
-
PFCpTwrxj0j/Dug6LxIa1wB7UenYn5NjkFSF8F5HufE7l4goIVh0/fVgRripCsnk
|
|
6
|
-
iMQFVhMr+2glX+19zTVkBl9EtAPbkXsegUUdnkOH2sd1TBN4HUibnktX+P1aIlhd
|
|
7
|
-
p741Eni42jU92u33iZasvvnkA94Q2MhBhzx3lgqAAcHcePzBI9yOEQbTqik4Nkpv
|
|
8
|
-
moqqDFBbAgMBAAECggEACDu0aeFCZsjF70jsIVNzvJDSYRJVHV14/HHxdENOCGvj
|
|
9
|
-
uGENy1fLgW5ByJmFjvtdCqxVBXanrGiFnOkuXvQRWlV380JHQuQ497qmgGJ4aIar
|
|
10
|
-
Nm2a1OiWCysyV43/OgEwNxkUpO4HgNJuAKrazD1HQe+sS9MMCDCUeBC+izH3vN5s
|
|
11
|
-
9LWOrhde+ZwCkHJgRxp6V5HBJXU/27T02ZjgvPXupzXQ2ozRgx2gn5lLvJJfqG/h
|
|
12
|
-
jEMi9qyuwoRUL4QXDg3TNHHSllnENdRrVJr7VsvYpRpQogVsr4V7zIZiFkAcbuC4
|
|
13
|
-
kMuHeahkjqmlSYVuz9DxPHcmFRW/B5MI5MLQwCvI6QKBgQD61emfMrA323QIWF5l
|
|
14
|
-
hbNQBS3hje9pwN27EU3QmCq0z/2Ja0TTaSeNSdlQAfMEGHsO5tL0ldqS267uqs5r
|
|
15
|
-
clrukrOFmcARZiHne5U1uMcWgYvRmPTTdwng1lQSBuLo7XR9f6sW3u1csOJ8PY4w
|
|
16
|
-
oKtsAIKm0r53iVRCPpd7D9Lr3QKBgQDRyWIccS93kI2rmRxkg4nhniCP8uAacYHy
|
|
17
|
-
ZEZUFpm0p23rLKF7zlGd1xZmwKV5lnd1278qBt9YMvDGw6Mlt3WTQUh9XQ3b5q1V
|
|
18
|
-
xOHfyn2QvWfFbX/tWousq2RP3CJakGiKMefSx8aZB/Vwat3kSdgz825Vcr9oN2j1
|
|
19
|
-
lZZemRlllwKBgD+K1QXpN07PHxPGHxOkzQnmZfsPk6i8nO74sTe4dIkdb2LSc7sC
|
|
20
|
-
lmxqWsyz0bYl5Xae7q3bEGXHlQcM6gG6FGL/cs7TZh6COvCDFAdp6bU/2AWes9al
|
|
21
|
-
/zCf2ug+KllLXgk0wEio7IRgot9KUKzs5sATWQWduXZaqaemZ4VbOtlZAoGAK457
|
|
22
|
-
b1pUt6dssq8khP4Gl7CEJD8SJotgppoJPBZBLZABs21dzd3FdlcdEJJ5COijvvW0
|
|
23
|
-
GB67Ko3RDgO6kO8pWzyXt2elo/2eCYvKNfPN5EpfDE+y2u4Mx/oB5s7W1yTaf67i
|
|
24
|
-
6hGOe2F6pS+bCHYIjKjDRLyBXzr58NtxEA0Lh9sCgYEA6dI03OhJpruHxyOE3HYE
|
|
25
|
-
oSDah3wr8WUw5BsGDsrW2tMVV7Y3YRO02mL/DB17bmM1FzS5s0n8DKU0c2wViFm4
|
|
26
|
-
ZI4wZdm1hA1MBQBEFVQlVkmwtfyhmsnr98Huckd58PfMUZf6KJE9L4h7SXtPa/VL
|
|
27
|
-
492ASgwAEWWqFlfK6wSrHiM=
|
|
28
|
-
-----END PRIVATE KEY-----
|
package/localhost+2.pem
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
-----BEGIN CERTIFICATE-----
|
|
2
|
-
MIIEoTCCAwmgAwIBAgIRAKSu5ey0gTHNC82jEhdfdpYwDQYJKoZIhvcNAQELBQAw
|
|
3
|
-
gbExHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTFDMEEGA1UECww6bWFy
|
|
4
|
-
Y29ATWFyY29zLU1hY0Jvb2stUHJvLmxvY2FsIChNYXJjbyBDYXN0aWxsbyBEZWxs
|
|
5
|
-
YSBTZXJhKTFKMEgGA1UEAwxBbWtjZXJ0IG1hcmNvQE1hcmNvcy1NYWNCb29rLVBy
|
|
6
|
-
by5sb2NhbCAoTWFyY28gQ2FzdGlsbG8gRGVsbGEgU2VyYSkwHhcNMjUwMjAzMDE1
|
|
7
|
-
MjM5WhcNMjcwNTAzMDE1MjM5WjBuMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1l
|
|
8
|
-
bnQgY2VydGlmaWNhdGUxQzBBBgNVBAsMOm1hcmNvQE1hcmNvcy1NYWNCb29rLVBy
|
|
9
|
-
by5sb2NhbCAoTWFyY28gQ2FzdGlsbG8gRGVsbGEgU2VyYSkwggEiMA0GCSqGSIb3
|
|
10
|
-
DQEBAQUAA4IBDwAwggEKAoIBAQDNjfXRKBwXU0IHftJJGiR5w6Ccf2JfPZJgDdar
|
|
11
|
-
hVgJjL2cy9d4AwIq4ydiuU+H8MnguzdGrFaCgYOBGfgEI4dOAGr1AInRVVHRWKvw
|
|
12
|
-
GdLYm59dBRi5PQuU2EKVonQNsZy/qcOJSBzBSSAxPFCpTwrxj0j/Dug6LxIa1wB7
|
|
13
|
-
UenYn5NjkFSF8F5HufE7l4goIVh0/fVgRripCsnkiMQFVhMr+2glX+19zTVkBl9E
|
|
14
|
-
tAPbkXsegUUdnkOH2sd1TBN4HUibnktX+P1aIlhdp741Eni42jU92u33iZasvvnk
|
|
15
|
-
A94Q2MhBhzx3lgqAAcHcePzBI9yOEQbTqik4NkpvmoqqDFBbAgMBAAGjdjB0MA4G
|
|
16
|
-
A1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBTN
|
|
17
|
-
imcw8dRZ+LdM83iq2xnhRgHVMjAsBgNVHREEJTAjgglsb2NhbGhvc3SHBH8AAAGH
|
|
18
|
-
EAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggGBAIA2HiGT0dtdkNfB
|
|
19
|
-
LFAIUvlatqHkH4Pds3jd0TnT4muoebPoHc3HSoqnSdt4g2vN+75S47G7E5b/qQbH
|
|
20
|
-
ZfkL9kgZoCar79DuVpC/kCPI/NACRsqdgIcnd09FqN2Dmg1UP2F/nulLucWfOiSL
|
|
21
|
-
T3q3nVEAm3UL4R5VopFW94bp8jJ/cWseDohIGZBza4yUiMnbd6mK+g+DoExfGbwc
|
|
22
|
-
A3601xeDWcDxKv+KaBWQeyoUWO66FKYOJcdwaiPCPrvXVz3tdbxBpHB/beNeulaU
|
|
23
|
-
6EzDRCmtIwEZNwq9MNn0wViOjta1TtkeERtbvnmgPmFhhSbHDtGbbxuwWnV91aZg
|
|
24
|
-
0JFymKsmmBIv4Qu50zVPo7Jz0n6CucIPVUW8ZoamVBr6ljHOfZIg9N0hkRoX7DUT
|
|
25
|
-
pPV22KliCRZCHYnyVcIzwtW2lxlzydUnldhxZga6vlfoKR5WzCceTtsX2+p8dbJe
|
|
26
|
-
p5jVRsLyT/sfECb8n5N7BYRLSh36FekFGErFCr9Svqd9pRVsWg==
|
|
27
|
-
-----END CERTIFICATE-----
|
package/proto/workflow.proto
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
syntax = "proto3";
|
|
2
|
-
|
|
3
|
-
package blok.workflow.v1;
|
|
4
|
-
|
|
5
|
-
message WorkflowRequest {
|
|
6
|
-
string Name = 1;
|
|
7
|
-
string Message = 2;
|
|
8
|
-
string Encoding = 3;
|
|
9
|
-
string Type = 4;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
message WorkflowResponse {
|
|
13
|
-
string Message = 1;
|
|
14
|
-
string Encoding = 2;
|
|
15
|
-
string Type = 3;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
enum MessageEncoding {
|
|
19
|
-
BASE64 = 0;
|
|
20
|
-
STRING = 1;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
enum MessageType {
|
|
24
|
-
TEXT = 0;
|
|
25
|
-
JSON = 1;
|
|
26
|
-
XML = 2;
|
|
27
|
-
HTML = 3;
|
|
28
|
-
BINARY = 4;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
service WorkflowService {
|
|
32
|
-
rpc ExecuteWorkflow (WorkflowRequest) returns (WorkflowResponse) {}
|
|
33
|
-
}
|
package/src/GRpcTrigger.ts
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DefaultLogger,
|
|
3
|
-
type GlobalOptions,
|
|
4
|
-
NodeMap,
|
|
5
|
-
type ParamsDictionary,
|
|
6
|
-
TriggerBase,
|
|
7
|
-
type TriggerResponse,
|
|
8
|
-
} from "@blokjs/runner";
|
|
9
|
-
import { type Context, GlobalError } from "@blokjs/shared";
|
|
10
|
-
import type { ConnectRouter } from "@connectrpc/connect";
|
|
11
|
-
import { type Span, SpanStatusCode, metrics, trace } from "@opentelemetry/api";
|
|
12
|
-
import fastify from "fastify";
|
|
13
|
-
import { v4 as uuid } from "uuid";
|
|
14
|
-
import MessageDecode from "./MessageDecode";
|
|
15
|
-
import nodes from "./Nodes";
|
|
16
|
-
import workflows from "./Workflows";
|
|
17
|
-
import type RuntimeWorkflow from "./types/RuntimeWorkflow";
|
|
18
|
-
|
|
19
|
-
import { type Step, Workflow } from "@blokjs/helper";
|
|
20
|
-
import type { TriggerOpts } from "@blokjs/helper/dist/types/TriggerOpts";
|
|
21
|
-
import {
|
|
22
|
-
MessageEncoding,
|
|
23
|
-
MessageType,
|
|
24
|
-
type WorkflowRequest,
|
|
25
|
-
type WorkflowResponse,
|
|
26
|
-
WorkflowService,
|
|
27
|
-
} from "./gen/workflow_pb";
|
|
28
|
-
|
|
29
|
-
enum NodeTypes {
|
|
30
|
-
MODULE = "module",
|
|
31
|
-
LOCAL = "local",
|
|
32
|
-
PYTHON3 = "runtime.python3",
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export default class GRpcTrigger extends TriggerBase {
|
|
36
|
-
private server = fastify({
|
|
37
|
-
http2: true,
|
|
38
|
-
// https: {
|
|
39
|
-
// key: readFileSync("localhost+2-key.pem", "utf8"),
|
|
40
|
-
// cert: readFileSync("localhost+2.pem", "utf8"),
|
|
41
|
-
// }
|
|
42
|
-
});
|
|
43
|
-
private nodeMap: GlobalOptions = <GlobalOptions>{};
|
|
44
|
-
protected tracer = trace.getTracer(
|
|
45
|
-
process.env.PROJECT_NAME || "trigger-grpc-workflow",
|
|
46
|
-
process.env.PROJECT_VERSION || "0.0.1",
|
|
47
|
-
);
|
|
48
|
-
private logger = new DefaultLogger();
|
|
49
|
-
|
|
50
|
-
constructor() {
|
|
51
|
-
super();
|
|
52
|
-
this.loadNodes();
|
|
53
|
-
this.loadWorkflows();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
getApp() {
|
|
57
|
-
return this.server;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async listen(): Promise<number> {
|
|
61
|
-
return 0;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
loadNodes() {
|
|
65
|
-
this.nodeMap.nodes = new NodeMap();
|
|
66
|
-
const nodeKeys = Object.keys(nodes);
|
|
67
|
-
for (const key of nodeKeys) {
|
|
68
|
-
this.nodeMap.nodes.addNode(key, nodes[key]);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
loadWorkflows() {
|
|
73
|
-
this.nodeMap.workflows = workflows;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
processRequest(router: ConnectRouter, trigger: GRpcTrigger) {
|
|
77
|
-
router.service(WorkflowService, {
|
|
78
|
-
executeWorkflow: (request: WorkflowRequest) => trigger.executeWorkflow(request),
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async executeWorkflow(request: WorkflowRequest) {
|
|
83
|
-
const start = performance.now();
|
|
84
|
-
const coder = new MessageDecode();
|
|
85
|
-
let name = request.Name;
|
|
86
|
-
const messageContext: Context = coder.requestDecode(request);
|
|
87
|
-
const runtimeWorkflow = messageContext as unknown as RuntimeWorkflow;
|
|
88
|
-
const id: string = (messageContext.request.query?.requestId as string) || (uuid() as string);
|
|
89
|
-
let remoteNodeExecution = false;
|
|
90
|
-
|
|
91
|
-
const defaultMeter = metrics.getMeter("default");
|
|
92
|
-
const workflow_runner_errors = defaultMeter.createCounter("workflow_errors", {
|
|
93
|
-
description: "Workflow runner errors",
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
return await this.tracer.startActiveSpan(`${name}`, async (span: Span) => {
|
|
97
|
-
try {
|
|
98
|
-
if (runtimeWorkflow !== undefined) {
|
|
99
|
-
const workflowModel = runtimeWorkflow.workflow;
|
|
100
|
-
const node_type = (workflowModel.steps[0] as unknown as ParamsDictionary).type;
|
|
101
|
-
let set_node_type: NodeTypes = NodeTypes.MODULE;
|
|
102
|
-
switch (node_type) {
|
|
103
|
-
case "runtime.python3":
|
|
104
|
-
set_node_type = NodeTypes.PYTHON3;
|
|
105
|
-
break;
|
|
106
|
-
case "local":
|
|
107
|
-
set_node_type = NodeTypes.LOCAL;
|
|
108
|
-
break;
|
|
109
|
-
default:
|
|
110
|
-
set_node_type = NodeTypes.MODULE;
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const trigger = Object.keys(workflowModel.trigger)[0];
|
|
115
|
-
const trigger_config =
|
|
116
|
-
((workflowModel.trigger as unknown as ParamsDictionary)[trigger] as unknown as TriggerOpts) || {};
|
|
117
|
-
|
|
118
|
-
const step: Step = Workflow({
|
|
119
|
-
name: `Remote Node: ${name}`,
|
|
120
|
-
version: "1.0.0",
|
|
121
|
-
description: "Remote Node",
|
|
122
|
-
})
|
|
123
|
-
.addTrigger((trigger as unknown as "http") || "grpc", trigger_config)
|
|
124
|
-
.addStep({
|
|
125
|
-
name: "node",
|
|
126
|
-
node: name,
|
|
127
|
-
type: set_node_type,
|
|
128
|
-
inputs: ((workflowModel.nodes as unknown as ParamsDictionary).node as unknown as ParamsDictionary).inputs,
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
this.nodeMap.workflows[id] = step;
|
|
132
|
-
name = id;
|
|
133
|
-
remoteNodeExecution = true;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
await this.configuration.init(name, this.nodeMap);
|
|
137
|
-
let ctx: Context = this.createContext(undefined, name, id);
|
|
138
|
-
ctx.request = messageContext.request;
|
|
139
|
-
ctx.logger.log(`Workflow: ${name}, Version: ${this.configuration.version}`);
|
|
140
|
-
|
|
141
|
-
const response: TriggerResponse = await this.run(ctx);
|
|
142
|
-
ctx = response.ctx;
|
|
143
|
-
const average = response.metrics;
|
|
144
|
-
|
|
145
|
-
// Support both module nodes (wrapped BlokResponse with .data/.contentType)
|
|
146
|
-
// and runtime adapter nodes (raw data without wrapper)
|
|
147
|
-
const hasWrapper =
|
|
148
|
-
ctx.response && typeof ctx.response === "object" && "data" in ctx.response && "contentType" in ctx.response;
|
|
149
|
-
if (!hasWrapper) {
|
|
150
|
-
// Runtime adapter node: ctx.response is raw data, wrap it
|
|
151
|
-
ctx.response = {
|
|
152
|
-
data: ctx.response,
|
|
153
|
-
contentType: "application/json",
|
|
154
|
-
success: true,
|
|
155
|
-
error: null,
|
|
156
|
-
} as typeof ctx.response;
|
|
157
|
-
}
|
|
158
|
-
if (ctx.response.contentType === undefined || ctx.response.contentType === "")
|
|
159
|
-
ctx.response.contentType = "application/json";
|
|
160
|
-
|
|
161
|
-
const end = performance.now();
|
|
162
|
-
ctx.logger.log(`Completed in ${(end - start).toFixed(2)}ms`);
|
|
163
|
-
|
|
164
|
-
span.setAttribute("success", true);
|
|
165
|
-
span.setAttribute("Content-Type", ctx.response.contentType as string);
|
|
166
|
-
span.setAttribute("workflow_request_id", `${ctx.id}`);
|
|
167
|
-
span.setAttribute("workflow_elapsed_time", `${end - start}`);
|
|
168
|
-
span.setAttribute("workflow_version", `${this.configuration.version}`);
|
|
169
|
-
span.setAttribute("workflow_name", `${this.configuration.name}`);
|
|
170
|
-
span.setAttribute("workflow_memory_avg_mb", `${average.memory.total}`);
|
|
171
|
-
span.setAttribute("workflow_memory_min_mb", `${average.memory.min}`);
|
|
172
|
-
span.setAttribute("workflow_memory_max_mb", `${average.memory.max}`);
|
|
173
|
-
span.setAttribute("workflow_cpu_percentage", `${average.cpu.average}`);
|
|
174
|
-
span.setAttribute("workflow_cpu_total", `${average.cpu.total}`);
|
|
175
|
-
span.setAttribute("workflow_cpu_usage", `${average.cpu.usage}`);
|
|
176
|
-
span.setAttribute("workflow_cpu_model", `${average.cpu.model}`);
|
|
177
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
178
|
-
|
|
179
|
-
return coder.responseEncode(ctx, request.Encoding, request.Type);
|
|
180
|
-
} catch (e: unknown) {
|
|
181
|
-
span.setAttribute("success", false);
|
|
182
|
-
span.setAttribute("workflow_request_id", `${id}`);
|
|
183
|
-
span.recordException(e as Error);
|
|
184
|
-
let message: WorkflowResponse = <WorkflowResponse>{};
|
|
185
|
-
const base64Key = MessageEncoding[MessageEncoding.BASE64];
|
|
186
|
-
const textKey = MessageType[MessageType.TEXT];
|
|
187
|
-
const jsonKey = MessageType[MessageType.JSON];
|
|
188
|
-
|
|
189
|
-
if (e instanceof GlobalError) {
|
|
190
|
-
const error_context = e as GlobalError;
|
|
191
|
-
|
|
192
|
-
if (error_context.context.message === "{}" && error_context.context.json instanceof DOMException) {
|
|
193
|
-
workflow_runner_errors.add(1, {
|
|
194
|
-
env: process.env.NODE_ENV,
|
|
195
|
-
workflow_version: `${this.configuration.version || "unknown"}`,
|
|
196
|
-
workflow_name: `${name || this.configuration.name}`,
|
|
197
|
-
});
|
|
198
|
-
span.setStatus({
|
|
199
|
-
code: SpanStatusCode.ERROR,
|
|
200
|
-
message: (error_context.context.json as Error).toString(),
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
this.logger.error(`${(error_context.context.json as Error).toString()}`);
|
|
204
|
-
message = {
|
|
205
|
-
Message: coder.responseErrorEncode((error_context.context.json as Error).toString(), base64Key, textKey),
|
|
206
|
-
Encoding: base64Key,
|
|
207
|
-
Type: textKey,
|
|
208
|
-
} as WorkflowResponse;
|
|
209
|
-
} else {
|
|
210
|
-
if (error_context.context.code === undefined) error_context.setCode(500);
|
|
211
|
-
|
|
212
|
-
if (error_context.hasJson()) {
|
|
213
|
-
workflow_runner_errors.add(1, {
|
|
214
|
-
env: process.env.NODE_ENV,
|
|
215
|
-
workflow_version: `${this.configuration.version || "unknown"}`,
|
|
216
|
-
workflow_name: `${name || this.configuration.name}`,
|
|
217
|
-
});
|
|
218
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: JSON.stringify(error_context.context.json) });
|
|
219
|
-
this.logger.error(`${JSON.stringify(error_context.context.json)}`);
|
|
220
|
-
message = {
|
|
221
|
-
Message: coder.responseErrorEncode(JSON.stringify(error_context.context.json), base64Key, textKey),
|
|
222
|
-
Encoding: base64Key,
|
|
223
|
-
Type: jsonKey,
|
|
224
|
-
} as WorkflowResponse;
|
|
225
|
-
} else {
|
|
226
|
-
workflow_runner_errors.add(1, {
|
|
227
|
-
env: process.env.NODE_ENV,
|
|
228
|
-
workflow_version: `${this.configuration.version || "unknown"}`,
|
|
229
|
-
workflow_name: `${name || this.configuration.name}`,
|
|
230
|
-
});
|
|
231
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: error_context.message });
|
|
232
|
-
this.logger.error(`${error_context.message}`, error_context.stack?.replace(/\n/g, " "));
|
|
233
|
-
message = {
|
|
234
|
-
Message: coder.responseErrorEncode(error_context.message, base64Key, textKey),
|
|
235
|
-
Encoding: MessageEncoding[MessageEncoding.BASE64],
|
|
236
|
-
Type: textKey,
|
|
237
|
-
} as WorkflowResponse;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
} else {
|
|
241
|
-
workflow_runner_errors.add(1, {
|
|
242
|
-
env: process.env.NODE_ENV,
|
|
243
|
-
workflow_version: `${this.configuration.version || "unknown"}`,
|
|
244
|
-
workflow_name: `${name || this.configuration.name}`,
|
|
245
|
-
});
|
|
246
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: (e as Error).message });
|
|
247
|
-
this.logger.error(`${(e as Error).message}`, `${(e as Error).stack?.replace(/\n/g, " ")}`);
|
|
248
|
-
|
|
249
|
-
message = {
|
|
250
|
-
Message: coder.responseErrorEncode((e as Error).message, base64Key, textKey),
|
|
251
|
-
Encoding: base64Key,
|
|
252
|
-
Type: textKey,
|
|
253
|
-
} as WorkflowResponse;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return message;
|
|
257
|
-
} finally {
|
|
258
|
-
if (remoteNodeExecution) {
|
|
259
|
-
delete this.nodeMap.workflows[id];
|
|
260
|
-
}
|
|
261
|
-
span.end();
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
}
|
package/src/GrpcClient.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { type Transport, createClient } from "@connectrpc/connect";
|
|
2
|
-
import { createGrpcTransport } from "@connectrpc/connect-node";
|
|
3
|
-
import { createConnectTransport } from "@connectrpc/connect-node";
|
|
4
|
-
import { createGrpcWebTransport } from "@connectrpc/connect-node";
|
|
5
|
-
import { type WorkflowRequest, type WorkflowResponse, WorkflowService } from "./gen/workflow_pb";
|
|
6
|
-
|
|
7
|
-
export type RpcOptions = {
|
|
8
|
-
host: string;
|
|
9
|
-
port: number;
|
|
10
|
-
protocol: string;
|
|
11
|
-
httpVersion: HttpVersionEnum;
|
|
12
|
-
transport: TransportEnum;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type CallOptions = {
|
|
16
|
-
headers: Record<string, string>;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export enum TransportEnum {
|
|
20
|
-
GRPC = "grpc",
|
|
21
|
-
GRPC_WEB = "grpc-web",
|
|
22
|
-
CONNECT = "connect",
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export enum HttpVersionEnum {
|
|
26
|
-
HTTP1 = "1.1",
|
|
27
|
-
HTTP2 = "2",
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export type { WorkflowRequest, WorkflowResponse };
|
|
31
|
-
|
|
32
|
-
export default class GrpcClient {
|
|
33
|
-
protected opts: RpcOptions;
|
|
34
|
-
|
|
35
|
-
constructor(options: RpcOptions) {
|
|
36
|
-
this.opts = options;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async call(message: WorkflowRequest, opts?: CallOptions): Promise<WorkflowResponse> {
|
|
40
|
-
const transport = this.transport();
|
|
41
|
-
const client = createClient(WorkflowService, transport);
|
|
42
|
-
return await client.executeWorkflow(message, opts);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
transport(): Transport {
|
|
46
|
-
switch (this.opts.transport) {
|
|
47
|
-
case TransportEnum.GRPC:
|
|
48
|
-
return createGrpcTransport({
|
|
49
|
-
baseUrl: `${this.opts.protocol}://${this.opts.host}:${this.opts.port}/`,
|
|
50
|
-
interceptors: [],
|
|
51
|
-
});
|
|
52
|
-
case TransportEnum.GRPC_WEB:
|
|
53
|
-
return createGrpcWebTransport({
|
|
54
|
-
baseUrl: `${this.opts.protocol}://${this.opts.host}:${this.opts.port}/`,
|
|
55
|
-
httpVersion: this.opts.httpVersion,
|
|
56
|
-
interceptors: [],
|
|
57
|
-
});
|
|
58
|
-
case TransportEnum.CONNECT:
|
|
59
|
-
return createConnectTransport({
|
|
60
|
-
baseUrl: `${this.opts.protocol}://${this.opts.host}:${this.opts.port}/`,
|
|
61
|
-
httpVersion: this.opts.httpVersion,
|
|
62
|
-
interceptors: [],
|
|
63
|
-
});
|
|
64
|
-
default:
|
|
65
|
-
throw new Error("Invalid transport type");
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// let client = new GrpcClient({
|
|
71
|
-
// host: "localhost",
|
|
72
|
-
// port: 8433,
|
|
73
|
-
// protocol: "http",
|
|
74
|
-
// httpVersion: HttpVersionEnum.HTTP2,
|
|
75
|
-
// transport: TransportEnum.GRPC,
|
|
76
|
-
// });
|
|
77
|
-
|
|
78
|
-
// client.call({
|
|
79
|
-
// "Name": "countries",
|
|
80
|
-
// "Message": "ewogICAgInJlcXVlc3QiOiB7CiAgICAgICAgImJvZHkiOiB7CiAgICAgICAgICAgICJjb3VudHJ5X25hbWUiOiAiQ2FuYWRhIgogICAgICAgIH0KICAgIH0KfQ==",
|
|
81
|
-
// "Encoding": "BASE64",
|
|
82
|
-
// "Type": "JSON"
|
|
83
|
-
// } as WorkflowRequest).then((response) => {
|
|
84
|
-
// console.log(response);
|
|
85
|
-
// }).catch((error) => {
|
|
86
|
-
// console.error(error);
|
|
87
|
-
// });
|