@twin.org/api-processors 0.0.2-next.9 → 0.0.3-next.2

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.
Files changed (73) hide show
  1. package/dist/es/data/restRouteProcessor.js +115 -0
  2. package/dist/es/data/restRouteProcessor.js.map +1 -0
  3. package/dist/es/data/socketRouteProcessor.js +126 -0
  4. package/dist/es/data/socketRouteProcessor.js.map +1 -0
  5. package/dist/es/identity/contextIdProcessor.js +68 -0
  6. package/dist/es/identity/contextIdProcessor.js.map +1 -0
  7. package/dist/es/identity/staticContextIdProcessor.js +59 -0
  8. package/dist/es/identity/staticContextIdProcessor.js.map +1 -0
  9. package/dist/es/index.js +19 -0
  10. package/dist/es/index.js.map +1 -0
  11. package/dist/es/logging/loggingProcessor.js +177 -0
  12. package/dist/es/logging/loggingProcessor.js.map +1 -0
  13. package/dist/es/mimeType/jsonLdMimeTypeProcessor.js +38 -0
  14. package/dist/es/mimeType/jsonLdMimeTypeProcessor.js.map +1 -0
  15. package/dist/es/mimeType/jwtMimeTypeProcessor.js +34 -0
  16. package/dist/es/mimeType/jwtMimeTypeProcessor.js.map +1 -0
  17. package/dist/es/models/IContextIdProcessorConfig.js +4 -0
  18. package/dist/es/models/IContextIdProcessorConfig.js.map +1 -0
  19. package/dist/es/models/IContextIdProcessorConstructorOptions.js +2 -0
  20. package/dist/es/models/IContextIdProcessorConstructorOptions.js.map +1 -0
  21. package/dist/es/models/ILoggingProcessorConfig.js +4 -0
  22. package/dist/es/models/ILoggingProcessorConfig.js.map +1 -0
  23. package/dist/es/models/ILoggingProcessorConstructorOptions.js +2 -0
  24. package/dist/es/models/ILoggingProcessorConstructorOptions.js.map +1 -0
  25. package/dist/es/models/IRestRouteProcessorConstructorOptions.js +2 -0
  26. package/dist/es/models/IRestRouteProcessorConstructorOptions.js.map +1 -0
  27. package/dist/es/models/IRouteProcessorConfig.js +4 -0
  28. package/dist/es/models/IRouteProcessorConfig.js.map +1 -0
  29. package/dist/es/models/ISocketRouteProcessorConstructorOptions.js +2 -0
  30. package/dist/es/models/ISocketRouteProcessorConstructorOptions.js.map +1 -0
  31. package/dist/es/models/IStaticContextIdProcessorConfig.js +4 -0
  32. package/dist/es/models/IStaticContextIdProcessorConfig.js.map +1 -0
  33. package/dist/es/models/IStaticContextIdProcessorConstructorOptions.js +2 -0
  34. package/dist/es/models/IStaticContextIdProcessorConstructorOptions.js.map +1 -0
  35. package/dist/types/data/restRouteProcessor.d.ts +9 -5
  36. package/dist/types/data/socketRouteProcessor.d.ts +10 -6
  37. package/dist/types/identity/contextIdProcessor.d.ts +39 -0
  38. package/dist/types/identity/staticContextIdProcessor.d.ts +33 -0
  39. package/dist/types/index.d.ts +16 -14
  40. package/dist/types/logging/loggingProcessor.d.ts +13 -7
  41. package/dist/types/mimeType/jsonLdMimeTypeProcessor.d.ts +6 -1
  42. package/dist/types/mimeType/jwtMimeTypeProcessor.d.ts +6 -1
  43. package/dist/types/models/IContextIdProcessorConfig.d.ts +13 -0
  44. package/dist/types/models/IContextIdProcessorConstructorOptions.d.ts +10 -0
  45. package/dist/types/models/ILoggingProcessorConstructorOptions.d.ts +1 -1
  46. package/dist/types/models/IRestRouteProcessorConstructorOptions.d.ts +1 -1
  47. package/dist/types/models/ISocketRouteProcessorConstructorOptions.d.ts +1 -1
  48. package/dist/types/models/IStaticContextIdProcessorConfig.d.ts +17 -0
  49. package/dist/types/models/IStaticContextIdProcessorConstructorOptions.d.ts +10 -0
  50. package/docs/changelog.md +96 -0
  51. package/docs/reference/classes/{NodeIdentityProcessor.md → ContextIdProcessor.md} +37 -19
  52. package/docs/reference/classes/JsonLdMimeTypeProcessor.md +17 -3
  53. package/docs/reference/classes/JwtMimeTypeProcessor.md +17 -3
  54. package/docs/reference/classes/LoggingProcessor.md +27 -13
  55. package/docs/reference/classes/RestRouteProcessor.md +19 -11
  56. package/docs/reference/classes/SocketRouteProcessor.md +22 -14
  57. package/docs/reference/classes/StaticContextIdProcessor.md +99 -0
  58. package/docs/reference/index.md +6 -4
  59. package/docs/reference/interfaces/IContextIdProcessorConfig.md +19 -0
  60. package/docs/reference/interfaces/IContextIdProcessorConstructorOptions.md +11 -0
  61. package/docs/reference/interfaces/IStaticContextIdProcessorConfig.md +27 -0
  62. package/docs/reference/interfaces/IStaticContextIdProcessorConstructorOptions.md +11 -0
  63. package/locales/en.json +0 -5
  64. package/package.json +20 -9
  65. package/dist/cjs/index.cjs +0 -534
  66. package/dist/esm/index.mjs +0 -526
  67. package/dist/types/identity/nodeIdentityProcessor.d.ts +0 -28
  68. package/dist/types/identity/staticUserIdentityProcessor.d.ts +0 -27
  69. package/dist/types/models/IStaticUserIdentityProcessorConfig.d.ts +0 -9
  70. package/dist/types/models/IStaticUserIdentityProcessorConstructorOptions.d.ts +0 -10
  71. package/docs/reference/classes/StaticUserIdentityProcessor.md +0 -85
  72. package/docs/reference/interfaces/IStaticUserIdentityProcessorConfig.md +0 -11
  73. package/docs/reference/interfaces/IStaticUserIdentityProcessorConstructorOptions.md +0 -11
