allserver 2.2.1 → 2.4.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
CHANGED
|
@@ -361,8 +361,6 @@ app.use("/route-with-allsever", middleware);
|
|
|
361
361
|
|
|
362
362
|
Doesn't require a dedicated client transport. Use the HTTP client below.
|
|
363
363
|
|
|
364
|
-
NB: not yet tested in production.
|
|
365
|
-
|
|
366
364
|
```js
|
|
367
365
|
const { Allserver, LambdaTransport } = require("allserver");
|
|
368
366
|
|
|
@@ -655,13 +653,11 @@ import { Lambda } from "@aws-sdk/client-lambda";
|
|
|
655
653
|
const invocationResponse = await new Lambda().invoke({
|
|
656
654
|
FunctionName: "my-lambda-name",
|
|
657
655
|
Payload: JSON.stringify({
|
|
656
|
+
_: { procedureName: "updateUser" },
|
|
658
657
|
id: "123412341234123412341234",
|
|
659
658
|
firstName: "Fred",
|
|
660
659
|
lastName: "Flinstone",
|
|
661
660
|
}),
|
|
662
|
-
ClientContext: JSON.stringify({
|
|
663
|
-
procedureName: "updateUser",
|
|
664
|
-
}),
|
|
665
661
|
});
|
|
666
662
|
const { success, code, message, user } = JSON.parse(invocationResponse.Payload);
|
|
667
663
|
```
|
|
@@ -669,7 +665,7 @@ const { success, code, message, user } = JSON.parse(invocationResponse.Payload);
|
|
|
669
665
|
Alternatively, you can call the same procedure using the `aws` CLI:
|
|
670
666
|
|
|
671
667
|
```shell
|
|
672
|
-
aws lambda invoke --function-name my-lambda-name --
|
|
668
|
+
aws lambda invoke --function-name my-lambda-name --payload '{"_":{"procedureName":"updateUser"},"id":"123412341234123412341234","firstName":"Fred","lastName":"Flinstone"}}'
|
|
673
669
|
```
|
|
674
670
|
|
|
675
671
|
## `AllserverClient` options
|
|
@@ -913,7 +909,7 @@ const client = AllserverClient({
|
|
|
913
909
|
ctx.http.headers.authorization = "Basic my-token";
|
|
914
910
|
},
|
|
915
911
|
async after(ctx) {
|
|
916
|
-
if (ctx.error) console.error(ctx.error) else console.log(ctx.result);
|
|
912
|
+
if (ctx.error) console.error(ctx.error); else console.log(ctx.result);
|
|
917
913
|
},
|
|
918
914
|
});
|
|
919
915
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "allserver",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.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": {
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"@grpc/grpc-js": "^1.1.7",
|
|
66
66
|
"@grpc/proto-loader": "^0.7.5",
|
|
67
67
|
"bullmq": "^3.10.1",
|
|
68
|
+
"cls-hooked": "^4.2.2",
|
|
68
69
|
"eslint": "^8.55.0",
|
|
69
70
|
"express": "^4.18.2",
|
|
70
71
|
"lambda-local": "^1.7.3",
|
|
@@ -213,40 +213,55 @@ module.exports = require("stampit")({
|
|
|
213
213
|
const defaultCtx = { client: this, isIntrospection: true };
|
|
214
214
|
const ctx = transport.createCallContext(defaultCtx);
|
|
215
215
|
|
|
216
|
-
await this._callMiddlewares(ctx, "before")
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
216
|
+
await this._callMiddlewares(ctx, "before", async () => {
|
|
217
|
+
if (!ctx.result) {
|
|
218
|
+
try {
|
|
219
|
+
// This is supposed to be executed only once (per uri) unless it throws.
|
|
220
|
+
// There are only 3 situations when this throws:
|
|
221
|
+
// * the "introspect" method not found on server,
|
|
222
|
+
// * the network request is malformed,
|
|
223
|
+
// * couldn't connect to the remote host.
|
|
224
|
+
ctx.result = await transport.introspect(ctx);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
ctx.result = {
|
|
227
|
+
success: false,
|
|
228
|
+
code: "ALLSERVER_CLIENT_INTROSPECTION_FAILED",
|
|
229
|
+
message: `Couldn't introspect ${transport.uri} due to: ${err.message}`,
|
|
230
|
+
noNetToServer: Boolean(err.noNetToServer),
|
|
231
|
+
error: err,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
234
|
}
|
|
235
|
-
}
|
|
236
235
|
|
|
237
|
-
|
|
236
|
+
await this._callMiddlewares(ctx, "after");
|
|
237
|
+
});
|
|
238
238
|
|
|
239
239
|
return ctx.result;
|
|
240
240
|
},
|
|
241
241
|
|
|
242
|
-
async _callMiddlewares(ctx, middlewareType) {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
242
|
+
async _callMiddlewares(ctx, middlewareType, next) {
|
|
243
|
+
const runMiddlewares = async (middlewares) => {
|
|
244
|
+
if (!middlewares?.length) {
|
|
245
|
+
// no middlewares to run
|
|
246
|
+
if (next) return await next();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const middleware = middlewares[0];
|
|
250
|
+
async function handleMiddlewareResult(result) {
|
|
247
251
|
if (result !== undefined) {
|
|
248
252
|
ctx.result = result;
|
|
249
|
-
|
|
253
|
+
// Do not call any more middlewares
|
|
254
|
+
} else {
|
|
255
|
+
await runMiddlewares(middlewares.slice(1));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
if (middleware.length > 1) {
|
|
260
|
+
// This middleware accepts more than one argument
|
|
261
|
+
await middleware.call(this, ctx, handleMiddlewareResult);
|
|
262
|
+
} else {
|
|
263
|
+
const result = await middleware.call(this, ctx);
|
|
264
|
+
await handleMiddlewareResult(result);
|
|
250
265
|
}
|
|
251
266
|
} catch (err) {
|
|
252
267
|
if (!this[p].neverThrow) throw err;
|
|
@@ -257,34 +272,42 @@ module.exports = require("stampit")({
|
|
|
257
272
|
message = `The '${middlewareType}' middleware error while calling '${ctx.procedureName}' procedure: ${err.message}`;
|
|
258
273
|
}
|
|
259
274
|
ctx.result = { success: false, code, message, error: err };
|
|
260
|
-
|
|
275
|
+
// Do not call any more middlewares
|
|
276
|
+
if (next) return await next();
|
|
261
277
|
}
|
|
262
|
-
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const middlewares = [].concat(this[p][middlewareType]).filter(isFunction);
|
|
281
|
+
return await runMiddlewares(middlewares);
|
|
263
282
|
},
|
|
264
283
|
|
|
265
284
|
async call(procedureName, arg) {
|
|
285
|
+
if (!arg) arg = {};
|
|
286
|
+
if (!arg._) arg._ = {};
|
|
287
|
+
arg._.procedureName = procedureName;
|
|
288
|
+
|
|
266
289
|
const transport = this[p].transport;
|
|
267
290
|
const defaultCtx = { procedureName, arg, client: this };
|
|
268
291
|
const ctx = transport.createCallContext(defaultCtx);
|
|
269
292
|
|
|
270
|
-
await this._callMiddlewares(ctx, "before")
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
293
|
+
await this._callMiddlewares(ctx, "before", async () => {
|
|
294
|
+
if (!ctx.result) {
|
|
295
|
+
try {
|
|
296
|
+
ctx.result = await transport.call(ctx);
|
|
297
|
+
} catch (err) {
|
|
298
|
+
if (!this[p].neverThrow) throw err;
|
|
299
|
+
|
|
300
|
+
let { code, message } = err;
|
|
301
|
+
if (!err.code || err.noNetToServer) {
|
|
302
|
+
code = "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE";
|
|
303
|
+
message = `Couldn't reach remote procedure ${ctx.procedureName} due to: ${err.message}`;
|
|
304
|
+
}
|
|
305
|
+
ctx.result = { success: false, code, message, error: err };
|
|
282
306
|
}
|
|
283
|
-
ctx.result = { success: false, code, message, error: err };
|
|
284
307
|
}
|
|
285
|
-
}
|
|
286
308
|
|
|
287
|
-
|
|
309
|
+
await this._callMiddlewares(ctx, "after");
|
|
310
|
+
});
|
|
288
311
|
|
|
289
312
|
return ctx.result;
|
|
290
313
|
},
|
|
@@ -28,12 +28,15 @@ module.exports = require("./ClientTransport").compose({
|
|
|
28
28
|
return result;
|
|
29
29
|
},
|
|
30
30
|
|
|
31
|
-
async call(
|
|
31
|
+
async call(ctx) {
|
|
32
32
|
let promise = this.awsSdkLambdaClient.invoke({
|
|
33
33
|
FunctionName: this.uri.substring("lambda://".length),
|
|
34
|
+
// TODO: change to this during the next major release:
|
|
35
|
+
// Payload: JSON.stringify(ctx.arg),
|
|
34
36
|
Payload: JSON.stringify({
|
|
35
|
-
callContext: { ...lambda.callContext, procedureName },
|
|
36
|
-
callArg: lambda.callArg,
|
|
37
|
+
callContext: { ...ctx.lambda.callContext, procedureName: ctx.procedureName },
|
|
38
|
+
callArg: ctx.lambda.callArg,
|
|
39
|
+
...ctx.arg,
|
|
37
40
|
}),
|
|
38
41
|
});
|
|
39
42
|
if (typeof promise.promise === "function") promise = promise.promise(); // AWS SDK v2 adoption
|
|
@@ -44,6 +47,8 @@ module.exports = require("./ClientTransport").compose({
|
|
|
44
47
|
createCallContext(defaultCtx) {
|
|
45
48
|
return {
|
|
46
49
|
...defaultCtx,
|
|
50
|
+
// TODO: change to this during the next major release:
|
|
51
|
+
// lambda: {}
|
|
47
52
|
lambda: {
|
|
48
53
|
callContext: {},
|
|
49
54
|
callArg: defaultCtx.arg,
|
package/src/server/Allserver.js
CHANGED
|
@@ -50,6 +50,13 @@ module.exports = require("stampit")({
|
|
|
50
50
|
await this.transport.prepareIntrospectionReply(ctx);
|
|
51
51
|
},
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* This method does not throw.
|
|
55
|
+
* The `ctx.procedure` is the function to call.
|
|
56
|
+
* @param ctx
|
|
57
|
+
* @return {Promise<void>}
|
|
58
|
+
* @private
|
|
59
|
+
*/
|
|
53
60
|
async _callProcedure(ctx) {
|
|
54
61
|
if (!isFunction(ctx.procedure)) {
|
|
55
62
|
ctx.result = {
|
|
@@ -91,16 +98,29 @@ module.exports = require("stampit")({
|
|
|
91
98
|
}
|
|
92
99
|
},
|
|
93
100
|
|
|
94
|
-
async _callMiddlewares(ctx, middlewareType) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
async _callMiddlewares(ctx, middlewareType, next) {
|
|
102
|
+
const runMiddlewares = async (middlewares) => {
|
|
103
|
+
if (!middlewares?.length) {
|
|
104
|
+
// no middlewares to run
|
|
105
|
+
if (next) return await next();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const middleware = middlewares[0];
|
|
109
|
+
async function handleMiddlewareResult(result) {
|
|
101
110
|
if (result !== undefined) {
|
|
102
111
|
ctx.result = result;
|
|
103
|
-
|
|
112
|
+
// Do not call any more middlewares
|
|
113
|
+
} else {
|
|
114
|
+
await runMiddlewares(middlewares.slice(1));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
if (middleware.length > 1) {
|
|
119
|
+
// This middleware accepts more than one argument
|
|
120
|
+
await middleware.call(this, ctx, handleMiddlewareResult);
|
|
121
|
+
} else {
|
|
122
|
+
const result = await middleware.call(this, ctx);
|
|
123
|
+
await handleMiddlewareResult(result);
|
|
104
124
|
}
|
|
105
125
|
} catch (err) {
|
|
106
126
|
const code = err.code || "ALLSERVER_MIDDLEWARE_ERROR";
|
|
@@ -111,9 +131,14 @@ module.exports = require("stampit")({
|
|
|
111
131
|
code,
|
|
112
132
|
message: `'${err.message}' error in '${middlewareType}' middleware`,
|
|
113
133
|
};
|
|
114
|
-
|
|
134
|
+
// Do not call any more middlewares
|
|
135
|
+
if (next) return await next();
|
|
115
136
|
}
|
|
116
|
-
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const middlewares = [].concat(this[middlewareType]).filter(isFunction);
|
|
140
|
+
|
|
141
|
+
return await runMiddlewares(middlewares);
|
|
117
142
|
},
|
|
118
143
|
|
|
119
144
|
async handleCall(ctx) {
|
|
@@ -123,18 +148,22 @@ module.exports = require("stampit")({
|
|
|
123
148
|
ctx.isIntrospection = this.transport.isIntrospection(ctx);
|
|
124
149
|
if (!ctx.isIntrospection && ctx.procedureName) ctx.procedure = this.procedures[ctx.procedureName];
|
|
125
150
|
|
|
126
|
-
|
|
151
|
+
if (!ctx.arg) ctx.arg = {};
|
|
152
|
+
if (!ctx.arg._) ctx.arg._ = {};
|
|
153
|
+
if (!ctx.arg._.procedureName) ctx.arg._.procedureName = ctx.procedureName;
|
|
127
154
|
|
|
128
|
-
|
|
129
|
-
if (ctx.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
155
|
+
await this._callMiddlewares(ctx, "before", async () => {
|
|
156
|
+
if (!ctx.result) {
|
|
157
|
+
if (ctx.isIntrospection) {
|
|
158
|
+
await this._introspect(ctx);
|
|
159
|
+
} else {
|
|
160
|
+
await this._callProcedure(ctx);
|
|
161
|
+
}
|
|
133
162
|
}
|
|
134
|
-
}
|
|
135
163
|
|
|
136
|
-
|
|
137
|
-
|
|
164
|
+
// Warning! This call might overwrite an existing result.
|
|
165
|
+
await this._callMiddlewares(ctx, "after");
|
|
166
|
+
});
|
|
138
167
|
|
|
139
168
|
return this.transport.reply(ctx);
|
|
140
169
|
},
|
|
@@ -21,7 +21,9 @@ module.exports = require("./Transport").compose({
|
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
23
|
} else {
|
|
24
|
-
|
|
24
|
+
// TODO: change to this during the next major release:
|
|
25
|
+
// ctx.arg = ctx.lambda.invoke || {};
|
|
26
|
+
ctx.arg = ctx.lambda.invoke.callArg || ctx.lambda.invoke || {};
|
|
25
27
|
return true;
|
|
26
28
|
}
|
|
27
29
|
},
|
|
@@ -54,7 +56,11 @@ module.exports = require("./Transport").compose({
|
|
|
54
56
|
headers: event?.headers,
|
|
55
57
|
};
|
|
56
58
|
} else {
|
|
57
|
-
|
|
59
|
+
// TODO: change to this during the next major release:
|
|
60
|
+
// lambda.invoke = event || {};
|
|
61
|
+
lambda.invoke = event?.callArg
|
|
62
|
+
? { callContext: event?.callContext, callArg: event?.callArg }
|
|
63
|
+
: event || {};
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
this._handleRequest({ ...defaultCtx, lambda });
|
|
@@ -64,7 +70,9 @@ module.exports = require("./Transport").compose({
|
|
|
64
70
|
|
|
65
71
|
getProcedureName(ctx) {
|
|
66
72
|
const { isHttp, http, invoke } = ctx.lambda;
|
|
67
|
-
|
|
73
|
+
// TODO: change to this during the next major release:
|
|
74
|
+
// return (isHttp ? http.path.substr(1) : invoke._?.procedureName) || ""; // if no procedureName then it's an introspection
|
|
75
|
+
return isHttp ? http.path.substr(1) : invoke.callContext?.procedureName || invoke._?.procedureName || ""; // if no procedureName then it's an introspection
|
|
68
76
|
},
|
|
69
77
|
|
|
70
78
|
isIntrospection(ctx) {
|