allserver 2.1.0 → 2.2.0-1
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/README.md +69 -8
- package/package.json +5 -3
- package/src/client/AllserverClient.js +1 -0
- package/src/client/LambdaClientTransport.js +54 -0
- package/src/index.js +3 -0
- package/src/server/LambdaTransport.js +44 -47
package/README.md
CHANGED
|
@@ -176,7 +176,10 @@ npm i allserver
|
|
|
176
176
|
|
|
177
177
|
#### Client
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
Two ways:
|
|
180
|
+
|
|
181
|
+
- Same as the HTTP protocol client above.
|
|
182
|
+
- Direct invocation via AWS SDK or AWS CLI.
|
|
180
183
|
|
|
181
184
|
### gRPC protocol
|
|
182
185
|
|
|
@@ -369,15 +372,16 @@ exports.handler = Allserver({
|
|
|
369
372
|
}).start();
|
|
370
373
|
```
|
|
371
374
|
|
|
372
|
-
|
|
375
|
+
### Server in Bare AWS Lambda
|
|
376
|
+
|
|
377
|
+
Invoke directly via AWS SDK or AWS CLI. But better use the LambdaClientTransport (aka `"lambda://"` scheme) below.
|
|
373
378
|
|
|
374
379
|
```js
|
|
375
380
|
const { Allserver, LambdaTransport } = require("allserver");
|
|
376
381
|
|
|
377
|
-
|
|
378
|
-
exports = Allserver({
|
|
382
|
+
exports.handler = Allserver({
|
|
379
383
|
procedures,
|
|
380
|
-
transport: LambdaTransport(
|
|
384
|
+
transport: LambdaTransport(),
|
|
381
385
|
}).start();
|
|
382
386
|
```
|
|
383
387
|
|
|
@@ -596,19 +600,76 @@ Sometimes you need to unit test your procedures via the `AllserverClient`. For t
|
|
|
596
600
|
const { Allserver, MemoryTransport } = require("allserver");
|
|
597
601
|
|
|
598
602
|
const memoryServer = Allserver({
|
|
599
|
-
|
|
600
|
-
|
|
603
|
+
procedures,
|
|
604
|
+
transport: MemoryTransport(),
|
|
601
605
|
});
|
|
602
606
|
|
|
603
607
|
const client = memoryServer.start();
|
|
604
608
|
|
|
605
609
|
const { success, code, message, user } = await client.updateUser({
|
|
610
|
+
id: "123412341234123412341234",
|
|
611
|
+
firstName: "Fred",
|
|
612
|
+
lastName: "Flinstone",
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
assert(success === true);
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Bare AWS Lambda invocation
|
|
619
|
+
|
|
620
|
+
First you need to install any of the AWS SDK versions.
|
|
621
|
+
|
|
622
|
+
```shell
|
|
623
|
+
npm i allserver aws-sdk
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
or
|
|
627
|
+
|
|
628
|
+
```shell
|
|
629
|
+
npm i allserver @aws-sdk/client-lambda
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
The invoke the lambda this way:
|
|
633
|
+
|
|
634
|
+
```js
|
|
635
|
+
const { AllserverClient } = require("allserver");
|
|
636
|
+
// or
|
|
637
|
+
const AllserverClient = require("allserver/Client");
|
|
638
|
+
|
|
639
|
+
const client = AllserverClient({ uri: "lambda://my-lambda-name" });
|
|
640
|
+
|
|
641
|
+
const { success, code, message, user } = await client.updateUser({
|
|
642
|
+
id: "123412341234123412341234",
|
|
643
|
+
firstName: "Fred",
|
|
644
|
+
lastName: "Flinstone",
|
|
645
|
+
});
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
#### Using AWS SDK
|
|
649
|
+
|
|
650
|
+
As usual, the client side does not require the Allserver packages at all.
|
|
651
|
+
|
|
652
|
+
```js
|
|
653
|
+
import { Lambda } from "@aws-sdk/client-lambda";
|
|
654
|
+
|
|
655
|
+
const invocationResponse = await new Lambda().invoke({
|
|
656
|
+
FunctionName: "my-lambda-name",
|
|
657
|
+
Payload: JSON.stringify({
|
|
606
658
|
id: "123412341234123412341234",
|
|
607
659
|
firstName: "Fred",
|
|
608
660
|
lastName: "Flinstone",
|
|
661
|
+
}),
|
|
662
|
+
ClientContext: JSON.stringify({
|
|
663
|
+
procedureName: "updateUser",
|
|
664
|
+
}),
|
|
609
665
|
});
|
|
666
|
+
const { success, code, message, user } = JSON.parse(invocationResponse.Payload);
|
|
667
|
+
```
|
|
610
668
|
|
|
611
|
-
|
|
669
|
+
Alternatively, you can call the same procedure using the `aws` CLI:
|
|
670
|
+
|
|
671
|
+
```shell
|
|
672
|
+
aws lambda invoke --function-name my-lambda-name --client-context '{"procedureName":"updateUser"}' --payload '{"id":"123412341234123412341234","firstName":"Fred","lastName":"Flinstone"}'
|
|
612
673
|
```
|
|
613
674
|
|
|
614
675
|
## `AllserverClient` options
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "allserver",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0-1",
|
|
4
4
|
"description": "Multi-protocol simple RPC server and [optional] client. Boilerplate-less. Opinionated. Minimalistic. DX-first.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
"cov": "nyc --reporter=html npm run test"
|
|
10
10
|
},
|
|
11
11
|
"keywords": [
|
|
12
|
+
"simple",
|
|
13
|
+
"rpc",
|
|
12
14
|
"http",
|
|
13
15
|
"grpc",
|
|
14
16
|
"websocket",
|
|
@@ -63,7 +65,7 @@
|
|
|
63
65
|
"@grpc/grpc-js": "^1.1.7",
|
|
64
66
|
"@grpc/proto-loader": "^0.7.5",
|
|
65
67
|
"bullmq": "^3.10.1",
|
|
66
|
-
"eslint": "^
|
|
68
|
+
"eslint": "^8.55.0",
|
|
67
69
|
"express": "^4.18.2",
|
|
68
70
|
"lambda-local": "^1.7.3",
|
|
69
71
|
"micro": "^10.0.1",
|
|
@@ -77,7 +79,7 @@
|
|
|
77
79
|
},
|
|
78
80
|
"eslintConfig": {
|
|
79
81
|
"parserOptions": {
|
|
80
|
-
"ecmaVersion":
|
|
82
|
+
"ecmaVersion": 2022
|
|
81
83
|
},
|
|
82
84
|
"env": {
|
|
83
85
|
"es6": true,
|
|
@@ -165,6 +165,7 @@ module.exports = require("stampit")({
|
|
|
165
165
|
https() { return require("./HttpClientTransport"); },
|
|
166
166
|
grpc() { return require("./GrpcClientTransport"); },
|
|
167
167
|
bullmq() { return require("./BullmqClientTransport"); },
|
|
168
|
+
lambda() { return require("./LambdaClientTransport"); },
|
|
168
169
|
},
|
|
169
170
|
},
|
|
170
171
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module.exports = require("./ClientTransport").compose({
|
|
2
|
+
name: "LambdaClientTransport",
|
|
3
|
+
|
|
4
|
+
props: {
|
|
5
|
+
awsSdkLambdaClient: null,
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
init() {
|
|
9
|
+
if (!this.awsSdkLambdaClient) {
|
|
10
|
+
let Lambda;
|
|
11
|
+
try {
|
|
12
|
+
Lambda = require("aws-sdk").Lambda; // AWS SDK v2 adoption
|
|
13
|
+
} catch {
|
|
14
|
+
Lambda = require("@aws-sdk/client-lambda").Lambda;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.awsSdkLambdaClient = new Lambda();
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
methods: {
|
|
22
|
+
async introspect(ctx) {
|
|
23
|
+
ctx.procedureName = "";
|
|
24
|
+
const result = await this.call(ctx);
|
|
25
|
+
// The server-side Transport will not have the call result if introspection is not available on the server side,
|
|
26
|
+
// but the server itself is up and running processing calls.
|
|
27
|
+
if (!result.procedures) throw Error("The lambda introspection call returned nothing"); // The ClientTransport expects us to throw if call fails
|
|
28
|
+
return result;
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
async call({ procedureName, lambda }) {
|
|
32
|
+
let promise = this.awsSdkLambdaClient.invoke({
|
|
33
|
+
FunctionName: this.uri.substring("lambda://".length),
|
|
34
|
+
Payload: JSON.stringify({
|
|
35
|
+
callContext: { ...lambda.callContext, procedureName },
|
|
36
|
+
callArg: lambda.callArg,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
if (typeof promise.promise === "function") promise = promise.promise(); // AWS SDK v2 adoption
|
|
40
|
+
const invocationResponse = await promise;
|
|
41
|
+
return JSON.parse(invocationResponse.Payload);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
createCallContext(defaultCtx) {
|
|
45
|
+
return {
|
|
46
|
+
...defaultCtx,
|
|
47
|
+
lambda: {
|
|
48
|
+
callContext: {},
|
|
49
|
+
callArg: defaultCtx.arg,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
package/src/index.js
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
module.exports = require("./Transport").compose({
|
|
2
2
|
name: "LambdaTransport",
|
|
3
3
|
|
|
4
|
-
props: {
|
|
5
|
-
_mapProceduresToExports: false,
|
|
6
|
-
},
|
|
7
|
-
|
|
8
|
-
init({ mapProceduresToExports }) {
|
|
9
|
-
this._mapProceduresToExports = mapProceduresToExports || this._mapProceduresToExports;
|
|
10
|
-
},
|
|
11
|
-
|
|
12
4
|
methods: {
|
|
13
|
-
async
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
5
|
+
async deserializeEvent(ctx) {
|
|
6
|
+
if (ctx.lambda.isHttp) {
|
|
7
|
+
const { body, query } = ctx.lambda.http;
|
|
8
|
+
try {
|
|
9
|
+
// If there is no body we will use request query (aka search params)
|
|
10
|
+
if (!body) {
|
|
11
|
+
ctx.arg = query || {};
|
|
12
|
+
} else {
|
|
13
|
+
if ((typeof body === "string" && body[0] === "{") || Buffer.isBuffer(body)) {
|
|
14
|
+
ctx.arg = JSON.parse(body);
|
|
15
|
+
} else {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
} catch (err) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
ctx.arg = ctx.lambda.invoke.callArg || {};
|
|
20
25
|
return true;
|
|
21
|
-
} catch (err) {
|
|
22
|
-
return false;
|
|
23
26
|
}
|
|
24
27
|
},
|
|
25
28
|
|
|
26
29
|
async _handleRequest(ctx) {
|
|
27
|
-
if (await this.
|
|
30
|
+
if (await this.deserializeEvent(ctx)) {
|
|
28
31
|
await ctx.allserver.handleCall(ctx);
|
|
29
32
|
} else {
|
|
30
33
|
// HTTP protocol request was malformed (not expected structure).
|
|
@@ -36,37 +39,27 @@ module.exports = require("./Transport").compose({
|
|
|
36
39
|
},
|
|
37
40
|
|
|
38
41
|
startServer(defaultCtx) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
exports[procedureName] = async (event) =>
|
|
43
|
-
new Promise((resolve) => {
|
|
44
|
-
const path = "/" + procedureName;
|
|
45
|
-
const ctx = {
|
|
46
|
-
...defaultCtx,
|
|
47
|
-
lambda: { event, resolve, path, query: { ...(event.queryStringParameters || {}) } },
|
|
48
|
-
};
|
|
42
|
+
return async (event, context) => {
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
const lambda = { event, context, resolve };
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
return exports;
|
|
54
|
-
}
|
|
46
|
+
const path = (event && (event.path || event.requestContext?.http?.path)) || undefined;
|
|
47
|
+
lambda.isHttp = Boolean(path);
|
|
55
48
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
};
|
|
49
|
+
if (lambda.isHttp) {
|
|
50
|
+
lambda.http = { path, query: event?.queryStringParameters, body: event?.body };
|
|
51
|
+
} else {
|
|
52
|
+
lambda.invoke = { callContext: event?.callContext, callArg: event?.callArg };
|
|
53
|
+
}
|
|
62
54
|
|
|
63
|
-
this._handleRequest(
|
|
55
|
+
this._handleRequest({ ...defaultCtx, lambda });
|
|
64
56
|
});
|
|
65
57
|
};
|
|
66
58
|
},
|
|
67
59
|
|
|
68
60
|
getProcedureName(ctx) {
|
|
69
|
-
|
|
61
|
+
const { isHttp, http, invoke } = ctx.lambda;
|
|
62
|
+
return isHttp ? http.path.substr(1) : invoke.callContext?.procedureName;
|
|
70
63
|
},
|
|
71
64
|
|
|
72
65
|
isIntrospection(ctx) {
|
|
@@ -85,12 +78,16 @@ module.exports = require("./Transport").compose({
|
|
|
85
78
|
},
|
|
86
79
|
|
|
87
80
|
reply(ctx) {
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
81
|
+
if (ctx.lambda.isHttp) {
|
|
82
|
+
if (!ctx.lambda.statusCode) ctx.lambda.statusCode = 200;
|
|
83
|
+
ctx.lambda.resolve({
|
|
84
|
+
statusCode: ctx.lambda.statusCode,
|
|
85
|
+
headers: { "content-type": "application/json" },
|
|
86
|
+
body: JSON.stringify(ctx.result),
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
ctx.lambda.resolve(ctx.result);
|
|
90
|
+
}
|
|
94
91
|
},
|
|
95
92
|
},
|
|
96
93
|
});
|