allserver 2.0.1 → 2.2.0-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/README.md +89 -5
- package/package.json +5 -3
- package/src/client/AllserverClient.js +1 -0
- package/src/client/LambdaClientTransport.js +58 -0
- package/src/client/MemoryClientTransport.js +29 -0
- package/src/index.js +9 -0
- package/src/server/LambdaTransport.js +33 -49
- package/src/server/MemoryTransport.js +28 -0
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
|
|
|
@@ -588,6 +592,86 @@ const data = await job.waitUntilFinished(queueEvents, 30_000);
|
|
|
588
592
|
const { success, code, message, user } = data;
|
|
589
593
|
```
|
|
590
594
|
|
|
595
|
+
### In memory
|
|
596
|
+
|
|
597
|
+
Sometimes you need to unit test your procedures via the `AllserverClient`. For that we have `MemoryTransport`.
|
|
598
|
+
|
|
599
|
+
```js
|
|
600
|
+
const { Allserver, MemoryTransport } = require("allserver");
|
|
601
|
+
|
|
602
|
+
const memoryServer = Allserver({
|
|
603
|
+
procedures,
|
|
604
|
+
transport: MemoryTransport(),
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
const client = memoryServer.start();
|
|
608
|
+
|
|
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({
|
|
658
|
+
id: "123412341234123412341234",
|
|
659
|
+
firstName: "Fred",
|
|
660
|
+
lastName: "Flinstone",
|
|
661
|
+
}),
|
|
662
|
+
ClientContext: JSON.stringify({
|
|
663
|
+
procedureName: "updateUser",
|
|
664
|
+
}),
|
|
665
|
+
});
|
|
666
|
+
const { success, code, message, user } = JSON.parse(invocationResponse.Payload);
|
|
667
|
+
```
|
|
668
|
+
|
|
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"}'
|
|
673
|
+
```
|
|
674
|
+
|
|
591
675
|
## `AllserverClient` options
|
|
592
676
|
|
|
593
677
|
**All the arguments are optional.** But either `uri` or `transport` must be provided. We are trying to keep the highest possible DX here.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "allserver",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0-0",
|
|
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,58 @@
|
|
|
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
|
+
// InvocationType: "RequestResponse",
|
|
35
|
+
Payload: lambda.payload && JSON.stringify(lambda.payload),
|
|
36
|
+
ClientContext: Buffer.from(
|
|
37
|
+
JSON.stringify({
|
|
38
|
+
...lambda.clientContext,
|
|
39
|
+
procedureName,
|
|
40
|
+
})
|
|
41
|
+
).toString("base64"),
|
|
42
|
+
});
|
|
43
|
+
if (typeof promise.promise === "function") promise = promise.promise(); // AWS SDK v2 adoption
|
|
44
|
+
const invocationResponse = await promise;
|
|
45
|
+
return JSON.parse(invocationResponse.Payload);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
createCallContext(defaultCtx) {
|
|
49
|
+
return {
|
|
50
|
+
...defaultCtx,
|
|
51
|
+
lambda: {
|
|
52
|
+
clientContext: {},
|
|
53
|
+
payload: defaultCtx.arg,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module.exports = require("./ClientTransport").compose({
|
|
2
|
+
name: "MemoryClientTransport",
|
|
3
|
+
|
|
4
|
+
props: {
|
|
5
|
+
_allserverContext: null,
|
|
6
|
+
uri: "memory",
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
init({ allserverContext }) {
|
|
10
|
+
this._allserverContext = allserverContext || this._allserverContext;
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
methods: {
|
|
14
|
+
async introspect(ctx) {
|
|
15
|
+
ctx.procedureName = "";
|
|
16
|
+
const result = await this.call(ctx);
|
|
17
|
+
if (!result) throw Error(); // The ClientTransport expects us to throw if call fails
|
|
18
|
+
return result;
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async call(ctx) {
|
|
22
|
+
return this._allserverContext.allserver.handleCall(ctx);
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
createCallContext(defaultCtx) {
|
|
26
|
+
return { ...defaultCtx, ...this._allserverContext, memory: {} };
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
package/src/index.js
CHANGED
|
@@ -23,6 +23,9 @@ module.exports = {
|
|
|
23
23
|
get BullmqTransport() {
|
|
24
24
|
return require("./server/BullmqTransport");
|
|
25
25
|
},
|
|
26
|
+
get MemoryTransport() {
|
|
27
|
+
return require("./server/MemoryTransport");
|
|
28
|
+
},
|
|
26
29
|
|
|
27
30
|
// client
|
|
28
31
|
|
|
@@ -41,4 +44,10 @@ module.exports = {
|
|
|
41
44
|
get BullmqClientTransport() {
|
|
42
45
|
return require("./client/BullmqClientTransport");
|
|
43
46
|
},
|
|
47
|
+
get MemoryClientTransport() {
|
|
48
|
+
return require("./client/MemoryClientTransport");
|
|
49
|
+
},
|
|
50
|
+
get LambdaClientTransport() {
|
|
51
|
+
return require("./client/LambdaClientTransport");
|
|
52
|
+
},
|
|
44
53
|
};
|
|
@@ -1,30 +1,26 @@
|
|
|
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 = ctx.lambda.event.body;
|
|
8
|
+
let query = ctx.lambda.query;
|
|
9
|
+
try {
|
|
10
|
+
// If there is no body we will use request query (aka search params)
|
|
11
|
+
ctx.arg = body ? JSON.parse(body) : query;
|
|
12
|
+
return true;
|
|
13
|
+
} catch (err) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
} else {
|
|
17
|
+
ctx.arg = ctx.lambda.event || {};
|
|
20
18
|
return true;
|
|
21
|
-
} catch (err) {
|
|
22
|
-
return false;
|
|
23
19
|
}
|
|
24
20
|
},
|
|
25
21
|
|
|
26
22
|
async _handleRequest(ctx) {
|
|
27
|
-
if (await this.
|
|
23
|
+
if (await this.deserializeEvent(ctx)) {
|
|
28
24
|
await ctx.allserver.handleCall(ctx);
|
|
29
25
|
} else {
|
|
30
26
|
// HTTP protocol request was malformed (not expected structure).
|
|
@@ -36,37 +32,21 @@ module.exports = require("./Transport").compose({
|
|
|
36
32
|
},
|
|
37
33
|
|
|
38
34
|
startServer(defaultCtx) {
|
|
39
|
-
|
|
40
|
-
const exports = {};
|
|
41
|
-
for (const procedureName of Object.keys(defaultCtx.allserver.procedures)) {
|
|
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
|
-
};
|
|
49
|
-
|
|
50
|
-
this._handleRequest(ctx);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
return exports;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return async (event) => {
|
|
35
|
+
return async (event, context) => {
|
|
57
36
|
return new Promise((resolve) => {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
};
|
|
37
|
+
const path = (event && (event.path || event.requestContext?.http?.path)) || undefined;
|
|
38
|
+
const isHttp = Boolean(path);
|
|
39
|
+
const procedureName = isHttp ? path.substr(1) : context.clientContext?.procedureName;
|
|
40
|
+
const query = { ...(event?.queryStringParameters || {}) };
|
|
41
|
+
const lambda = { resolve, isHttp, event, context, path, query, procedureName };
|
|
62
42
|
|
|
63
|
-
this._handleRequest(
|
|
43
|
+
this._handleRequest({ ...defaultCtx, lambda });
|
|
64
44
|
});
|
|
65
45
|
};
|
|
66
46
|
},
|
|
67
47
|
|
|
68
|
-
getProcedureName(
|
|
69
|
-
return
|
|
48
|
+
getProcedureName({ lambda: { procedureName } }) {
|
|
49
|
+
return procedureName;
|
|
70
50
|
},
|
|
71
51
|
|
|
72
52
|
isIntrospection(ctx) {
|
|
@@ -85,12 +65,16 @@ module.exports = require("./Transport").compose({
|
|
|
85
65
|
},
|
|
86
66
|
|
|
87
67
|
reply(ctx) {
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
68
|
+
if (ctx.lambda.isHttp) {
|
|
69
|
+
if (!ctx.lambda.statusCode) ctx.lambda.statusCode = 200;
|
|
70
|
+
ctx.lambda.resolve({
|
|
71
|
+
statusCode: ctx.lambda.statusCode,
|
|
72
|
+
headers: { "content-type": "application/json" },
|
|
73
|
+
body: JSON.stringify(ctx.result),
|
|
74
|
+
});
|
|
75
|
+
} else {
|
|
76
|
+
ctx.lambda.resolve(ctx.result);
|
|
77
|
+
}
|
|
94
78
|
},
|
|
95
79
|
},
|
|
96
80
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module.exports = require("./Transport").compose({
|
|
2
|
+
name: "MemoryTransport",
|
|
3
|
+
|
|
4
|
+
props: {
|
|
5
|
+
AllserverClient: require("../client/AllserverClient"),
|
|
6
|
+
MemoryClientTransport: require("../client/MemoryClientTransport"),
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
methods: {
|
|
10
|
+
startServer(defaultCtx) {
|
|
11
|
+
return this.AllserverClient({
|
|
12
|
+
transport: this.MemoryClientTransport({ allserverContext: { ...defaultCtx, memory: {} } }),
|
|
13
|
+
});
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
reply(ctx) {
|
|
17
|
+
return ctx.result;
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
getProcedureName(ctx) {
|
|
21
|
+
return ctx.procedureName;
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
isIntrospection(ctx) {
|
|
25
|
+
return this.getProcedureName(ctx) === "";
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|