@@ -0,0 +1,115 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { HttpErrorHelper } from "@twin.org/api-models";
4
+ import { Is, NotFoundError } from "@twin.org/core";
5
+ import { HeaderTypes, HttpStatusCode, MimeTypes } from "@twin.org/web";
6
+ /**
7
+ * Process the REST request and hands it on to the route handler.
8
+ */
9
+ export class RestRouteProcessor {
10
+ /**
11
+ * Runtime name for the class.
12
+ */
13
+ static CLASS_NAME = "RestRouteProcessor";
14
+ /**
15
+ * Include the stack with errors.
16
+ * @internal
17
+ */
18
+ _includeErrorStack;
19
+ /**
20
+ * Create a new instance of RouteProcessor.
21
+ * @param options Options for the processor.
22
+ */
23
+ constructor(options) {
24
+ this._includeErrorStack = options?.config?.includeErrorStack ?? false;
25
+ }
26
+ /**
27
+ * Returns the class name of the component.
28
+ * @returns The class name of the component.
29
+ */
30
+ className() {
31
+ return RestRouteProcessor.CLASS_NAME;
32
+ }
33
+ /**
34
+ * Process the REST request for the specified route.
35
+ * @param request The incoming request.
36
+ * @param response The outgoing response.
37
+ * @param route The route to process.
38
+ * @param processorState The state handed through the processors.
39
+ * @param loggingComponentType The logging component type for the request.
40
+ */
41
+ async process(request, response, route, processorState, loggingComponentType) {
42
+ // Don't handle the route if another processor has already set the response
43
+ // status code e.g. from an auth processor
44
+ if (Is.empty(response.statusCode)) {
45
+ if (Is.empty(route)) {
46
+ HttpErrorHelper.buildResponse(response, {
47
+ name: NotFoundError.CLASS_NAME,
48
+ message: `${RestRouteProcessor.CLASS_NAME}.routeNotFound`,
49
+ properties: {
50
+ notFoundId: request.url
51
+ }
52
+ }, HttpStatusCode.notFound);
53
+ }
54
+ else {
55
+ try {
56
+ const req = {
57
+ pathParams: request.pathParams,
58
+ query: request.query,
59
+ body: request.body
60
+ };
61
+ const restRouteResponse = await route.handler({
62
+ serverRequest: request,
63
+ processorState,
64
+ loggingComponentType
65
+ }, req);
66
+ let statusCode = restRouteResponse.statusCode ?? response.statusCode ?? HttpStatusCode.ok;
67
+ const headers = restRouteResponse?.headers ?? {};
68
+ if (Is.empty(restRouteResponse?.body)) {
69
+ // If there is no custom status code and the body is empty
70
+ // use the no content response and set the length to 0
71
+ headers[HeaderTypes.ContentLength] = "0";
72
+ // Only change to no content if the status code is ok
73
+ // This could be something like a created status code
74
+ // which is successful but has no content
75
+ if (statusCode === HttpStatusCode.ok) {
76
+ statusCode = HttpStatusCode.noContent;
77
+ }
78
+ }
79
+ else {
80
+ // Only set the content type if there is a body
81
+ // If there are custom response types for the route then use them
82
+ // instead of the default application/json
83
+ headers[HeaderTypes.ContentType] =
84
+ restRouteResponse?.attachment?.mimeType ??
85
+ restRouteResponse.headers?.[HeaderTypes.ContentType] ??
86
+ `${MimeTypes.Json}; charset=utf-8`;
87
+ // If there are filename or inline options set then add the content disposition
88
+ if (Is.stringValue(restRouteResponse?.attachment?.filename) ||
89
+ Is.boolean(restRouteResponse?.attachment?.inline)) {
90
+ let filename = "";
91
+ if (Is.stringValue(restRouteResponse?.attachment?.filename)) {
92
+ filename = `; filename="${restRouteResponse?.attachment?.filename}"`;
93
+ }
94
+ headers[HeaderTypes.ContentDisposition] =
95
+ `${restRouteResponse?.attachment?.inline ? "inline" : "attachment"}${filename}`;
96
+ }
97
+ // If this is a binary response then set the content length
98
+ if (Is.uint8Array(restRouteResponse?.body)) {
99
+ const contentLength = restRouteResponse.body.length;
100
+ headers[HeaderTypes.ContentLength] = contentLength.toString();
101
+ }
102
+ response.body = restRouteResponse?.body;
103
+ }
104
+ response.headers = headers;
105
+ response.statusCode = statusCode;
106
+ }
107
+ catch (err) {
108
+ const { error, httpStatusCode } = HttpErrorHelper.processError(err, this._includeErrorStack);
109
+ HttpErrorHelper.buildResponse(response, error, httpStatusCode);
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ //# sourceMappingURL=restRouteProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restRouteProcessor.js","sourceRoot":"","sources":["../../../src/data/restRouteProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpErrorHelper,\n\ttype IHttpRequest,\n\ttype IHttpResponse,\n\ttype IHttpServerRequest,\n\ttype IRestRoute,\n\ttype IRestRouteProcessor,\n\ttype IRestRouteResponseOptions\n} from \"@twin.org/api-models\";\nimport { Is, NotFoundError } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HeaderTypes, HttpStatusCode, MimeTypes } from \"@twin.org/web\";\nimport type { IRestRouteProcessorConstructorOptions } from \"../models/IRestRouteProcessorConstructorOptions.js\";\n\n/**\n * Process the REST request and hands it on to the route handler.\n */\nexport class RestRouteProcessor implements IRestRouteProcessor {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<RestRouteProcessor>();\n\n\t/**\n\t * Include the stack with errors.\n\t * @internal\n\t */\n\tprivate readonly _includeErrorStack: boolean;\n\n\t/**\n\t * Create a new instance of RouteProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: IRestRouteProcessorConstructorOptions) {\n\t\tthis._includeErrorStack = options?.config?.includeErrorStack ?? false;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn RestRouteProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * Process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param processorState The state handed through the processors.\n\t * @param loggingComponentType The logging component type for the request.\n\t */\n\tpublic async process(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IRestRoute | undefined,\n\t\tprocessorState: { [id: string]: unknown },\n\t\tloggingComponentType?: string\n\t): Promise<void> {\n\t\t// Don't handle the route if another processor has already set the response\n\t\t// status code e.g. from an auth processor\n\t\tif (Is.empty(response.statusCode)) {\n\t\t\tif (Is.empty(route)) {\n\t\t\t\tHttpErrorHelper.buildResponse(\n\t\t\t\t\tresponse,\n\t\t\t\t\t{\n\t\t\t\t\t\tname: NotFoundError.CLASS_NAME,\n\t\t\t\t\t\tmessage: `${RestRouteProcessor.CLASS_NAME}.routeNotFound`,\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\tnotFoundId: request.url\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tHttpStatusCode.notFound\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst req: IHttpRequest = {\n\t\t\t\t\t\tpathParams: request.pathParams,\n\t\t\t\t\t\tquery: request.query,\n\t\t\t\t\t\tbody: request.body\n\t\t\t\t\t};\n\n\t\t\t\t\tconst restRouteResponse: IHttpResponse & IRestRouteResponseOptions = await route.handler(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tserverRequest: request,\n\t\t\t\t\t\t\tprocessorState,\n\t\t\t\t\t\t\tloggingComponentType\n\t\t\t\t\t\t},\n\t\t\t\t\t\treq\n\t\t\t\t\t);\n\n\t\t\t\t\tlet statusCode: HttpStatusCode =\n\t\t\t\t\t\trestRouteResponse.statusCode ?? response.statusCode ?? HttpStatusCode.ok;\n\n\t\t\t\t\tconst headers = restRouteResponse?.headers ?? {};\n\n\t\t\t\t\tif (Is.empty(restRouteResponse?.body)) {\n\t\t\t\t\t\t// If there is no custom status code and the body is empty\n\t\t\t\t\t\t// use the no content response and set the length to 0\n\t\t\t\t\t\theaders[HeaderTypes.ContentLength] = \"0\";\n\t\t\t\t\t\t// Only change to no content if the status code is ok\n\t\t\t\t\t\t// This could be something like a created status code\n\t\t\t\t\t\t// which is successful but has no content\n\t\t\t\t\t\tif (statusCode === HttpStatusCode.ok) {\n\t\t\t\t\t\t\tstatusCode = HttpStatusCode.noContent;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Only set the content type if there is a body\n\t\t\t\t\t\t// If there are custom response types for the route then use them\n\t\t\t\t\t\t// instead of the default application/json\n\t\t\t\t\t\theaders[HeaderTypes.ContentType] =\n\t\t\t\t\t\t\trestRouteResponse?.attachment?.mimeType ??\n\t\t\t\t\t\t\trestRouteResponse.headers?.[HeaderTypes.ContentType] ??\n\t\t\t\t\t\t\t`${MimeTypes.Json}; charset=utf-8`;\n\n\t\t\t\t\t\t// If there are filename or inline options set then add the content disposition\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tIs.stringValue(restRouteResponse?.attachment?.filename) ||\n\t\t\t\t\t\t\tIs.boolean(restRouteResponse?.attachment?.inline)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tlet filename = \"\";\n\t\t\t\t\t\t\tif (Is.stringValue(restRouteResponse?.attachment?.filename)) {\n\t\t\t\t\t\t\t\tfilename = `; filename=\"${restRouteResponse?.attachment?.filename}\"`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\theaders[HeaderTypes.ContentDisposition] =\n\t\t\t\t\t\t\t\t`${restRouteResponse?.attachment?.inline ? \"inline\" : \"attachment\"}${filename}`;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If this is a binary response then set the content length\n\t\t\t\t\t\tif (Is.uint8Array(restRouteResponse?.body)) {\n\t\t\t\t\t\t\tconst contentLength = restRouteResponse.body.length;\n\t\t\t\t\t\t\theaders[HeaderTypes.ContentLength] = contentLength.toString();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tresponse.body = restRouteResponse?.body;\n\t\t\t\t\t}\n\n\t\t\t\t\tresponse.headers = headers;\n\t\t\t\t\tresponse.statusCode = statusCode;\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst { error, httpStatusCode } = HttpErrorHelper.processError(\n\t\t\t\t\t\terr,\n\t\t\t\t\t\tthis._includeErrorStack\n\t\t\t\t\t);\n\n\t\t\t\t\tHttpErrorHelper.buildResponse(response, error, httpStatusCode);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,126 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { HttpErrorHelper } from "@twin.org/api-models";
4
+ import { Is, NotFoundError } from "@twin.org/core";
5
+ import { HttpStatusCode } from "@twin.org/web";
6
+ /**
7
+ * Process the socket request and hands it on to the route handler.
8
+ */
9
+ export class SocketRouteProcessor {
10
+ /**
11
+ * Runtime name for the class.
12
+ */
13
+ static CLASS_NAME = "SocketRouteProcessor";
14
+ /**
15
+ * Include the stack with errors.
16
+ * @internal
17
+ */
18
+ _includeErrorStack;
19
+ /**
20
+ * Create a new instance of SocketRouteProcessor.
21
+ * @param options Options for the processor.
22
+ */
23
+ constructor(options) {
24
+ this._includeErrorStack = options?.config?.includeErrorStack ?? false;
25
+ }
26
+ /**
27
+ * Returns the class name of the component.
28
+ * @returns The class name of the component.
29
+ */
30
+ className() {
31
+ return SocketRouteProcessor.CLASS_NAME;
32
+ }
33
+ /**
34
+ * Process the connected event.
35
+ * @param request The server request object containing the socket id and other parameters.
36
+ * @param route The route being requested, if a matching one was found.
37
+ * @param loggingComponentType The logging component type for the request.
38
+ * @returns Promise that resolves when the request is processed.
39
+ */
40
+ async connected(request, route, loggingComponentType) {
41
+ if (route?.connected) {
42
+ try {
43
+ const socketRequestContext = {
44
+ socketId: request.socketId,
45
+ serverRequest: request,
46
+ processorState: {},
47
+ loggingComponentType
48
+ };
49
+ route.connected(socketRequestContext);
50
+ }
51
+ catch { }
52
+ }
53
+ }
54
+ /**
55
+ * Process the disconnected event.
56
+ * @param request The server request object containing the socket id and other parameters.
57
+ * @param route The route being requested, if a matching one was found.
58
+ * @param loggingComponentType The logging component type for the request.
59
+ * @returns Promise that resolves when the request is processed.
60
+ */
61
+ async disconnected(request, route, loggingComponentType) {
62
+ if (route?.disconnected) {
63
+ try {
64
+ const socketRequestContext = {
65
+ socketId: request.socketId,
66
+ serverRequest: request,
67
+ processorState: {},
68
+ loggingComponentType
69
+ };
70
+ route.disconnected(socketRequestContext);
71
+ }
72
+ catch { }
73
+ }
74
+ }
75
+ /**
76
+ * Process the socket request for the specified route.
77
+ * @param request The incoming request.
78
+ * @param response The outgoing response.
79
+ * @param route The route to process.
80
+ * @param processorState The state handed through the processors.
81
+ * @param responseEmitter The function to emit a response.
82
+ * @param loggingComponentType The logging component type for the request.
83
+ */
84
+ async process(request, response, route, processorState, responseEmitter, loggingComponentType) {
85
+ // Don't handle the route if another processor has already set the response
86
+ // status code e.g. from an auth processor
87
+ if (Is.empty(response.statusCode)) {
88
+ if (Is.empty(route)) {
89
+ HttpErrorHelper.buildResponse(response, {
90
+ name: NotFoundError.CLASS_NAME,
91
+ message: `${SocketRouteProcessor.CLASS_NAME}.routeNotFound`,
92
+ properties: {
93
+ notFoundId: request.url
94
+ }
95
+ }, HttpStatusCode.notFound);
96
+ }
97
+ else {
98
+ try {
99
+ const req = {
100
+ pathParams: request.pathParams,
101
+ query: request.query,
102
+ body: request.body
103
+ };
104
+ const socketRequestContext = {
105
+ socketId: request.socketId,
106
+ serverRequest: request,
107
+ processorState,
108
+ loggingComponentType
109
+ };
110
+ route.handler(socketRequestContext, req, async (topic, socketRouteResponse) => {
111
+ response.headers = socketRouteResponse?.headers;
112
+ response.body = socketRouteResponse?.body;
113
+ response.statusCode =
114
+ socketRouteResponse.statusCode ?? response.statusCode ?? HttpStatusCode.ok;
115
+ await responseEmitter(topic, response);
116
+ });
117
+ }
118
+ catch (err) {
119
+ const { error, httpStatusCode } = HttpErrorHelper.processError(err, this._includeErrorStack);
120
+ HttpErrorHelper.buildResponse(response, error, httpStatusCode);
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ //# sourceMappingURL=socketRouteProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socketRouteProcessor.js","sourceRoot":"","sources":["../../../src/data/socketRouteProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpErrorHelper,\n\ttype IHttpRequest,\n\ttype IHttpResponse,\n\ttype ISocketRequestContext,\n\ttype ISocketRoute,\n\ttype ISocketRouteProcessor,\n\ttype ISocketServerRequest\n} from \"@twin.org/api-models\";\nimport { Is, NotFoundError } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HttpStatusCode } from \"@twin.org/web\";\nimport type { ISocketRouteProcessorConstructorOptions } from \"../models/ISocketRouteProcessorConstructorOptions.js\";\n\n/**\n * Process the socket request and hands it on to the route handler.\n */\nexport class SocketRouteProcessor implements ISocketRouteProcessor {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<SocketRouteProcessor>();\n\n\t/**\n\t * Include the stack with errors.\n\t * @internal\n\t */\n\tprivate readonly _includeErrorStack: boolean;\n\n\t/**\n\t * Create a new instance of SocketRouteProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: ISocketRouteProcessorConstructorOptions) {\n\t\tthis._includeErrorStack = options?.config?.includeErrorStack ?? false;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn SocketRouteProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * Process the connected event.\n\t * @param request The server request object containing the socket id and other parameters.\n\t * @param route The route being requested, if a matching one was found.\n\t * @param loggingComponentType The logging component type for the request.\n\t * @returns Promise that resolves when the request is processed.\n\t */\n\tpublic async connected(\n\t\trequest: ISocketServerRequest,\n\t\troute: ISocketRoute | undefined,\n\t\tloggingComponentType?: string\n\t): Promise<void> {\n\t\tif (route?.connected) {\n\t\t\ttry {\n\t\t\t\tconst socketRequestContext: ISocketRequestContext = {\n\t\t\t\t\tsocketId: request.socketId,\n\t\t\t\t\tserverRequest: request,\n\t\t\t\t\tprocessorState: {},\n\t\t\t\t\tloggingComponentType\n\t\t\t\t};\n\n\t\t\t\troute.connected(socketRequestContext);\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\t/**\n\t * Process the disconnected event.\n\t * @param request The server request object containing the socket id and other parameters.\n\t * @param route The route being requested, if a matching one was found.\n\t * @param loggingComponentType The logging component type for the request.\n\t * @returns Promise that resolves when the request is processed.\n\t */\n\tpublic async disconnected(\n\t\trequest: ISocketServerRequest,\n\t\troute: ISocketRoute | undefined,\n\t\tloggingComponentType?: string\n\t): Promise<void> {\n\t\tif (route?.disconnected) {\n\t\t\ttry {\n\t\t\t\tconst socketRequestContext: ISocketRequestContext = {\n\t\t\t\t\tsocketId: request.socketId,\n\t\t\t\t\tserverRequest: request,\n\t\t\t\t\tprocessorState: {},\n\t\t\t\t\tloggingComponentType\n\t\t\t\t};\n\n\t\t\t\troute.disconnected(socketRequestContext);\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\t/**\n\t * Process the socket request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param processorState The state handed through the processors.\n\t * @param responseEmitter The function to emit a response.\n\t * @param loggingComponentType The logging component type for the request.\n\t */\n\tpublic async process(\n\t\trequest: ISocketServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: ISocketRoute | undefined,\n\t\tprocessorState: { [id: string]: unknown },\n\t\tresponseEmitter: (topic: string, response: IHttpResponse) => Promise<void>,\n\t\tloggingComponentType?: string\n\t): Promise<void> {\n\t\t// Don't handle the route if another processor has already set the response\n\t\t// status code e.g. from an auth processor\n\t\tif (Is.empty(response.statusCode)) {\n\t\t\tif (Is.empty(route)) {\n\t\t\t\tHttpErrorHelper.buildResponse(\n\t\t\t\t\tresponse,\n\t\t\t\t\t{\n\t\t\t\t\t\tname: NotFoundError.CLASS_NAME,\n\t\t\t\t\t\tmessage: `${SocketRouteProcessor.CLASS_NAME}.routeNotFound`,\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\tnotFoundId: request.url\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tHttpStatusCode.notFound\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tconst req: IHttpRequest = {\n\t\t\t\t\t\tpathParams: request.pathParams,\n\t\t\t\t\t\tquery: request.query,\n\t\t\t\t\t\tbody: request.body\n\t\t\t\t\t};\n\n\t\t\t\t\tconst socketRequestContext: ISocketRequestContext = {\n\t\t\t\t\t\tsocketId: request.socketId,\n\t\t\t\t\t\tserverRequest: request,\n\t\t\t\t\t\tprocessorState,\n\t\t\t\t\t\tloggingComponentType\n\t\t\t\t\t};\n\n\t\t\t\t\troute.handler(socketRequestContext, req, async (topic, socketRouteResponse) => {\n\t\t\t\t\t\tresponse.headers = socketRouteResponse?.headers;\n\t\t\t\t\t\tresponse.body = socketRouteResponse?.body;\n\t\t\t\t\t\tresponse.statusCode =\n\t\t\t\t\t\t\tsocketRouteResponse.statusCode ?? response.statusCode ?? HttpStatusCode.ok;\n\t\t\t\t\t\tawait responseEmitter(topic, response);\n\t\t\t\t\t});\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst { error, httpStatusCode } = HttpErrorHelper.processError(\n\t\t\t\t\t\terr,\n\t\t\t\t\t\tthis._includeErrorStack\n\t\t\t\t\t);\n\n\t\t\t\t\tHttpErrorHelper.buildResponse(response, error, httpStatusCode);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,68 @@
1
+ import { ContextIdStore, ContextIdHelper } from "@twin.org/context";
2
+ import { Guards, Is } from "@twin.org/core";
3
+ /**
4
+ * Adds an id to the request context ids.
5
+ */
6
+ export class ContextIdProcessor {
7
+ /**
8
+ * Runtime name for the class.
9
+ */
10
+ static CLASS_NAME = "ContextIdProcessor";
11
+ /**
12
+ * The fixed identity key for request context.
13
+ * @internal
14
+ */
15
+ _key;
16
+ /**
17
+ * Only add the identity if the request is authenticated.
18
+ * @internal
19
+ */
20
+ _authOnly;
21
+ /**
22
+ * The identity value for request context.
23
+ * @internal
24
+ */
25
+ _value;
26
+ /**
27
+ * Create a new instance of ContextIdProcessor.
28
+ * @param options Options for the processor.
29
+ */
30
+ constructor(options) {
31
+ Guards.object(ContextIdProcessor.CLASS_NAME, "options", options);
32
+ Guards.object(ContextIdProcessor.CLASS_NAME, "options.config", options.config);
33
+ Guards.stringValue(ContextIdProcessor.CLASS_NAME, "options.config.key", options.config.key);
34
+ this._key = options.config.key;
35
+ this._authOnly = options.config.authOnly ?? false;
36
+ }
37
+ /**
38
+ * Returns the class name of the component.
39
+ * @returns The class name of the component.
40
+ */
41
+ className() {
42
+ return ContextIdProcessor.CLASS_NAME;
43
+ }
44
+ /**
45
+ * The service needs to be started when the application is initialized.
46
+ * @param nodeLoggingComponentType The node logging component type.
47
+ * @returns Nothing.
48
+ */
49
+ async start(nodeLoggingComponentType) {
50
+ const contextIds = await ContextIdStore.getContextIds();
51
+ ContextIdHelper.guard(contextIds, this._key);
52
+ this._value = contextIds[this._key];
53
+ }
54
+ /**
55
+ * Pre process the REST request for the specified route.
56
+ * @param request The incoming request.
57
+ * @param response The outgoing response.
58
+ * @param route The route to process.
59
+ * @param contextIds The context IDs of the request.
60
+ * @param processorState The state handed through the processors.
61
+ */
62
+ async pre(request, response, route, contextIds, processorState) {
63
+ if (!this._authOnly || (!Is.empty(route) && !(route.skipAuth ?? false))) {
64
+ contextIds[this._key] = this._value;
65
+ }
66
+ }
67
+ }
68
+ //# sourceMappingURL=contextIdProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextIdProcessor.js","sourceRoot":"","sources":["../../../src/identity/contextIdProcessor.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAoB,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAK5C;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAC9B;;OAEG;IACI,MAAM,CAAU,UAAU,wBAAwC;IAEzE;;;OAGG;IACc,IAAI,CAAS;IAE9B;;;OAGG;IACc,SAAS,CAAU;IAEpC;;;OAGG;IACK,MAAM,CAAU;IAExB;;;OAGG;IACH,YAAY,OAA8C;QACzD,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QACvE,MAAM,CAAC,MAAM,CACZ,kBAAkB,CAAC,UAAU,oBAE7B,OAAO,CAAC,MAAM,CACd,CAAC;QACF,MAAM,CAAC,WAAW,CACjB,kBAAkB,CAAC,UAAU,wBAE7B,OAAO,CAAC,MAAM,CAAC,GAAG,CAClB,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;QAC/B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC;IACnD,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,kBAAkB,CAAC,UAAU,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,KAAK,CAAC,wBAAiC;QACnD,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACxD,eAAe,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,GAAG,CACf,OAA2B,EAC3B,QAAuB,EACvB,KAA6B,EAC7B,UAAuB,EACvB,cAAyC;QAEzC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC,EAAE,CAAC;YACzE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACrC,CAAC;IACF,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIBaseRoute,\n\tIBaseRouteProcessor,\n\tIHttpResponse,\n\tIHttpServerRequest\n} from \"@twin.org/api-models\";\nimport { ContextIdStore, ContextIdHelper, type IContextIds } from \"@twin.org/context\";\nimport { Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IContextIdProcessorConfig } from \"../models/IContextIdProcessorConfig.js\";\nimport type { IContextIdProcessorConstructorOptions } from \"../models/IContextIdProcessorConstructorOptions.js\";\n\n/**\n * Adds an id to the request context ids.\n */\nexport class ContextIdProcessor implements IBaseRouteProcessor {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<ContextIdProcessor>();\n\n\t/**\n\t * The fixed identity key for request context.\n\t * @internal\n\t */\n\tprivate readonly _key: string;\n\n\t/**\n\t * Only add the identity if the request is authenticated.\n\t * @internal\n\t */\n\tprivate readonly _authOnly: boolean;\n\n\t/**\n\t * The identity value for request context.\n\t * @internal\n\t */\n\tprivate _value?: string;\n\n\t/**\n\t * Create a new instance of ContextIdProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options: IContextIdProcessorConstructorOptions) {\n\t\tGuards.object(ContextIdProcessor.CLASS_NAME, nameof(options), options);\n\t\tGuards.object<IContextIdProcessorConfig>(\n\t\t\tContextIdProcessor.CLASS_NAME,\n\t\t\tnameof(options.config),\n\t\t\toptions.config\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tContextIdProcessor.CLASS_NAME,\n\t\t\tnameof(options.config.key),\n\t\t\toptions.config.key\n\t\t);\n\t\tthis._key = options.config.key;\n\t\tthis._authOnly = options.config.authOnly ?? false;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn ContextIdProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * The service needs to be started when the application is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, this._key);\n\t\tthis._value = contextIds[this._key];\n\t}\n\n\t/**\n\t * Pre process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t */\n\tpublic async pre(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tif (!this._authOnly || (!Is.empty(route) && !(route.skipAuth ?? false))) {\n\t\t\tcontextIds[this._key] = this._value;\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,59 @@
1
+ import { Guards, Is } from "@twin.org/core";
2
+ /**
3
+ * Adds a static context id to the request context.
4
+ */
5
+ export class StaticContextIdProcessor {
6
+ /**
7
+ * Runtime name for the class.
8
+ */
9
+ static CLASS_NAME = "StaticContextIdProcessor";
10
+ /**
11
+ * The fixed identity key for request context.
12
+ * @internal
13
+ */
14
+ _key;
15
+ /**
16
+ * The fixed identity value for request context.
17
+ * @internal
18
+ */
19
+ _value;
20
+ /**
21
+ * Only add the identity if the request is authenticated.
22
+ * @internal
23
+ */
24
+ _authOnly;
25
+ /**
26
+ * Create a new instance of StaticContextIdProcessor.
27
+ * @param options Options for the processor.
28
+ */
29
+ constructor(options) {
30
+ Guards.object(StaticContextIdProcessor.CLASS_NAME, "options", options);
31
+ Guards.object(StaticContextIdProcessor.CLASS_NAME, "options.config", options.config);
32
+ Guards.stringValue(StaticContextIdProcessor.CLASS_NAME, "options.config.key", options.config.key);
33
+ Guards.stringValue(StaticContextIdProcessor.CLASS_NAME, "options.config.value", options.config.value);
34
+ this._key = options.config.key;
35
+ this._value = options.config.value;
36
+ this._authOnly = options.config.authOnly ?? false;
37
+ }
38
+ /**
39
+ * Returns the class name of the component.
40
+ * @returns The class name of the component.
41
+ */
42
+ className() {
43
+ return StaticContextIdProcessor.CLASS_NAME;
44
+ }
45
+ /**
46
+ * Pre process the REST request for the specified route.
47
+ * @param request The incoming request.
48
+ * @param response The outgoing response.
49
+ * @param route The route to process.
50
+ * @param contextIds The context IDs of the request.
51
+ * @param processorState The state handed through the processors.
52
+ */
53
+ async pre(request, response, route, contextIds, processorState) {
54
+ if (!this._authOnly || (!Is.empty(route) && !(route.skipAuth ?? false))) {
55
+ contextIds[this._key] = this._value;
56
+ }
57
+ }
58
+ }
59
+ //# sourceMappingURL=staticContextIdProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staticContextIdProcessor.js","sourceRoot":"","sources":["../../../src/identity/staticContextIdProcessor.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAK5C;;GAEG;AACH,MAAM,OAAO,wBAAwB;IACpC;;OAEG;IACI,MAAM,CAAU,UAAU,8BAA8C;IAE/E;;;OAGG;IACc,IAAI,CAAS;IAE9B;;;OAGG;IACc,MAAM,CAAS;IAEhC;;;OAGG;IACc,SAAS,CAAU;IAEpC;;;OAGG;IACH,YAAY,OAAoD;QAC/D,MAAM,CAAC,MAAM,CAAC,wBAAwB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QAC7E,MAAM,CAAC,MAAM,CACZ,wBAAwB,CAAC,UAAU,oBAEnC,OAAO,CAAC,MAAM,CACd,CAAC;QACF,MAAM,CAAC,WAAW,CACjB,wBAAwB,CAAC,UAAU,wBAEnC,OAAO,CAAC,MAAM,CAAC,GAAG,CAClB,CAAC;QACF,MAAM,CAAC,WAAW,CACjB,wBAAwB,CAAC,UAAU,0BAEnC,OAAO,CAAC,MAAM,CAAC,KAAK,CACpB,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;QACnC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC;IACnD,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,wBAAwB,CAAC,UAAU,CAAC;IAC5C,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,GAAG,CACf,OAA2B,EAC3B,QAAuB,EACvB,KAA6B,EAC7B,UAAuB,EACvB,cAAyC;QAEzC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC,EAAE,CAAC;YACzE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACrC,CAAC;IACF,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIBaseRoute,\n\tIBaseRouteProcessor,\n\tIHttpResponse,\n\tIHttpServerRequest\n} from \"@twin.org/api-models\";\nimport type { IContextIds } from \"@twin.org/context\";\nimport { Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IStaticContextIdProcessorConfig } from \"../models/IStaticContextIdProcessorConfig.js\";\nimport type { IStaticContextIdProcessorConstructorOptions } from \"../models/IStaticContextIdProcessorConstructorOptions.js\";\n\n/**\n * Adds a static context id to the request context.\n */\nexport class StaticContextIdProcessor implements IBaseRouteProcessor {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<StaticContextIdProcessor>();\n\n\t/**\n\t * The fixed identity key for request context.\n\t * @internal\n\t */\n\tprivate readonly _key: string;\n\n\t/**\n\t * The fixed identity value for request context.\n\t * @internal\n\t */\n\tprivate readonly _value: string;\n\n\t/**\n\t * Only add the identity if the request is authenticated.\n\t * @internal\n\t */\n\tprivate readonly _authOnly: boolean;\n\n\t/**\n\t * Create a new instance of StaticContextIdProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options: IStaticContextIdProcessorConstructorOptions) {\n\t\tGuards.object(StaticContextIdProcessor.CLASS_NAME, nameof(options), options);\n\t\tGuards.object<IStaticContextIdProcessorConfig>(\n\t\t\tStaticContextIdProcessor.CLASS_NAME,\n\t\t\tnameof(options.config),\n\t\t\toptions.config\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tStaticContextIdProcessor.CLASS_NAME,\n\t\t\tnameof(options.config.key),\n\t\t\toptions.config.key\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tStaticContextIdProcessor.CLASS_NAME,\n\t\t\tnameof(options.config.value),\n\t\t\toptions.config.value\n\t\t);\n\t\tthis._key = options.config.key;\n\t\tthis._value = options.config.value;\n\t\tthis._authOnly = options.config.authOnly ?? false;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn StaticContextIdProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * Pre process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t */\n\tpublic async pre(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tif (!this._authOnly || (!Is.empty(route) && !(route.skipAuth ?? false))) {\n\t\t\tcontextIds[this._key] = this._value;\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,19 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export * from "./data/restRouteProcessor.js";
4
+ export * from "./data/socketRouteProcessor.js";
5
+ export * from "./identity/contextIdProcessor.js";
6
+ export * from "./identity/staticContextIdProcessor.js";
7
+ export * from "./logging/loggingProcessor.js";
8
+ export * from "./mimeType/jsonLdMimeTypeProcessor.js";
9
+ export * from "./mimeType/jwtMimeTypeProcessor.js";
10
+ export * from "./models/IContextIdProcessorConfig.js";
11
+ export * from "./models/IContextIdProcessorConstructorOptions.js";
12
+ export * from "./models/ILoggingProcessorConfig.js";
13
+ export * from "./models/ILoggingProcessorConstructorOptions.js";
14
+ export * from "./models/IRestRouteProcessorConstructorOptions.js";
15
+ export * from "./models/IRouteProcessorConfig.js";
16
+ export * from "./models/ISocketRouteProcessorConstructorOptions.js";
17
+ export * from "./models/IStaticContextIdProcessorConfig.js";
18
+ export * from "./models/IStaticContextIdProcessorConstructorOptions.js";
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,kCAAkC,CAAC;AACjD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,mDAAmD,CAAC;AAClE,cAAc,qCAAqC,CAAC;AACpD,cAAc,iDAAiD,CAAC;AAChE,cAAc,mDAAmD,CAAC;AAClE,cAAc,mCAAmC,CAAC;AAClD,cAAc,qDAAqD,CAAC;AACpE,cAAc,6CAA6C,CAAC;AAC5D,cAAc,yDAAyD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./data/restRouteProcessor.js\";\nexport * from \"./data/socketRouteProcessor.js\";\nexport * from \"./identity/contextIdProcessor.js\";\nexport * from \"./identity/staticContextIdProcessor.js\";\nexport * from \"./logging/loggingProcessor.js\";\nexport * from \"./mimeType/jsonLdMimeTypeProcessor.js\";\nexport * from \"./mimeType/jwtMimeTypeProcessor.js\";\nexport * from \"./models/IContextIdProcessorConfig.js\";\nexport * from \"./models/IContextIdProcessorConstructorOptions.js\";\nexport * from \"./models/ILoggingProcessorConfig.js\";\nexport * from \"./models/ILoggingProcessorConstructorOptions.js\";\nexport * from \"./models/IRestRouteProcessorConstructorOptions.js\";\nexport * from \"./models/IRouteProcessorConfig.js\";\nexport * from \"./models/ISocketRouteProcessorConstructorOptions.js\";\nexport * from \"./models/IStaticContextIdProcessorConfig.js\";\nexport * from \"./models/IStaticContextIdProcessorConstructorOptions.js\";\n"]}
@@ -0,0 +1,177 @@
1
+ import { Coerce, ComponentFactory, Is, ObjectHelper } from "@twin.org/core";
2
+ import { HeaderTypes, HttpStatusCode, MimeTypes } from "@twin.org/web";
3
+ /**
4
+ * Process the REST request and log its information.
5
+ */
6
+ export class LoggingProcessor {
7
+ /**
8
+ * Runtime name for the class.
9
+ */
10
+ static CLASS_NAME = "LoggingProcessor";
11
+ /**
12
+ * The component for logging the information.
13
+ * @internal
14
+ */
15
+ _logging;
16
+ /**
17
+ * Include the body objects when logging the information.
18
+ * @internal
19
+ */
20
+ _includeBody;
21
+ /**
22
+ * Show the full base64 content for data, default to abbreviate.
23
+ * @internal
24
+ */
25
+ _fullBase64;
26
+ /**
27
+ * List of property names to obfuscate, defaults to "password".
28
+ * @internal
29
+ */
30
+ _obfuscateProperties;
31
+ /**
32
+ * Create a new instance of LoggingProcessor.
33
+ * @param options Options for the processor.
34
+ */
35
+ constructor(options) {
36
+ this._logging = ComponentFactory.getIfExists(options?.loggingComponentType ?? "logging");
37
+ this._includeBody = options?.config?.includeBody ?? false;
38
+ this._fullBase64 = options?.config?.fullBase64 ?? false;
39
+ this._obfuscateProperties = options?.config?.obfuscateProperties ?? ["password"];
40
+ }
41
+ /**
42
+ * Returns the class name of the component.
43
+ * @returns The class name of the component.
44
+ */
45
+ className() {
46
+ return LoggingProcessor.CLASS_NAME;
47
+ }
48
+ /**
49
+ * Pre process the REST request for the specified route.
50
+ * @param request The incoming request.
51
+ * @param response The outgoing response.
52
+ * @param route The route to process.
53
+ * @param contextIds The context IDs of the request.
54
+ * @param processorState The state handed through the processors.
55
+ */
56
+ async pre(request, response, route, contextIds, processorState) {
57
+ const now = process.hrtime.bigint();
58
+ processorState.requestStart = now;
59
+ const contentType = request.headers?.[HeaderTypes.ContentType];
60
+ const isJson = this.isMimeJson(contentType);
61
+ let requestUrl = "";
62
+ if (Is.stringValue(request.url)) {
63
+ // Socket paths do not have a prefix so just use the whole url.
64
+ if (request.url.startsWith("http")) {
65
+ requestUrl = new URL(request.url).pathname;
66
+ }
67
+ else {
68
+ requestUrl = request.url;
69
+ }
70
+ }
71
+ await this._logging?.log({
72
+ level: "info",
73
+ source: LoggingProcessor.CLASS_NAME,
74
+ ts: Date.now(),
75
+ message: `===> ${request.method} ${requestUrl}`,
76
+ data: this._includeBody && isJson
77
+ ? this.processJson("body", ObjectHelper.clone(request?.body))
78
+ : undefined
79
+ });
80
+ }
81
+ /**
82
+ * Post process the REST request for the specified route.
83
+ * @param request The incoming request.
84
+ * @param response The outgoing response.
85
+ * @param route The route to process.
86
+ * @param contextIds The context IDs of the request.
87
+ * @param processorState The state handed through the processors.
88
+ */
89
+ async post(request, response, route, contextIds, processorState) {
90
+ let data;
91
+ if (this._includeBody) {
92
+ const contentType = response.headers?.[HeaderTypes.ContentType];
93
+ const isJson = this.isMimeJson(contentType);
94
+ const contentLength = response.headers?.[HeaderTypes.ContentLength];
95
+ if (isJson) {
96
+ data = {
97
+ body: this.processJson("body", ObjectHelper.clone(response.body))
98
+ };
99
+ }
100
+ else {
101
+ const dataParts = {};
102
+ if (Is.stringValue(contentType)) {
103
+ dataParts["Content Type"] = contentType;
104
+ }
105
+ if (Is.stringValue(contentLength)) {
106
+ dataParts["Content Length"] = contentLength;
107
+ }
108
+ if (Object.keys(dataParts).length > 0) {
109
+ data = {
110
+ headers: dataParts
111
+ };
112
+ }
113
+ }
114
+ }
115
+ const now = process.hrtime.bigint();
116
+ const start = Coerce.bigint(processorState.requestStart) ?? now;
117
+ const elapsed = now - start;
118
+ const elapsedMicroSeconds = Math.floor(Number(elapsed) / 1000);
119
+ let requestUrl = "";
120
+ if (Is.stringValue(request.url)) {
121
+ // Socket paths do not have a prefix so just use the whole url.
122
+ if (request.url.startsWith("http")) {
123
+ requestUrl = new URL(request.url).pathname;
124
+ }
125
+ else {
126
+ requestUrl = request.url;
127
+ }
128
+ }
129
+ await this._logging?.log({
130
+ level: Is.number(response.statusCode) && response.statusCode >= HttpStatusCode.badRequest
131
+ ? "error"
132
+ : "info",
133
+ source: LoggingProcessor.CLASS_NAME,
134
+ ts: Date.now(),
135
+ message: `<=== ${response.statusCode ?? ""} ${request.method} ${requestUrl} duration: ${elapsedMicroSeconds}µs`,
136
+ data
137
+ });
138
+ }
139
+ /**
140
+ * Process the JSON.
141
+ * @param propValue The property to process.
142
+ * @returns The processed property.
143
+ * @internal
144
+ */
145
+ processJson(propName, propValue) {
146
+ if (Is.array(propValue)) {
147
+ for (let i = 0; i < propValue.length; i++) {
148
+ propValue[i] = this.processJson(`${i}`, propValue[i]);
149
+ }
150
+ }
151
+ else if (Is.object(propValue)) {
152
+ for (const key of Object.keys(propValue)) {
153
+ propValue[key] = this.processJson(key, propValue[key]);
154
+ }
155
+ }
156
+ else if (!this._fullBase64 && Is.stringBase64(propValue) && propValue.length > 256) {
157
+ propValue = "<base64>";
158
+ }
159
+ else if (Is.string(propValue) &&
160
+ this._obfuscateProperties.some(op => new RegExp(op).test(propName))) {
161
+ propValue = "**************";
162
+ }
163
+ return propValue;
164
+ }
165
+ /**
166
+ * Check if the content type is JSON.
167
+ * @param contentType The content type to check.
168
+ * @returns True if the content type is JSON, false otherwise.
169
+ * @internal
170
+ */
171
+ isMimeJson(contentType) {
172
+ return Is.stringValue(contentType)
173
+ ? contentType.includes(MimeTypes.Json) || contentType.includes(MimeTypes.JsonLd)
174
+ : false;
175
+ }
176
+ }
177
+ //# sourceMappingURL=loggingProcessor.js.map