@twin.org/api-server-fastify 0.0.1 → 0.0.2-next.10

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.
@@ -3,8 +3,8 @@
3
3
  var FastifyCompress = require('@fastify/compress');
4
4
  var FastifyCors = require('@fastify/cors');
5
5
  var apiModels = require('@twin.org/api-models');
6
+ var apiProcessors = require('@twin.org/api-processors');
6
7
  var core = require('@twin.org/core');
7
- var loggingModels = require('@twin.org/logging-models');
8
8
  var web = require('@twin.org/web');
9
9
  var Fastify = require('fastify');
10
10
  var fp = require('fastify-plugin');
@@ -47,10 +47,15 @@ class FastifyWebServer {
47
47
  */
48
48
  CLASS_NAME = "FastifyWebServer";
49
49
  /**
50
- * The logging connector.
50
+ * The logging component type.
51
51
  * @internal
52
52
  */
53
- _loggingConnector;
53
+ _loggingComponentType;
54
+ /**
55
+ * The logging component.
56
+ * @internal
57
+ */
58
+ _logging;
54
59
  /**
55
60
  * The options for the server.
56
61
  * @internal
@@ -86,12 +91,15 @@ class FastifyWebServer {
86
91
  * @param options The options for the server.
87
92
  */
88
93
  constructor(options) {
89
- this._loggingConnector = core.Is.stringValue(options?.loggingConnectorType)
90
- ? loggingModels.LoggingConnectorFactory.get(options.loggingConnectorType)
91
- : undefined;
94
+ this._loggingComponentType = options?.loggingComponentType;
95
+ this._logging = core.ComponentFactory.getIfExists(options?.loggingComponentType ?? "logging");
92
96
  this._fastify = Fastify({
93
- maxParamLength: 2000,
97
+ routerOptions: {
98
+ maxParamLength: 2000
99
+ },
94
100
  ...options?.config?.web
101
+ // Need this cast for now as maxParamLength has moved in to routerOptions
102
+ // but the TS defs has not been updated yet
95
103
  });
96
104
  this._socketConfig = {
97
105
  path: "/socket",
@@ -99,6 +107,10 @@ class FastifyWebServer {
99
107
  };
100
108
  this._started = false;
101
109
  this._mimeTypeProcessors = options?.mimeTypeProcessors ?? [];
110
+ const hasJsonLd = this._mimeTypeProcessors.find(processor => processor.CLASS_NAME === "json-ld");
111
+ if (!hasJsonLd) {
112
+ this._mimeTypeProcessors.push(new apiProcessors.JsonLdMimeTypeProcessor());
113
+ }
102
114
  this._includeErrorStack = options?.config?.includeErrorStack ?? false;
103
115
  }
104
116
  /**
@@ -124,7 +136,7 @@ class FastifyWebServer {
124
136
  if (core.Is.arrayValue(socketRoutes) && !core.Is.arrayValue(socketRouteProcessors)) {
125
137
  throw new core.GeneralError(this.CLASS_NAME, "noSocketProcessors");
126
138
  }
127
- await this._loggingConnector?.log({
139
+ await this._logging?.log({
128
140
  level: "info",
129
141
  ts: Date.now(),
130
142
  source: this.CLASS_NAME,
@@ -169,7 +181,7 @@ class FastifyWebServer {
169
181
  err = errorAndCode.error;
170
182
  httpStatusCode = errorAndCode.httpStatusCode;
171
183
  }
172
- await this._loggingConnector?.log({
184
+ await this._logging?.log({
173
185
  level: "error",
174
186
  ts: Date.now(),
175
187
  source: this.CLASS_NAME,
@@ -190,7 +202,7 @@ class FastifyWebServer {
190
202
  async start() {
191
203
  const host = this._options?.host ?? FastifyWebServer._DEFAULT_HOST;
192
204
  const port = this._options?.port ?? FastifyWebServer._DEFAULT_PORT;
193
- await this._loggingConnector?.log({
205
+ await this._logging?.log({
194
206
  level: "info",
195
207
  ts: Date.now(),
196
208
  source: this.CLASS_NAME,
@@ -205,7 +217,7 @@ class FastifyWebServer {
205
217
  await this._fastify.listen({ port, host });
206
218
  const addresses = this._fastify.addresses();
207
219
  const protocol = core.Is.object(this._fastify.initialConfig.https) ? "https://" : "http://";
208
- await this._loggingConnector?.log({
220
+ await this._logging?.log({
209
221
  level: "info",
210
222
  ts: Date.now(),
211
223
  source: this.CLASS_NAME,
@@ -219,7 +231,7 @@ class FastifyWebServer {
219
231
  this._started = true;
220
232
  }
221
233
  catch (err) {
222
- await this._loggingConnector?.log({
234
+ await this._logging?.log({
223
235
  level: "error",
224
236
  ts: Date.now(),
225
237
  source: this.CLASS_NAME,
@@ -237,7 +249,7 @@ class FastifyWebServer {
237
249
  if (this._started) {
238
250
  this._started = false;
239
251
  await this._fastify.close();
240
- await this._loggingConnector?.log({
252
+ await this._logging?.log({
241
253
  level: "info",
242
254
  ts: Date.now(),
243
255
  source: this.CLASS_NAME,
@@ -258,7 +270,7 @@ class FastifyWebServer {
258
270
  if (!path.startsWith("/")) {
259
271
  path = `/${path}`;
260
272
  }
261
- await this._loggingConnector?.log({
273
+ await this._logging?.log({
262
274
  level: "info",
263
275
  ts: Date.now(),
264
276
  source: this.CLASS_NAME,
@@ -283,30 +295,33 @@ class FastifyWebServer {
283
295
  for (const socketRoute of socketRoutes) {
284
296
  const path = core.StringHelper.trimLeadingSlashes(core.StringHelper.trimTrailingSlashes(socketRoute.path));
285
297
  const pathParts = path.split("/");
286
- await this._loggingConnector?.log({
298
+ const namespace = `/${pathParts[0]}`;
299
+ const topic = pathParts.slice(1).join("/");
300
+ await this._logging?.log({
287
301
  level: "info",
288
302
  ts: Date.now(),
289
303
  source: this.CLASS_NAME,
290
304
  message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.socketRouteAdded`,
291
- data: { route: `/${path}` }
305
+ data: {
306
+ handshakePath: this._socketConfig.path,
307
+ namespace,
308
+ eventName: topic
309
+ }
292
310
  });
293
- const socketNamespace = io.of(`/${pathParts[0]}`);
294
- const topic = pathParts.slice(1).join("/");
311
+ const socketNamespace = io.of(namespace);
295
312
  socketNamespace.on("connection", async (socket) => {
296
- const httpServerRequest = {
313
+ const socketServerRequest = {
297
314
  method: web.HttpMethod.GET,
298
315
  url: socket.handshake.url,
299
316
  query: socket.handshake.query,
300
- headers: socket.handshake.headers
317
+ headers: socket.handshake.headers,
318
+ socketId: socket.id
301
319
  };
302
320
  // Pass the connected information on to any processors
303
321
  try {
304
- const processorState = {
305
- socketId: socket.id
306
- };
307
322
  for (const socketRouteProcessor of socketRouteProcessors) {
308
- if (core.Is.function(socketRouteProcessor.connected)) {
309
- await socketRouteProcessor.connected(httpServerRequest, socketRoute, processorState);
323
+ if (socketRouteProcessor.connected) {
324
+ await socketRouteProcessor.connected(socketServerRequest, socketRoute, this._loggingComponentType);
310
325
  }
311
326
  }
312
327
  }
@@ -318,13 +333,10 @@ class FastifyWebServer {
318
333
  }
319
334
  socket.on("disconnect", async () => {
320
335
  try {
321
- const processorState = {
322
- socketId: socket.id
323
- };
324
336
  // The socket disconnected so notify any processors
325
337
  for (const socketRouteProcessor of socketRouteProcessors) {
326
- if (core.Is.function(socketRouteProcessor.disconnected)) {
327
- await socketRouteProcessor.disconnected(httpServerRequest, socketRoute, processorState);
338
+ if (socketRouteProcessor.disconnected) {
339
+ await socketRouteProcessor.disconnected(socketServerRequest, socketRoute, this._loggingComponentType);
328
340
  }
329
341
  }
330
342
  }
@@ -381,19 +393,20 @@ class FastifyWebServer {
381
393
  */
382
394
  async runProcessorsRest(restRouteProcessors, restRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState) {
383
395
  try {
384
- for (const routeProcessor of restRouteProcessors) {
385
- if (core.Is.function(routeProcessor.pre)) {
386
- await routeProcessor.pre(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
396
+ const filteredProcessors = this.filterRouteProcessors(restRoute, restRouteProcessors);
397
+ for (const routeProcessor of filteredProcessors) {
398
+ if (routeProcessor.pre) {
399
+ await routeProcessor.pre(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState, this._loggingComponentType);
387
400
  }
388
401
  }
389
- for (const routeProcessor of restRouteProcessors) {
390
- if (core.Is.function(routeProcessor.process)) {
391
- await routeProcessor.process(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
402
+ for (const routeProcessor of filteredProcessors) {
403
+ if (routeProcessor.process) {
404
+ await routeProcessor.process(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState, this._loggingComponentType);
392
405
  }
393
406
  }
394
- for (const routeProcessor of restRouteProcessors) {
395
- if (core.Is.function(routeProcessor.post)) {
396
- await routeProcessor.post(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
407
+ for (const routeProcessor of filteredProcessors) {
408
+ if (routeProcessor.post) {
409
+ await routeProcessor.post(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState, this._loggingComponentType);
397
410
  }
398
411
  }
399
412
  }
@@ -402,6 +415,33 @@ class FastifyWebServer {
402
415
  apiModels.HttpErrorHelper.buildResponse(httpResponse, error, httpStatusCode);
403
416
  }
404
417
  }
418
+ /**
419
+ * Filter the route processors based on the requested features.
420
+ * @param route The route to process.
421
+ * @param routeProcessors The processors to filter.
422
+ * @returns The filtered list of route processor.
423
+ * @internal
424
+ */
425
+ filterRouteProcessors(route, routeProcessors) {
426
+ const requestedFeatures = route?.processorFeatures ?? [];
427
+ if (!core.Is.arrayValue(requestedFeatures)) {
428
+ // If there are no requested features, we just return all the processors
429
+ return routeProcessors;
430
+ }
431
+ // Reduce the list of route processors to just those in the requested features list
432
+ const reducedProcessors = routeProcessors.filter(routeProcessor => {
433
+ // Processors that do not define any features always get run
434
+ // If the route processor has features defined, then we only run it
435
+ // if the route has at least one of those features required
436
+ let runRouteProcessor = true;
437
+ if (routeProcessor.features) {
438
+ const routeProcessorFeatures = routeProcessor.features();
439
+ runRouteProcessor = routeProcessorFeatures.some(feature => requestedFeatures.includes(feature));
440
+ }
441
+ return runRouteProcessor;
442
+ });
443
+ return reducedProcessors;
444
+ }
405
445
  /**
406
446
  * Handle the incoming socket request.
407
447
  * @param socketRouteProcessors The hooks to process the incoming requests.
@@ -413,49 +453,50 @@ class FastifyWebServer {
413
453
  * @internal
414
454
  */
415
455
  async handleRequestSocket(socketRouteProcessors, socketRoute, socket, fullPath, emitTopic, request) {
416
- const httpServerRequest = {
456
+ const socketServerRequest = {
417
457
  method: web.HttpMethod.GET,
418
458
  url: fullPath,
419
459
  query: socket.handshake.query,
420
460
  headers: socket.handshake.headers,
421
- body: request.body
461
+ body: request.body,
462
+ socketId: socket.id
422
463
  };
423
464
  const httpResponse = {};
424
465
  const httpRequestIdentity = {};
425
- const processorState = {
426
- socketId: socket.id
427
- };
428
- delete httpServerRequest.query?.EIO;
429
- delete httpServerRequest.query?.transport;
430
- await this.runProcessorsSocket(socketRouteProcessors, socketRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState, emitTopic, async (topic, response) => {
466
+ const processorState = {};
467
+ delete socketServerRequest.query?.EIO;
468
+ delete socketServerRequest.query?.transport;
469
+ await this.runProcessorsSocket(socketRouteProcessors, socketRoute, socketServerRequest, httpResponse, httpRequestIdentity, processorState, emitTopic, async (topic, response) => {
431
470
  await socket.emit(topic, response);
432
471
  });
433
472
  }
434
473
  /**
435
474
  * Run the socket processors for the route.
475
+ * @param socketId The id of the socket.
436
476
  * @param socketRouteProcessors The processors to run.
437
477
  * @param socketRoute The route to process.
438
- * @param httpServerRequest The incoming request.
478
+ * @param socketServerRequest The incoming request.
439
479
  * @param httpResponse The outgoing response.
440
480
  * @param httpRequestIdentity The identity context for the request.
441
481
  * @param processorState The state handed through the processors.
442
482
  * @param requestTopic The topic of the request.
443
483
  * @internal
444
484
  */
445
- async runProcessorsSocket(socketRouteProcessors, socketRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState, requestTopic, responseEmitter) {
485
+ async runProcessorsSocket(socketRouteProcessors, socketRoute, socketServerRequest, httpResponse, httpRequestIdentity, processorState, requestTopic, responseEmitter) {
486
+ const filteredProcessors = this.filterRouteProcessors(socketRoute, socketRouteProcessors);
446
487
  // Custom emit method which will also call the post processors
447
488
  const postProcessEmit = async (topic, response, responseProcessorState) => {
448
489
  await responseEmitter(topic, response);
449
490
  try {
450
491
  // The post processors are called after the response has been emitted
451
- for (const postSocketRouteProcessor of socketRouteProcessors) {
452
- if (core.Is.function(postSocketRouteProcessor.post)) {
453
- await postSocketRouteProcessor.post(httpServerRequest, response, socketRoute, httpRequestIdentity, responseProcessorState);
492
+ for (const postSocketRouteProcessor of filteredProcessors) {
493
+ if (postSocketRouteProcessor.post) {
494
+ await postSocketRouteProcessor.post(socketServerRequest, response, socketRoute, httpRequestIdentity, responseProcessorState, this._loggingComponentType);
454
495
  }
455
496
  }
456
497
  }
457
498
  catch (err) {
458
- this._loggingConnector?.log({
499
+ this._logging?.log({
459
500
  level: "error",
460
501
  ts: Date.now(),
461
502
  source: this.CLASS_NAME,
@@ -468,9 +509,9 @@ class FastifyWebServer {
468
509
  }
469
510
  };
470
511
  try {
471
- for (const socketRouteProcessor of socketRouteProcessors) {
472
- if (core.Is.function(socketRouteProcessor.pre)) {
473
- await socketRouteProcessor.pre(httpServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState);
512
+ for (const socketRouteProcessor of filteredProcessors) {
513
+ if (socketRouteProcessor.pre) {
514
+ await socketRouteProcessor.pre(socketServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState, this._loggingComponentType);
474
515
  }
475
516
  }
476
517
  // We always call all the processors regardless of any response set by a previous processor.
@@ -479,11 +520,11 @@ class FastifyWebServer {
479
520
  if (!core.Is.empty(httpResponse.statusCode)) {
480
521
  await postProcessEmit(requestTopic, httpResponse, processorState);
481
522
  }
482
- for (const socketRouteProcessor of socketRouteProcessors) {
483
- if (core.Is.function(socketRouteProcessor.process)) {
484
- await socketRouteProcessor.process(httpServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState, async (topic, processResponse) => {
523
+ for (const socketRouteProcessor of filteredProcessors) {
524
+ if (socketRouteProcessor.process) {
525
+ await socketRouteProcessor.process(socketServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState, async (topic, processResponse) => {
485
526
  await postProcessEmit(topic, processResponse, processorState);
486
- });
527
+ }, this._loggingComponentType);
487
528
  }
488
529
  }
489
530
  // If the processors set the status to any kind of error then we should emit this manually
@@ -1,8 +1,8 @@
1
1
  import FastifyCompress from '@fastify/compress';
2
2
  import FastifyCors from '@fastify/cors';
3
3
  import { HttpErrorHelper } from '@twin.org/api-models';
4
- import { StringHelper, Is, GeneralError, BaseError } from '@twin.org/core';
5
- import { LoggingConnectorFactory } from '@twin.org/logging-models';
4
+ import { JsonLdMimeTypeProcessor } from '@twin.org/api-processors';
5
+ import { StringHelper, ComponentFactory, Is, GeneralError, BaseError } from '@twin.org/core';
6
6
  import { HttpStatusCode, HttpMethod, HeaderTypes } from '@twin.org/web';
7
7
  import Fastify from 'fastify';
8
8
  import fp from 'fastify-plugin';
@@ -45,10 +45,15 @@ class FastifyWebServer {
45
45
  */
46
46
  CLASS_NAME = "FastifyWebServer";
47
47
  /**
48
- * The logging connector.
48
+ * The logging component type.
49
49
  * @internal
50
50
  */
51
- _loggingConnector;
51
+ _loggingComponentType;
52
+ /**
53
+ * The logging component.
54
+ * @internal
55
+ */
56
+ _logging;
52
57
  /**
53
58
  * The options for the server.
54
59
  * @internal
@@ -84,12 +89,15 @@ class FastifyWebServer {
84
89
  * @param options The options for the server.
85
90
  */
86
91
  constructor(options) {
87
- this._loggingConnector = Is.stringValue(options?.loggingConnectorType)
88
- ? LoggingConnectorFactory.get(options.loggingConnectorType)
89
- : undefined;
92
+ this._loggingComponentType = options?.loggingComponentType;
93
+ this._logging = ComponentFactory.getIfExists(options?.loggingComponentType ?? "logging");
90
94
  this._fastify = Fastify({
91
- maxParamLength: 2000,
95
+ routerOptions: {
96
+ maxParamLength: 2000
97
+ },
92
98
  ...options?.config?.web
99
+ // Need this cast for now as maxParamLength has moved in to routerOptions
100
+ // but the TS defs has not been updated yet
93
101
  });
94
102
  this._socketConfig = {
95
103
  path: "/socket",
@@ -97,6 +105,10 @@ class FastifyWebServer {
97
105
  };
98
106
  this._started = false;
99
107
  this._mimeTypeProcessors = options?.mimeTypeProcessors ?? [];
108
+ const hasJsonLd = this._mimeTypeProcessors.find(processor => processor.CLASS_NAME === "json-ld");
109
+ if (!hasJsonLd) {
110
+ this._mimeTypeProcessors.push(new JsonLdMimeTypeProcessor());
111
+ }
100
112
  this._includeErrorStack = options?.config?.includeErrorStack ?? false;
101
113
  }
102
114
  /**
@@ -122,7 +134,7 @@ class FastifyWebServer {
122
134
  if (Is.arrayValue(socketRoutes) && !Is.arrayValue(socketRouteProcessors)) {
123
135
  throw new GeneralError(this.CLASS_NAME, "noSocketProcessors");
124
136
  }
125
- await this._loggingConnector?.log({
137
+ await this._logging?.log({
126
138
  level: "info",
127
139
  ts: Date.now(),
128
140
  source: this.CLASS_NAME,
@@ -167,7 +179,7 @@ class FastifyWebServer {
167
179
  err = errorAndCode.error;
168
180
  httpStatusCode = errorAndCode.httpStatusCode;
169
181
  }
170
- await this._loggingConnector?.log({
182
+ await this._logging?.log({
171
183
  level: "error",
172
184
  ts: Date.now(),
173
185
  source: this.CLASS_NAME,
@@ -188,7 +200,7 @@ class FastifyWebServer {
188
200
  async start() {
189
201
  const host = this._options?.host ?? FastifyWebServer._DEFAULT_HOST;
190
202
  const port = this._options?.port ?? FastifyWebServer._DEFAULT_PORT;
191
- await this._loggingConnector?.log({
203
+ await this._logging?.log({
192
204
  level: "info",
193
205
  ts: Date.now(),
194
206
  source: this.CLASS_NAME,
@@ -203,7 +215,7 @@ class FastifyWebServer {
203
215
  await this._fastify.listen({ port, host });
204
216
  const addresses = this._fastify.addresses();
205
217
  const protocol = Is.object(this._fastify.initialConfig.https) ? "https://" : "http://";
206
- await this._loggingConnector?.log({
218
+ await this._logging?.log({
207
219
  level: "info",
208
220
  ts: Date.now(),
209
221
  source: this.CLASS_NAME,
@@ -217,7 +229,7 @@ class FastifyWebServer {
217
229
  this._started = true;
218
230
  }
219
231
  catch (err) {
220
- await this._loggingConnector?.log({
232
+ await this._logging?.log({
221
233
  level: "error",
222
234
  ts: Date.now(),
223
235
  source: this.CLASS_NAME,
@@ -235,7 +247,7 @@ class FastifyWebServer {
235
247
  if (this._started) {
236
248
  this._started = false;
237
249
  await this._fastify.close();
238
- await this._loggingConnector?.log({
250
+ await this._logging?.log({
239
251
  level: "info",
240
252
  ts: Date.now(),
241
253
  source: this.CLASS_NAME,
@@ -256,7 +268,7 @@ class FastifyWebServer {
256
268
  if (!path.startsWith("/")) {
257
269
  path = `/${path}`;
258
270
  }
259
- await this._loggingConnector?.log({
271
+ await this._logging?.log({
260
272
  level: "info",
261
273
  ts: Date.now(),
262
274
  source: this.CLASS_NAME,
@@ -281,30 +293,33 @@ class FastifyWebServer {
281
293
  for (const socketRoute of socketRoutes) {
282
294
  const path = StringHelper.trimLeadingSlashes(StringHelper.trimTrailingSlashes(socketRoute.path));
283
295
  const pathParts = path.split("/");
284
- await this._loggingConnector?.log({
296
+ const namespace = `/${pathParts[0]}`;
297
+ const topic = pathParts.slice(1).join("/");
298
+ await this._logging?.log({
285
299
  level: "info",
286
300
  ts: Date.now(),
287
301
  source: this.CLASS_NAME,
288
302
  message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.socketRouteAdded`,
289
- data: { route: `/${path}` }
303
+ data: {
304
+ handshakePath: this._socketConfig.path,
305
+ namespace,
306
+ eventName: topic
307
+ }
290
308
  });
291
- const socketNamespace = io.of(`/${pathParts[0]}`);
292
- const topic = pathParts.slice(1).join("/");
309
+ const socketNamespace = io.of(namespace);
293
310
  socketNamespace.on("connection", async (socket) => {
294
- const httpServerRequest = {
311
+ const socketServerRequest = {
295
312
  method: HttpMethod.GET,
296
313
  url: socket.handshake.url,
297
314
  query: socket.handshake.query,
298
- headers: socket.handshake.headers
315
+ headers: socket.handshake.headers,
316
+ socketId: socket.id
299
317
  };
300
318
  // Pass the connected information on to any processors
301
319
  try {
302
- const processorState = {
303
- socketId: socket.id
304
- };
305
320
  for (const socketRouteProcessor of socketRouteProcessors) {
306
- if (Is.function(socketRouteProcessor.connected)) {
307
- await socketRouteProcessor.connected(httpServerRequest, socketRoute, processorState);
321
+ if (socketRouteProcessor.connected) {
322
+ await socketRouteProcessor.connected(socketServerRequest, socketRoute, this._loggingComponentType);
308
323
  }
309
324
  }
310
325
  }
@@ -316,13 +331,10 @@ class FastifyWebServer {
316
331
  }
317
332
  socket.on("disconnect", async () => {
318
333
  try {
319
- const processorState = {
320
- socketId: socket.id
321
- };
322
334
  // The socket disconnected so notify any processors
323
335
  for (const socketRouteProcessor of socketRouteProcessors) {
324
- if (Is.function(socketRouteProcessor.disconnected)) {
325
- await socketRouteProcessor.disconnected(httpServerRequest, socketRoute, processorState);
336
+ if (socketRouteProcessor.disconnected) {
337
+ await socketRouteProcessor.disconnected(socketServerRequest, socketRoute, this._loggingComponentType);
326
338
  }
327
339
  }
328
340
  }
@@ -379,19 +391,20 @@ class FastifyWebServer {
379
391
  */
380
392
  async runProcessorsRest(restRouteProcessors, restRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState) {
381
393
  try {
382
- for (const routeProcessor of restRouteProcessors) {
383
- if (Is.function(routeProcessor.pre)) {
384
- await routeProcessor.pre(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
394
+ const filteredProcessors = this.filterRouteProcessors(restRoute, restRouteProcessors);
395
+ for (const routeProcessor of filteredProcessors) {
396
+ if (routeProcessor.pre) {
397
+ await routeProcessor.pre(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState, this._loggingComponentType);
385
398
  }
386
399
  }
387
- for (const routeProcessor of restRouteProcessors) {
388
- if (Is.function(routeProcessor.process)) {
389
- await routeProcessor.process(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
400
+ for (const routeProcessor of filteredProcessors) {
401
+ if (routeProcessor.process) {
402
+ await routeProcessor.process(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState, this._loggingComponentType);
390
403
  }
391
404
  }
392
- for (const routeProcessor of restRouteProcessors) {
393
- if (Is.function(routeProcessor.post)) {
394
- await routeProcessor.post(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState);
405
+ for (const routeProcessor of filteredProcessors) {
406
+ if (routeProcessor.post) {
407
+ await routeProcessor.post(httpServerRequest, httpResponse, restRoute, httpRequestIdentity, processorState, this._loggingComponentType);
395
408
  }
396
409
  }
397
410
  }
@@ -400,6 +413,33 @@ class FastifyWebServer {
400
413
  HttpErrorHelper.buildResponse(httpResponse, error, httpStatusCode);
401
414
  }
402
415
  }
416
+ /**
417
+ * Filter the route processors based on the requested features.
418
+ * @param route The route to process.
419
+ * @param routeProcessors The processors to filter.
420
+ * @returns The filtered list of route processor.
421
+ * @internal
422
+ */
423
+ filterRouteProcessors(route, routeProcessors) {
424
+ const requestedFeatures = route?.processorFeatures ?? [];
425
+ if (!Is.arrayValue(requestedFeatures)) {
426
+ // If there are no requested features, we just return all the processors
427
+ return routeProcessors;
428
+ }
429
+ // Reduce the list of route processors to just those in the requested features list
430
+ const reducedProcessors = routeProcessors.filter(routeProcessor => {
431
+ // Processors that do not define any features always get run
432
+ // If the route processor has features defined, then we only run it
433
+ // if the route has at least one of those features required
434
+ let runRouteProcessor = true;
435
+ if (routeProcessor.features) {
436
+ const routeProcessorFeatures = routeProcessor.features();
437
+ runRouteProcessor = routeProcessorFeatures.some(feature => requestedFeatures.includes(feature));
438
+ }
439
+ return runRouteProcessor;
440
+ });
441
+ return reducedProcessors;
442
+ }
403
443
  /**
404
444
  * Handle the incoming socket request.
405
445
  * @param socketRouteProcessors The hooks to process the incoming requests.
@@ -411,49 +451,50 @@ class FastifyWebServer {
411
451
  * @internal
412
452
  */
413
453
  async handleRequestSocket(socketRouteProcessors, socketRoute, socket, fullPath, emitTopic, request) {
414
- const httpServerRequest = {
454
+ const socketServerRequest = {
415
455
  method: HttpMethod.GET,
416
456
  url: fullPath,
417
457
  query: socket.handshake.query,
418
458
  headers: socket.handshake.headers,
419
- body: request.body
459
+ body: request.body,
460
+ socketId: socket.id
420
461
  };
421
462
  const httpResponse = {};
422
463
  const httpRequestIdentity = {};
423
- const processorState = {
424
- socketId: socket.id
425
- };
426
- delete httpServerRequest.query?.EIO;
427
- delete httpServerRequest.query?.transport;
428
- await this.runProcessorsSocket(socketRouteProcessors, socketRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState, emitTopic, async (topic, response) => {
464
+ const processorState = {};
465
+ delete socketServerRequest.query?.EIO;
466
+ delete socketServerRequest.query?.transport;
467
+ await this.runProcessorsSocket(socketRouteProcessors, socketRoute, socketServerRequest, httpResponse, httpRequestIdentity, processorState, emitTopic, async (topic, response) => {
429
468
  await socket.emit(topic, response);
430
469
  });
431
470
  }
432
471
  /**
433
472
  * Run the socket processors for the route.
473
+ * @param socketId The id of the socket.
434
474
  * @param socketRouteProcessors The processors to run.
435
475
  * @param socketRoute The route to process.
436
- * @param httpServerRequest The incoming request.
476
+ * @param socketServerRequest The incoming request.
437
477
  * @param httpResponse The outgoing response.
438
478
  * @param httpRequestIdentity The identity context for the request.
439
479
  * @param processorState The state handed through the processors.
440
480
  * @param requestTopic The topic of the request.
441
481
  * @internal
442
482
  */
443
- async runProcessorsSocket(socketRouteProcessors, socketRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState, requestTopic, responseEmitter) {
483
+ async runProcessorsSocket(socketRouteProcessors, socketRoute, socketServerRequest, httpResponse, httpRequestIdentity, processorState, requestTopic, responseEmitter) {
484
+ const filteredProcessors = this.filterRouteProcessors(socketRoute, socketRouteProcessors);
444
485
  // Custom emit method which will also call the post processors
445
486
  const postProcessEmit = async (topic, response, responseProcessorState) => {
446
487
  await responseEmitter(topic, response);
447
488
  try {
448
489
  // The post processors are called after the response has been emitted
449
- for (const postSocketRouteProcessor of socketRouteProcessors) {
450
- if (Is.function(postSocketRouteProcessor.post)) {
451
- await postSocketRouteProcessor.post(httpServerRequest, response, socketRoute, httpRequestIdentity, responseProcessorState);
490
+ for (const postSocketRouteProcessor of filteredProcessors) {
491
+ if (postSocketRouteProcessor.post) {
492
+ await postSocketRouteProcessor.post(socketServerRequest, response, socketRoute, httpRequestIdentity, responseProcessorState, this._loggingComponentType);
452
493
  }
453
494
  }
454
495
  }
455
496
  catch (err) {
456
- this._loggingConnector?.log({
497
+ this._logging?.log({
457
498
  level: "error",
458
499
  ts: Date.now(),
459
500
  source: this.CLASS_NAME,
@@ -466,9 +507,9 @@ class FastifyWebServer {
466
507
  }
467
508
  };
468
509
  try {
469
- for (const socketRouteProcessor of socketRouteProcessors) {
470
- if (Is.function(socketRouteProcessor.pre)) {
471
- await socketRouteProcessor.pre(httpServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState);
510
+ for (const socketRouteProcessor of filteredProcessors) {
511
+ if (socketRouteProcessor.pre) {
512
+ await socketRouteProcessor.pre(socketServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState, this._loggingComponentType);
472
513
  }
473
514
  }
474
515
  // We always call all the processors regardless of any response set by a previous processor.
@@ -477,11 +518,11 @@ class FastifyWebServer {
477
518
  if (!Is.empty(httpResponse.statusCode)) {
478
519
  await postProcessEmit(requestTopic, httpResponse, processorState);
479
520
  }
480
- for (const socketRouteProcessor of socketRouteProcessors) {
481
- if (Is.function(socketRouteProcessor.process)) {
482
- await socketRouteProcessor.process(httpServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState, async (topic, processResponse) => {
521
+ for (const socketRouteProcessor of filteredProcessors) {
522
+ if (socketRouteProcessor.process) {
523
+ await socketRouteProcessor.process(socketServerRequest, httpResponse, socketRoute, httpRequestIdentity, processorState, async (topic, processResponse) => {
483
524
  await postProcessEmit(topic, processResponse, processorState);
484
- });
525
+ }, this._loggingComponentType);
485
526
  }
486
527
  }
487
528
  // If the processors set the status to any kind of error then we should emit this manually
@@ -5,9 +5,9 @@ import type { IFastifyWebServerConfig } from "./IFastifyWebServerConfig";
5
5
  */
6
6
  export interface IFastifyWebServerConstructorOptions {
7
7
  /**
8
- * The type of the logging connector to use, if undefined, no logging will happen.
8
+ * The type of the logging component to use, if undefined, no logging will happen.
9
9
  */
10
- loggingConnectorType?: string;
10
+ loggingComponentType?: string;
11
11
  /**
12
12
  * Additional configuration for the server.
13
13
  */
package/docs/changelog.md CHANGED
@@ -1,5 +1,172 @@
1
1
  # @twin.org/api-server-fastify - Changelog
2
2
 
3
+ ## [0.0.2-next.10](https://github.com/twinfoundation/api/compare/api-server-fastify-v0.0.2-next.9...api-server-fastify-v0.0.2-next.10) (2025-09-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * add authentication generators and process features option ([a67edf1](https://github.com/twinfoundation/api/commit/a67edf1df212bd8ab94a40cddf5338551155696f))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/api-core bumped from 0.0.2-next.9 to 0.0.2-next.10
16
+ * @twin.org/api-models bumped from 0.0.2-next.9 to 0.0.2-next.10
17
+ * @twin.org/api-processors bumped from 0.0.2-next.9 to 0.0.2-next.10
18
+
19
+ ## [0.0.2-next.9](https://github.com/twinfoundation/api/compare/api-server-fastify-v0.0.2-next.8...api-server-fastify-v0.0.2-next.9) (2025-08-29)
20
+
21
+
22
+ ### Features
23
+
24
+ * eslint migration to flat config ([0dd5820](https://github.com/twinfoundation/api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805))
25
+
26
+
27
+ ### Dependencies
28
+
29
+ * The following workspace dependencies were updated
30
+ * dependencies
31
+ * @twin.org/api-core bumped from 0.0.2-next.8 to 0.0.2-next.9
32
+ * @twin.org/api-models bumped from 0.0.2-next.8 to 0.0.2-next.9
33
+ * @twin.org/api-processors bumped from 0.0.2-next.8 to 0.0.2-next.9
34
+
35
+ ## [0.0.2-next.8](https://github.com/twinfoundation/api/compare/api-server-fastify-v0.0.2-next.7...api-server-fastify-v0.0.2-next.8) (2025-08-21)
36
+
37
+
38
+ ### Features
39
+
40
+ * add root, favicon routes ([71da1c3](https://github.com/twinfoundation/api/commit/71da1c3a93c349588aff7084d1d8d6a29a277da8))
41
+
42
+
43
+ ### Dependencies
44
+
45
+ * The following workspace dependencies were updated
46
+ * dependencies
47
+ * @twin.org/api-core bumped from 0.0.2-next.7 to 0.0.2-next.8
48
+ * @twin.org/api-models bumped from 0.0.2-next.7 to 0.0.2-next.8
49
+ * @twin.org/api-processors bumped from 0.0.2-next.7 to 0.0.2-next.8
50
+
51
+ ## [0.0.2-next.7](https://github.com/twinfoundation/api/compare/api-server-fastify-v0.0.2-next.6...api-server-fastify-v0.0.2-next.7) (2025-08-20)
52
+
53
+
54
+ ### Features
55
+
56
+ * logging naming consistency ([a4a6ef2](https://github.com/twinfoundation/api/commit/a4a6ef2de5049045589eb78b177ff62e744bde9d))
57
+
58
+
59
+ ### Dependencies
60
+
61
+ * The following workspace dependencies were updated
62
+ * dependencies
63
+ * @twin.org/api-core bumped from 0.0.2-next.6 to 0.0.2-next.7
64
+ * @twin.org/api-models bumped from 0.0.2-next.6 to 0.0.2-next.7
65
+ * @twin.org/api-processors bumped from 0.0.2-next.6 to 0.0.2-next.7
66
+
67
+ ## [0.0.2-next.6](https://github.com/twinfoundation/api/compare/api-server-fastify-v0.0.2-next.5...api-server-fastify-v0.0.2-next.6) (2025-08-19)
68
+
69
+
70
+ ### Features
71
+
72
+ * update framework core ([d8eebf2](https://github.com/twinfoundation/api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa))
73
+
74
+
75
+ ### Dependencies
76
+
77
+ * The following workspace dependencies were updated
78
+ * dependencies
79
+ * @twin.org/api-core bumped from 0.0.2-next.5 to 0.0.2-next.6
80
+ * @twin.org/api-models bumped from 0.0.2-next.5 to 0.0.2-next.6
81
+ * @twin.org/api-processors bumped from 0.0.2-next.5 to 0.0.2-next.6
82
+
83
+ ## [0.0.2-next.5](https://github.com/twinfoundation/api/compare/api-server-fastify-v0.0.2-next.4...api-server-fastify-v0.0.2-next.5) (2025-07-25)
84
+
85
+
86
+ ### Features
87
+
88
+ * add json-ld mime type processor and auth admin component ([8861791](https://github.com/twinfoundation/api/commit/88617916e23bfbca023dbae1976fe421983a02ff))
89
+ * add logging component type to request contexts ([210de1b](https://github.com/twinfoundation/api/commit/210de1b9e1c91079b59a2b90ddd57569668d647d))
90
+ * add socket id, connect and disconnect ([20b0d0e](https://github.com/twinfoundation/api/commit/20b0d0ec279cab46141fee09de2c4a7087cdce16))
91
+ * improve socket route logging ([b8d9519](https://github.com/twinfoundation/api/commit/b8d95199f838ac6ba9f45c30ef7c4e613201ff53))
92
+ * update dependencies ([1171dc4](https://github.com/twinfoundation/api/commit/1171dc416a9481737f6a640e3cf30145768f37e9))
93
+ * use shared store mechanism ([#19](https://github.com/twinfoundation/api/issues/19)) ([32116df](https://github.com/twinfoundation/api/commit/32116df3b4380a30137f5056f242a5c99afa2df9))
94
+
95
+
96
+ ### Dependencies
97
+
98
+ * The following workspace dependencies were updated
99
+ * dependencies
100
+ * @twin.org/api-core bumped from 0.0.2-next.4 to 0.0.2-next.5
101
+ * @twin.org/api-models bumped from 0.0.2-next.4 to 0.0.2-next.5
102
+ * @twin.org/api-processors bumped from 0.0.2-next.4 to 0.0.2-next.5
103
+
104
+ ## [0.0.2-next.4](https://github.com/twinfoundation/api/compare/api-server-fastify-v0.0.2-next.3...api-server-fastify-v0.0.2-next.4) (2025-07-25)
105
+
106
+
107
+ ### Features
108
+
109
+ * add logging component type to request contexts ([210de1b](https://github.com/twinfoundation/api/commit/210de1b9e1c91079b59a2b90ddd57569668d647d))
110
+
111
+
112
+ ### Dependencies
113
+
114
+ * The following workspace dependencies were updated
115
+ * dependencies
116
+ * @twin.org/api-core bumped from 0.0.2-next.3 to 0.0.2-next.4
117
+ * @twin.org/api-models bumped from 0.0.2-next.3 to 0.0.2-next.4
118
+ * @twin.org/api-processors bumped from 0.0.2-next.3 to 0.0.2-next.4
119
+
120
+ ## [0.0.2-next.3](https://github.com/twinfoundation/api/compare/api-server-fastify-v0.0.2-next.2...api-server-fastify-v0.0.2-next.3) (2025-07-24)
121
+
122
+
123
+ ### Features
124
+
125
+ * add socket id, connect and disconnect ([20b0d0e](https://github.com/twinfoundation/api/commit/20b0d0ec279cab46141fee09de2c4a7087cdce16))
126
+
127
+
128
+ ### Dependencies
129
+
130
+ * The following workspace dependencies were updated
131
+ * dependencies
132
+ * @twin.org/api-core bumped from 0.0.2-next.2 to 0.0.2-next.3
133
+ * @twin.org/api-models bumped from 0.0.2-next.2 to 0.0.2-next.3
134
+ * @twin.org/api-processors bumped from 0.0.2-next.2 to 0.0.2-next.3
135
+
136
+ ## [0.0.2-next.2](https://github.com/twinfoundation/api/compare/api-server-fastify-v0.0.2-next.1...api-server-fastify-v0.0.2-next.2) (2025-07-17)
137
+
138
+
139
+ ### Features
140
+
141
+ * improve socket route logging ([b8d9519](https://github.com/twinfoundation/api/commit/b8d95199f838ac6ba9f45c30ef7c4e613201ff53))
142
+
143
+
144
+ ### Dependencies
145
+
146
+ * The following workspace dependencies were updated
147
+ * dependencies
148
+ * @twin.org/api-core bumped from 0.0.2-next.1 to 0.0.2-next.2
149
+ * @twin.org/api-models bumped from 0.0.2-next.1 to 0.0.2-next.2
150
+ * @twin.org/api-processors bumped from 0.0.2-next.1 to 0.0.2-next.2
151
+
152
+ ## [0.0.2-next.1](https://github.com/twinfoundation/api/compare/api-server-fastify-v0.0.2-next.0...api-server-fastify-v0.0.2-next.1) (2025-07-08)
153
+
154
+
155
+ ### Features
156
+
157
+ * add json-ld mime type processor and auth admin component ([8861791](https://github.com/twinfoundation/api/commit/88617916e23bfbca023dbae1976fe421983a02ff))
158
+ * update dependencies ([1171dc4](https://github.com/twinfoundation/api/commit/1171dc416a9481737f6a640e3cf30145768f37e9))
159
+ * use shared store mechanism ([#19](https://github.com/twinfoundation/api/issues/19)) ([32116df](https://github.com/twinfoundation/api/commit/32116df3b4380a30137f5056f242a5c99afa2df9))
160
+
161
+
162
+ ### Dependencies
163
+
164
+ * The following workspace dependencies were updated
165
+ * dependencies
166
+ * @twin.org/api-core bumped from 0.0.2-next.0 to 0.0.2-next.1
167
+ * @twin.org/api-models bumped from 0.0.2-next.0 to 0.0.2-next.1
168
+ * @twin.org/api-processors bumped from 0.0.2-next.0 to 0.0.2-next.1
169
+
3
170
  ## 0.0.1 (2025-07-03)
4
171
 
5
172
 
@@ -4,11 +4,11 @@ The options for the Fastify web server constructor.
4
4
 
5
5
  ## Properties
6
6
 
7
- ### loggingConnectorType?
7
+ ### loggingComponentType?
8
8
 
9
- > `optional` **loggingConnectorType**: `string`
9
+ > `optional` **loggingComponentType**: `string`
10
10
 
11
- The type of the logging connector to use, if undefined, no logging will happen.
11
+ The type of the logging component to use, if undefined, no logging will happen.
12
12
 
13
13
  ***
14
14
 
package/locales/en.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "stopped": "The Web Server was stopped",
8
8
  "badRequest": "The web server could not handle the request",
9
9
  "restRouteAdded": "Added REST route \"{route}\" \"{method}\"",
10
- "socketRouteAdded": "Added socket route \"{route}\"",
10
+ "socketRouteAdded": "Added socket route: handshake path \"{handshakePath}\", namespace \"{namespace}\", event name \"{eventName}\"",
11
11
  "noRestProcessors": "You must configure at least one REST processor",
12
12
  "noSocketProcessors": "You must configure at least one socket processor",
13
13
  "postProcessorError": "There was a failure after in a post processor for route \"{route}\""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/api-server-fastify",
3
- "version": "0.0.1",
3
+ "version": "0.0.2-next.10",
4
4
  "description": "Use Fastify as the core web server for APIs",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,15 +14,16 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@fastify/compress": "8.0.3",
18
- "@fastify/cors": "11.0.1",
19
- "@twin.org/api-core": "^0.0.1",
20
- "@twin.org/api-models": "^0.0.1",
21
- "@twin.org/core": "^0.0.1",
22
- "@twin.org/logging-models": "^0.0.1-next.2",
23
- "@twin.org/nameof": "^0.0.1",
24
- "@twin.org/web": "^0.0.1",
25
- "fastify": "5.3.3",
17
+ "@fastify/compress": "8.1.0",
18
+ "@fastify/cors": "11.1.0",
19
+ "@twin.org/api-core": "0.0.2-next.10",
20
+ "@twin.org/api-models": "0.0.2-next.10",
21
+ "@twin.org/api-processors": "0.0.2-next.10",
22
+ "@twin.org/core": "next",
23
+ "@twin.org/logging-models": "next",
24
+ "@twin.org/nameof": "next",
25
+ "@twin.org/web": "next",
26
+ "fastify": "5.5.0",
26
27
  "socket.io": "4.8.1"
27
28
  },
28
29
  "main": "./dist/cjs/index.cjs",