@twin.org/api-processors 0.0.1-next.8 → 0.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/dist/cjs/index.cjs +212 -30
- package/dist/esm/index.mjs +211 -31
- package/dist/types/data/{routeProcessor.d.ts → restRouteProcessor.d.ts} +8 -8
- package/dist/types/data/socketRouteProcessor.d.ts +32 -0
- package/dist/types/identity/nodeIdentityProcessor.d.ts +7 -3
- package/dist/types/identity/staticUserIdentityProcessor.d.ts +9 -9
- package/dist/types/index.d.ts +9 -4
- package/dist/types/logging/loggingProcessor.d.ts +11 -13
- package/dist/types/mimeType/jwtMimeTypeProcessor.d.ts +25 -0
- package/dist/types/models/ILoggingProcessorConfig.d.ts +17 -0
- package/dist/types/models/ILoggingProcessorConstructorOptions.d.ts +15 -0
- package/dist/types/models/IRestRouteProcessorConstructorOptions.d.ts +10 -0
- package/dist/types/models/ISocketRouteProcessorConstructorOptions.d.ts +10 -0
- package/dist/types/models/IStaticUserIdentityProcessorConstructorOptions.d.ts +10 -0
- package/docs/changelog.md +85 -1
- package/docs/reference/classes/JwtMimeTypeProcessor.md +81 -0
- package/docs/reference/classes/LoggingProcessor.md +43 -27
- package/docs/reference/classes/NodeIdentityProcessor.md +35 -15
- package/docs/reference/classes/{RouteProcessor.md → RestRouteProcessor.md} +30 -18
- package/docs/reference/classes/SocketRouteProcessor.md +99 -0
- package/docs/reference/classes/StaticUserIdentityProcessor.md +29 -17
- package/docs/reference/index.md +8 -3
- package/docs/reference/interfaces/ILoggingProcessorConfig.md +27 -0
- package/docs/reference/interfaces/ILoggingProcessorConstructorOptions.md +23 -0
- package/docs/reference/interfaces/IRestRouteProcessorConstructorOptions.md +11 -0
- package/docs/reference/interfaces/ISocketRouteProcessorConstructorOptions.md +11 -0
- package/docs/reference/interfaces/IStaticUserIdentityProcessorConstructorOptions.md +11 -0
- package/locales/en.json +5 -2
- package/package.json +10 -10
- package/dist/types/models/IRequestLoggingProcessorConfig.d.ts +0 -9
- package/dist/types/models/IResponseLoggingProcessorConfig.d.ts +0 -9
- package/docs/reference/interfaces/IRequestLoggingProcessorConfig.md +0 -11
- package/docs/reference/interfaces/IResponseLoggingProcessorConfig.md +0 -11
package/dist/cjs/index.cjs
CHANGED
|
@@ -10,11 +10,15 @@ var loggingModels = require('@twin.org/logging-models');
|
|
|
10
10
|
/**
|
|
11
11
|
* Process the REST request and hands it on to the route handler.
|
|
12
12
|
*/
|
|
13
|
-
class
|
|
13
|
+
class RestRouteProcessor {
|
|
14
|
+
/**
|
|
15
|
+
* The namespace supported by the processor.
|
|
16
|
+
*/
|
|
17
|
+
static NAMESPACE = "rest-route";
|
|
14
18
|
/**
|
|
15
19
|
* Runtime name for the class.
|
|
16
20
|
*/
|
|
17
|
-
CLASS_NAME = "
|
|
21
|
+
CLASS_NAME = "RestRouteProcessor";
|
|
18
22
|
/**
|
|
19
23
|
* Include the stack with errors.
|
|
20
24
|
* @internal
|
|
@@ -23,8 +27,6 @@ class RouteProcessor {
|
|
|
23
27
|
/**
|
|
24
28
|
* Create a new instance of RouteProcessor.
|
|
25
29
|
* @param options Options for the processor.
|
|
26
|
-
* @param options.config The configuration for the processor.
|
|
27
|
-
* @returns Promise that resolves when the processor is initialized.
|
|
28
30
|
*/
|
|
29
31
|
constructor(options) {
|
|
30
32
|
this._includeErrorStack = options?.config?.includeErrorStack ?? false;
|
|
@@ -112,42 +114,78 @@ class RouteProcessor {
|
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
// Copyright 2024 IOTA Stiftung.
|
|
118
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
115
119
|
/**
|
|
116
|
-
*
|
|
120
|
+
* Process the socket request and hands it on to the route handler.
|
|
117
121
|
*/
|
|
118
|
-
class
|
|
122
|
+
class SocketRouteProcessor {
|
|
123
|
+
/**
|
|
124
|
+
* The namespace supported by the processor.
|
|
125
|
+
*/
|
|
126
|
+
static NAMESPACE = "socket-route";
|
|
119
127
|
/**
|
|
120
128
|
* Runtime name for the class.
|
|
121
129
|
*/
|
|
122
|
-
CLASS_NAME = "
|
|
130
|
+
CLASS_NAME = "SocketRouteProcessor";
|
|
123
131
|
/**
|
|
124
|
-
*
|
|
132
|
+
* Include the stack with errors.
|
|
125
133
|
* @internal
|
|
126
134
|
*/
|
|
127
|
-
|
|
135
|
+
_includeErrorStack;
|
|
128
136
|
/**
|
|
129
|
-
* Create a new instance of
|
|
137
|
+
* Create a new instance of SocketRouteProcessor.
|
|
130
138
|
* @param options Options for the processor.
|
|
131
|
-
* @param options.config The configuration for the processor.
|
|
132
|
-
* @returns Promise that resolves when the processor is initialized.
|
|
133
139
|
*/
|
|
134
140
|
constructor(options) {
|
|
135
|
-
|
|
136
|
-
core.Guards.object(this.CLASS_NAME, "options.config", options.config);
|
|
137
|
-
core.Guards.stringValue(this.CLASS_NAME, "options.config.userIdentity", options.config.userIdentity);
|
|
138
|
-
this._userIdentity = options.config.userIdentity;
|
|
141
|
+
this._includeErrorStack = options?.config?.includeErrorStack ?? false;
|
|
139
142
|
}
|
|
140
143
|
/**
|
|
141
|
-
*
|
|
144
|
+
* Process the REST request for the specified route.
|
|
142
145
|
* @param request The incoming request.
|
|
143
146
|
* @param response The outgoing response.
|
|
144
147
|
* @param route The route to process.
|
|
145
148
|
* @param requestIdentity The identity context for the request.
|
|
146
149
|
* @param processorState The state handed through the processors.
|
|
150
|
+
* @param responseEmitter The function to emit a response.
|
|
147
151
|
*/
|
|
148
|
-
async
|
|
149
|
-
|
|
150
|
-
|
|
152
|
+
async process(request, response, route, requestIdentity, processorState, responseEmitter) {
|
|
153
|
+
// Don't handle the route if another processor has already set the response
|
|
154
|
+
// status code e.g. from an auth processor
|
|
155
|
+
if (core.Is.empty(response.statusCode)) {
|
|
156
|
+
if (core.Is.empty(route)) {
|
|
157
|
+
apiModels.HttpErrorHelper.buildResponse(response, {
|
|
158
|
+
name: core.NotFoundError.CLASS_NAME,
|
|
159
|
+
message: `${this.CLASS_NAME}.routeNotFound`,
|
|
160
|
+
properties: {
|
|
161
|
+
notFoundId: request.url
|
|
162
|
+
}
|
|
163
|
+
}, web.HttpStatusCode.notFound);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
try {
|
|
167
|
+
const req = {
|
|
168
|
+
pathParams: request.pathParams,
|
|
169
|
+
query: request.query,
|
|
170
|
+
body: request.body
|
|
171
|
+
};
|
|
172
|
+
await route.handler({
|
|
173
|
+
...requestIdentity,
|
|
174
|
+
serverRequest: request,
|
|
175
|
+
processorState
|
|
176
|
+
}, req, async (topic, restRouteResponse) => {
|
|
177
|
+
response.headers = restRouteResponse?.headers;
|
|
178
|
+
response.body = restRouteResponse?.body;
|
|
179
|
+
response.statusCode =
|
|
180
|
+
restRouteResponse.statusCode ?? response.statusCode ?? web.HttpStatusCode.ok;
|
|
181
|
+
await responseEmitter(topic, response);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
const { error, httpStatusCode } = apiModels.HttpErrorHelper.processError(err, this._includeErrorStack);
|
|
186
|
+
apiModels.HttpErrorHelper.buildResponse(response, error, httpStatusCode);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
151
189
|
}
|
|
152
190
|
}
|
|
153
191
|
}
|
|
@@ -156,6 +194,10 @@ class StaticUserIdentityProcessor {
|
|
|
156
194
|
* Adds a node identity to the request identity.
|
|
157
195
|
*/
|
|
158
196
|
class NodeIdentityProcessor {
|
|
197
|
+
/**
|
|
198
|
+
* The namespace supported by the processor.
|
|
199
|
+
*/
|
|
200
|
+
static NAMESPACE = "node-identity";
|
|
159
201
|
/**
|
|
160
202
|
* Runtime name for the class.
|
|
161
203
|
*/
|
|
@@ -188,10 +230,56 @@ class NodeIdentityProcessor {
|
|
|
188
230
|
}
|
|
189
231
|
}
|
|
190
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Adds a static user identity to the request context.
|
|
235
|
+
*/
|
|
236
|
+
class StaticUserIdentityProcessor {
|
|
237
|
+
/**
|
|
238
|
+
* The namespace supported by the processor.
|
|
239
|
+
*/
|
|
240
|
+
static NAMESPACE = "static-user-identity";
|
|
241
|
+
/**
|
|
242
|
+
* Runtime name for the class.
|
|
243
|
+
*/
|
|
244
|
+
CLASS_NAME = "StaticUserIdentityProcessor";
|
|
245
|
+
/**
|
|
246
|
+
* The fixed identity for request context.
|
|
247
|
+
* @internal
|
|
248
|
+
*/
|
|
249
|
+
_userIdentity;
|
|
250
|
+
/**
|
|
251
|
+
* Create a new instance of StaticIdentityProcessor.
|
|
252
|
+
* @param options Options for the processor.
|
|
253
|
+
*/
|
|
254
|
+
constructor(options) {
|
|
255
|
+
core.Guards.object(this.CLASS_NAME, "options", options);
|
|
256
|
+
core.Guards.object(this.CLASS_NAME, "options.config", options.config);
|
|
257
|
+
core.Guards.stringValue(this.CLASS_NAME, "options.config.userIdentity", options.config.userIdentity);
|
|
258
|
+
this._userIdentity = options.config.userIdentity;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Pre process the REST request for the specified route.
|
|
262
|
+
* @param request The incoming request.
|
|
263
|
+
* @param response The outgoing response.
|
|
264
|
+
* @param route The route to process.
|
|
265
|
+
* @param requestIdentity The identity context for the request.
|
|
266
|
+
* @param processorState The state handed through the processors.
|
|
267
|
+
*/
|
|
268
|
+
async pre(request, response, route, requestIdentity, processorState) {
|
|
269
|
+
if (!core.Is.empty(route) && !(route.skipAuth ?? false)) {
|
|
270
|
+
requestIdentity.userIdentity = this._userIdentity;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
191
275
|
/**
|
|
192
276
|
* Process the REST request and log its information.
|
|
193
277
|
*/
|
|
194
278
|
class LoggingProcessor {
|
|
279
|
+
/**
|
|
280
|
+
* The namespace supported by the processor.
|
|
281
|
+
*/
|
|
282
|
+
static NAMESPACE = "logging";
|
|
195
283
|
/**
|
|
196
284
|
* Runtime name for the class.
|
|
197
285
|
*/
|
|
@@ -207,15 +295,24 @@ class LoggingProcessor {
|
|
|
207
295
|
*/
|
|
208
296
|
_includeBody;
|
|
209
297
|
/**
|
|
210
|
-
*
|
|
298
|
+
* Show the full base64 content for data, default to abbreviate.
|
|
299
|
+
* @internal
|
|
300
|
+
*/
|
|
301
|
+
_fullBase64;
|
|
302
|
+
/**
|
|
303
|
+
* List of property names to obfuscate, defaults to "password".
|
|
304
|
+
* @internal
|
|
305
|
+
*/
|
|
306
|
+
_obfuscateProperties;
|
|
307
|
+
/**
|
|
308
|
+
* Create a new instance of LoggingProcessor.
|
|
211
309
|
* @param options Options for the processor.
|
|
212
|
-
* @param options.loggingConnectorType The type for the logging connector, defaults to "logging".
|
|
213
|
-
* @param options.config The configuration for the processor.
|
|
214
|
-
* @returns Promise that resolves when the processor is initialized.
|
|
215
310
|
*/
|
|
216
311
|
constructor(options) {
|
|
217
312
|
this._loggingConnector = loggingModels.LoggingConnectorFactory.get(options?.loggingConnectorType ?? "logging");
|
|
218
313
|
this._includeBody = options?.config?.includeBody ?? false;
|
|
314
|
+
this._fullBase64 = options?.config?.fullBase64 ?? false;
|
|
315
|
+
this._obfuscateProperties = options?.config?.obfuscateProperties ?? ["password"];
|
|
219
316
|
}
|
|
220
317
|
/**
|
|
221
318
|
* Pre process the REST request for the specified route.
|
|
@@ -228,12 +325,28 @@ class LoggingProcessor {
|
|
|
228
325
|
async pre(request, response, route, requestIdentity, processorState) {
|
|
229
326
|
const now = process.hrtime.bigint();
|
|
230
327
|
processorState.requestStart = now;
|
|
328
|
+
const contentType = request.headers?.[web.HeaderTypes.ContentType];
|
|
329
|
+
const isJson = core.Is.stringValue(contentType)
|
|
330
|
+
? contentType.includes(web.MimeTypes.Json) || contentType.includes(web.MimeTypes.JsonLd)
|
|
331
|
+
: false;
|
|
332
|
+
let requestUrl = "";
|
|
333
|
+
if (core.Is.stringValue(request.url)) {
|
|
334
|
+
// Socket paths do not have a prefix so just use the whole url.
|
|
335
|
+
if (request.url.startsWith("http")) {
|
|
336
|
+
requestUrl = new URL(request.url).pathname;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
requestUrl = request.url;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
231
342
|
await this._loggingConnector.log({
|
|
232
343
|
level: "info",
|
|
233
344
|
source: this.CLASS_NAME,
|
|
234
345
|
ts: Date.now(),
|
|
235
|
-
message: `===> ${request.method} ${
|
|
236
|
-
data: this._includeBody
|
|
346
|
+
message: `===> ${request.method} ${requestUrl}`,
|
|
347
|
+
data: this._includeBody && isJson
|
|
348
|
+
? this.processJson("body", core.ObjectHelper.clone(request?.body))
|
|
349
|
+
: undefined
|
|
237
350
|
});
|
|
238
351
|
}
|
|
239
352
|
/**
|
|
@@ -248,11 +361,13 @@ class LoggingProcessor {
|
|
|
248
361
|
let data;
|
|
249
362
|
if (this._includeBody) {
|
|
250
363
|
const contentType = response.headers?.[web.HeaderTypes.ContentType];
|
|
251
|
-
const isJson =
|
|
364
|
+
const isJson = core.Is.stringValue(contentType)
|
|
365
|
+
? contentType.includes(web.MimeTypes.Json) || contentType.includes(web.MimeTypes.JsonLd)
|
|
366
|
+
: false;
|
|
252
367
|
const contentLength = response.headers?.[web.HeaderTypes.ContentLength];
|
|
253
368
|
if (isJson) {
|
|
254
369
|
data = {
|
|
255
|
-
body: response.body
|
|
370
|
+
body: this.processJson("body", core.ObjectHelper.clone(response.body))
|
|
256
371
|
};
|
|
257
372
|
}
|
|
258
373
|
else {
|
|
@@ -274,19 +389,86 @@ class LoggingProcessor {
|
|
|
274
389
|
const start = core.Coerce.bigint(processorState.requestStart) ?? now;
|
|
275
390
|
const elapsed = now - start;
|
|
276
391
|
const elapsedMicroSeconds = Math.floor(Number(elapsed) / 1000);
|
|
392
|
+
let requestUrl = "";
|
|
393
|
+
if (core.Is.stringValue(request.url)) {
|
|
394
|
+
// Socket paths do not have a prefix so just use the whole url.
|
|
395
|
+
if (request.url.startsWith("http")) {
|
|
396
|
+
requestUrl = new URL(request.url).pathname;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
requestUrl = request.url;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
277
402
|
await this._loggingConnector.log({
|
|
278
403
|
level: core.Is.number(response.statusCode) && response.statusCode >= web.HttpStatusCode.badRequest
|
|
279
404
|
? "error"
|
|
280
405
|
: "info",
|
|
281
406
|
source: this.CLASS_NAME,
|
|
282
407
|
ts: Date.now(),
|
|
283
|
-
message: `<=== ${response.statusCode ?? ""} ${request.method} ${
|
|
408
|
+
message: `<=== ${response.statusCode ?? ""} ${request.method} ${requestUrl} duration: ${elapsedMicroSeconds}µs`,
|
|
284
409
|
data
|
|
285
410
|
});
|
|
286
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Process the JSON.
|
|
414
|
+
* @param propValue The property to process.
|
|
415
|
+
* @returns The processed property.
|
|
416
|
+
* @internal
|
|
417
|
+
*/
|
|
418
|
+
processJson(propName, propValue) {
|
|
419
|
+
if (core.Is.array(propValue)) {
|
|
420
|
+
for (let i = 0; i < propValue.length; i++) {
|
|
421
|
+
propValue[i] = this.processJson(`${i}`, propValue[i]);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
else if (core.Is.object(propValue)) {
|
|
425
|
+
for (const key of Object.keys(propValue)) {
|
|
426
|
+
propValue[key] = this.processJson(key, propValue[key]);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
else if (!this._fullBase64 && core.Is.stringBase64(propValue) && propValue.length > 256) {
|
|
430
|
+
propValue = "<base64>";
|
|
431
|
+
}
|
|
432
|
+
else if (core.Is.string(propValue) &&
|
|
433
|
+
this._obfuscateProperties.some(op => new RegExp(op).test(propName))) {
|
|
434
|
+
propValue = "**************";
|
|
435
|
+
}
|
|
436
|
+
return propValue;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Process the JWT mime type.
|
|
442
|
+
*/
|
|
443
|
+
class JwtMimeTypeProcessor {
|
|
444
|
+
/**
|
|
445
|
+
* The namespace supported by the processor.
|
|
446
|
+
*/
|
|
447
|
+
static NAMESPACE = "jwt";
|
|
448
|
+
/**
|
|
449
|
+
* Runtime name for the class.
|
|
450
|
+
*/
|
|
451
|
+
CLASS_NAME = "JwtMimeTypeProcessor";
|
|
452
|
+
/**
|
|
453
|
+
* Get the MIME types that this handler can handle.
|
|
454
|
+
* @returns The MIME types that this handler can handle.
|
|
455
|
+
*/
|
|
456
|
+
getTypes() {
|
|
457
|
+
return [web.MimeTypes.Jwt];
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Handle content.
|
|
461
|
+
* @param body The body to process.
|
|
462
|
+
* @returns The processed body.
|
|
463
|
+
*/
|
|
464
|
+
async handle(body) {
|
|
465
|
+
return core.Converter.bytesToUtf8(body);
|
|
466
|
+
}
|
|
287
467
|
}
|
|
288
468
|
|
|
469
|
+
exports.JwtMimeTypeProcessor = JwtMimeTypeProcessor;
|
|
289
470
|
exports.LoggingProcessor = LoggingProcessor;
|
|
290
471
|
exports.NodeIdentityProcessor = NodeIdentityProcessor;
|
|
291
|
-
exports.
|
|
472
|
+
exports.RestRouteProcessor = RestRouteProcessor;
|
|
473
|
+
exports.SocketRouteProcessor = SocketRouteProcessor;
|
|
292
474
|
exports.StaticUserIdentityProcessor = StaticUserIdentityProcessor;
|