@twin.org/api-server-fastify 0.0.2-next.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.
@@ -5,7 +5,6 @@ var FastifyCors = require('@fastify/cors');
5
5
  var apiModels = require('@twin.org/api-models');
6
6
  var apiProcessors = require('@twin.org/api-processors');
7
7
  var core = require('@twin.org/core');
8
- var loggingModels = require('@twin.org/logging-models');
9
8
  var web = require('@twin.org/web');
10
9
  var Fastify = require('fastify');
11
10
  var fp = require('fastify-plugin');
@@ -48,10 +47,15 @@ class FastifyWebServer {
48
47
  */
49
48
  CLASS_NAME = "FastifyWebServer";
50
49
  /**
51
- * The logging connector.
50
+ * The logging component type.
52
51
  * @internal
53
52
  */
54
- _loggingConnector;
53
+ _loggingComponentType;
54
+ /**
55
+ * The logging component.
56
+ * @internal
57
+ */
58
+ _logging;
55
59
  /**
56
60
  * The options for the server.
57
61
  * @internal
@@ -87,12 +91,15 @@ class FastifyWebServer {
87
91
  * @param options The options for the server.
88
92
  */
89
93
  constructor(options) {
90
- this._loggingConnector = core.Is.stringValue(options?.loggingConnectorType)
91
- ? loggingModels.LoggingConnectorFactory.get(options.loggingConnectorType)
92
- : undefined;
94
+ this._loggingComponentType = options?.loggingComponentType;
95
+ this._logging = core.ComponentFactory.getIfExists(options?.loggingComponentType ?? "logging");
93
96
  this._fastify = Fastify({
94
- maxParamLength: 2000,
97
+ routerOptions: {
98
+ maxParamLength: 2000
99
+ },
95
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
96
103
  });
97
104
  this._socketConfig = {
98
105
  path: "/socket",
@@ -129,7 +136,7 @@ class FastifyWebServer {
129
136
  if (core.Is.arrayValue(socketRoutes) && !core.Is.arrayValue(socketRouteProcessors)) {
130
137
  throw new core.GeneralError(this.CLASS_NAME, "noSocketProcessors");
131
138
  }
132
- await this._loggingConnector?.log({
139
+ await this._logging?.log({
133
140
  level: "info",
134
141
  ts: Date.now(),
135
142
  source: this.CLASS_NAME,
@@ -174,7 +181,7 @@ class FastifyWebServer {
174
181
  err = errorAndCode.error;
175
182
  httpStatusCode = errorAndCode.httpStatusCode;
176
183
  }
177
- await this._loggingConnector?.log({
184
+ await this._logging?.log({
178
185
  level: "error",
179
186
  ts: Date.now(),
180
187
  source: this.CLASS_NAME,
@@ -195,7 +202,7 @@ class FastifyWebServer {
195
202
  async start() {
196
203
  const host = this._options?.host ?? FastifyWebServer._DEFAULT_HOST;
197
204
  const port = this._options?.port ?? FastifyWebServer._DEFAULT_PORT;
198
- await this._loggingConnector?.log({
205
+ await this._logging?.log({
199
206
  level: "info",
200
207
  ts: Date.now(),
201
208
  source: this.CLASS_NAME,
@@ -210,7 +217,7 @@ class FastifyWebServer {
210
217
  await this._fastify.listen({ port, host });
211
218
  const addresses = this._fastify.addresses();
212
219
  const protocol = core.Is.object(this._fastify.initialConfig.https) ? "https://" : "http://";
213
- await this._loggingConnector?.log({
220
+ await this._logging?.log({
214
221
  level: "info",
215
222
  ts: Date.now(),
216
223
  source: this.CLASS_NAME,
@@ -224,7 +231,7 @@ class FastifyWebServer {
224
231
  this._started = true;
225
232
  }
226
233
  catch (err) {
227
- await this._loggingConnector?.log({
234
+ await this._logging?.log({
228
235
  level: "error",
229
236
  ts: Date.now(),
230
237
  source: this.CLASS_NAME,
@@ -242,7 +249,7 @@ class FastifyWebServer {
242
249
  if (this._started) {
243
250
  this._started = false;
244
251
  await this._fastify.close();
245
- await this._loggingConnector?.log({
252
+ await this._logging?.log({
246
253
  level: "info",
247
254
  ts: Date.now(),
248
255
  source: this.CLASS_NAME,
@@ -263,7 +270,7 @@ class FastifyWebServer {
263
270
  if (!path.startsWith("/")) {
264
271
  path = `/${path}`;
265
272
  }
266
- await this._loggingConnector?.log({
273
+ await this._logging?.log({
267
274
  level: "info",
268
275
  ts: Date.now(),
269
276
  source: this.CLASS_NAME,
@@ -288,30 +295,33 @@ class FastifyWebServer {
288
295
  for (const socketRoute of socketRoutes) {
289
296
  const path = core.StringHelper.trimLeadingSlashes(core.StringHelper.trimTrailingSlashes(socketRoute.path));
290
297
  const pathParts = path.split("/");
291
- await this._loggingConnector?.log({
298
+ const namespace = `/${pathParts[0]}`;
299
+ const topic = pathParts.slice(1).join("/");
300
+ await this._logging?.log({
292
301
  level: "info",
293
302
  ts: Date.now(),
294
303
  source: this.CLASS_NAME,
295
304
  message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.socketRouteAdded`,
296
- data: { route: `/${path}` }
305
+ data: {
306
+ handshakePath: this._socketConfig.path,
307
+ namespace,
308
+ eventName: topic
309
+ }
297
310
  });
298
- const socketNamespace = io.of(`/${pathParts[0]}`);
299
- const topic = pathParts.slice(1).join("/");
311
+ const socketNamespace = io.of(namespace);
300
312
  socketNamespace.on("connection", async (socket) => {
301
- const httpServerRequest = {
313
+ const socketServerRequest = {
302
314
  method: web.HttpMethod.GET,
303
315
  url: socket.handshake.url,
304
316
  query: socket.handshake.query,
305
- headers: socket.handshake.headers
317
+ headers: socket.handshake.headers,
318
+ socketId: socket.id
306
319
  };
307
320
  // Pass the connected information on to any processors
308
321
  try {
309
- const processorState = {
310
- socketId: socket.id
311
- };
312
322
  for (const socketRouteProcessor of socketRouteProcessors) {
313
- if (core.Is.function(socketRouteProcessor.connected)) {
314
- await socketRouteProcessor.connected(httpServerRequest, socketRoute, processorState);
323
+ if (socketRouteProcessor.connected) {
324
+ await socketRouteProcessor.connected(socketServerRequest, socketRoute, this._loggingComponentType);
315
325
  }
316
326
  }
317
327
  }
@@ -323,13 +333,10 @@ class FastifyWebServer {
323
333
  }
324
334
  socket.on("disconnect", async () => {
325
335
  try {
326
- const processorState = {
327
- socketId: socket.id
328
- };
329
336
  // The socket disconnected so notify any processors
330
337
  for (const socketRouteProcessor of socketRouteProcessors) {
331
- if (core.Is.function(socketRouteProcessor.disconnected)) {
332
- await socketRouteProcessor.disconnected(httpServerRequest, socketRoute, processorState);
338
+ if (socketRouteProcessor.disconnected) {
339
+ await socketRouteProcessor.disconnected(socketServerRequest, socketRoute, this._loggingComponentType);
333
340
  }
334
341
  }
335
342
  }
@@ -386,19 +393,20 @@ class FastifyWebServer {
386
393
  */
387
394
  async runProcessorsRest(restRouteProcessors, restRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState) {
388
395
  try {
389
- for (const routeProcessor of restRouteProcessors) {
390
- if (core.Is.function(routeProcessor.pre)) {
391
- 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);
392
400
  }
393
401
  }
394
- for (const routeProcessor of restRouteProcessors) {
395
- if (core.Is.function(routeProcessor.process)) {
396
- 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);
397
405
  }
398
406
  }
399
- for (const routeProcessor of restRouteProcessors) {
400
- if (core.Is.function(routeProcessor.post)) {
401
- 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);
402
410
  }
403
411
  }
404
412
  }
@@ -407,6 +415,33 @@ class FastifyWebServer {
407
415
  apiModels.HttpErrorHelper.buildResponse(httpResponse, error, httpStatusCode);
408
416
  }
409
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
+ }
410
445
  /**
411
446
  * Handle the incoming socket request.
412
447
  * @param socketRouteProcessors The hooks to process the incoming requests.
@@ -418,49 +453,50 @@ class FastifyWebServer {
418
453
  * @internal
419
454
  */
420
455
  async handleRequestSocket(socketRouteProcessors, socketRoute, socket, fullPath, emitTopic, request) {
421
- const httpServerRequest = {
456
+ const socketServerRequest = {
422
457
  method: web.HttpMethod.GET,
423
458
  url: fullPath,
424
459
  query: socket.handshake.query,
425
460
  headers: socket.handshake.headers,
426
- body: request.body
461
+ body: request.body,
462
+ socketId: socket.id
427
463
  };
428
464
  const httpResponse = {};
429
465
  const httpRequestIdentity = {};
430
- const processorState = {
431
- socketId: socket.id
432
- };
433
- delete httpServerRequest.query?.EIO;
434
- delete httpServerRequest.query?.transport;
435
- 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) => {
436
470
  await socket.emit(topic, response);
437
471
  });
438
472
  }
439
473
  /**
440
474
  * Run the socket processors for the route.
475
+ * @param socketId The id of the socket.
441
476
  * @param socketRouteProcessors The processors to run.
442
477
  * @param socketRoute The route to process.
443
- * @param httpServerRequest The incoming request.
478
+ * @param socketServerRequest The incoming request.
444
479
  * @param httpResponse The outgoing response.
445
480
  * @param httpRequestIdentity The identity context for the request.
446
481
  * @param processorState The state handed through the processors.
447
482
  * @param requestTopic The topic of the request.
448
483
  * @internal
449
484
  */
450
- 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);
451
487
  // Custom emit method which will also call the post processors
452
488
  const postProcessEmit = async (topic, response, responseProcessorState) => {
453
489
  await responseEmitter(topic, response);
454
490
  try {
455
491
  // The post processors are called after the response has been emitted
456
- for (const postSocketRouteProcessor of socketRouteProcessors) {
457
- if (core.Is.function(postSocketRouteProcessor.post)) {
458
- 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);
459
495
  }
460
496
  }
461
497
  }
462
498
  catch (err) {
463
- this._loggingConnector?.log({
499
+ this._logging?.log({
464
500
  level: "error",
465
501
  ts: Date.now(),
466
502
  source: this.CLASS_NAME,
@@ -473,9 +509,9 @@ class FastifyWebServer {
473
509
  }
474
510
  };
475
511
  try {
476
- for (const socketRouteProcessor of socketRouteProcessors) {
477
- if (core.Is.function(socketRouteProcessor.pre)) {
478
- 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);
479
515
  }
480
516
  }
481
517
  // We always call all the processors regardless of any response set by a previous processor.
@@ -484,11 +520,11 @@ class FastifyWebServer {
484
520
  if (!core.Is.empty(httpResponse.statusCode)) {
485
521
  await postProcessEmit(requestTopic, httpResponse, processorState);
486
522
  }
487
- for (const socketRouteProcessor of socketRouteProcessors) {
488
- if (core.Is.function(socketRouteProcessor.process)) {
489
- 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) => {
490
526
  await postProcessEmit(topic, processResponse, processorState);
491
- });
527
+ }, this._loggingComponentType);
492
528
  }
493
529
  }
494
530
  // If the processors set the status to any kind of error then we should emit this manually
@@ -2,8 +2,7 @@ import FastifyCompress from '@fastify/compress';
2
2
  import FastifyCors from '@fastify/cors';
3
3
  import { HttpErrorHelper } from '@twin.org/api-models';
4
4
  import { JsonLdMimeTypeProcessor } from '@twin.org/api-processors';
5
- import { StringHelper, Is, GeneralError, BaseError } from '@twin.org/core';
6
- import { LoggingConnectorFactory } from '@twin.org/logging-models';
5
+ import { StringHelper, ComponentFactory, Is, GeneralError, BaseError } from '@twin.org/core';
7
6
  import { HttpStatusCode, HttpMethod, HeaderTypes } from '@twin.org/web';
8
7
  import Fastify from 'fastify';
9
8
  import fp from 'fastify-plugin';
@@ -46,10 +45,15 @@ class FastifyWebServer {
46
45
  */
47
46
  CLASS_NAME = "FastifyWebServer";
48
47
  /**
49
- * The logging connector.
48
+ * The logging component type.
50
49
  * @internal
51
50
  */
52
- _loggingConnector;
51
+ _loggingComponentType;
52
+ /**
53
+ * The logging component.
54
+ * @internal
55
+ */
56
+ _logging;
53
57
  /**
54
58
  * The options for the server.
55
59
  * @internal
@@ -85,12 +89,15 @@ class FastifyWebServer {
85
89
  * @param options The options for the server.
86
90
  */
87
91
  constructor(options) {
88
- this._loggingConnector = Is.stringValue(options?.loggingConnectorType)
89
- ? LoggingConnectorFactory.get(options.loggingConnectorType)
90
- : undefined;
92
+ this._loggingComponentType = options?.loggingComponentType;
93
+ this._logging = ComponentFactory.getIfExists(options?.loggingComponentType ?? "logging");
91
94
  this._fastify = Fastify({
92
- maxParamLength: 2000,
95
+ routerOptions: {
96
+ maxParamLength: 2000
97
+ },
93
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
94
101
  });
95
102
  this._socketConfig = {
96
103
  path: "/socket",
@@ -127,7 +134,7 @@ class FastifyWebServer {
127
134
  if (Is.arrayValue(socketRoutes) && !Is.arrayValue(socketRouteProcessors)) {
128
135
  throw new GeneralError(this.CLASS_NAME, "noSocketProcessors");
129
136
  }
130
- await this._loggingConnector?.log({
137
+ await this._logging?.log({
131
138
  level: "info",
132
139
  ts: Date.now(),
133
140
  source: this.CLASS_NAME,
@@ -172,7 +179,7 @@ class FastifyWebServer {
172
179
  err = errorAndCode.error;
173
180
  httpStatusCode = errorAndCode.httpStatusCode;
174
181
  }
175
- await this._loggingConnector?.log({
182
+ await this._logging?.log({
176
183
  level: "error",
177
184
  ts: Date.now(),
178
185
  source: this.CLASS_NAME,
@@ -193,7 +200,7 @@ class FastifyWebServer {
193
200
  async start() {
194
201
  const host = this._options?.host ?? FastifyWebServer._DEFAULT_HOST;
195
202
  const port = this._options?.port ?? FastifyWebServer._DEFAULT_PORT;
196
- await this._loggingConnector?.log({
203
+ await this._logging?.log({
197
204
  level: "info",
198
205
  ts: Date.now(),
199
206
  source: this.CLASS_NAME,
@@ -208,7 +215,7 @@ class FastifyWebServer {
208
215
  await this._fastify.listen({ port, host });
209
216
  const addresses = this._fastify.addresses();
210
217
  const protocol = Is.object(this._fastify.initialConfig.https) ? "https://" : "http://";
211
- await this._loggingConnector?.log({
218
+ await this._logging?.log({
212
219
  level: "info",
213
220
  ts: Date.now(),
214
221
  source: this.CLASS_NAME,
@@ -222,7 +229,7 @@ class FastifyWebServer {
222
229
  this._started = true;
223
230
  }
224
231
  catch (err) {
225
- await this._loggingConnector?.log({
232
+ await this._logging?.log({
226
233
  level: "error",
227
234
  ts: Date.now(),
228
235
  source: this.CLASS_NAME,
@@ -240,7 +247,7 @@ class FastifyWebServer {
240
247
  if (this._started) {
241
248
  this._started = false;
242
249
  await this._fastify.close();
243
- await this._loggingConnector?.log({
250
+ await this._logging?.log({
244
251
  level: "info",
245
252
  ts: Date.now(),
246
253
  source: this.CLASS_NAME,
@@ -261,7 +268,7 @@ class FastifyWebServer {
261
268
  if (!path.startsWith("/")) {
262
269
  path = `/${path}`;
263
270
  }
264
- await this._loggingConnector?.log({
271
+ await this._logging?.log({
265
272
  level: "info",
266
273
  ts: Date.now(),
267
274
  source: this.CLASS_NAME,
@@ -286,30 +293,33 @@ class FastifyWebServer {
286
293
  for (const socketRoute of socketRoutes) {
287
294
  const path = StringHelper.trimLeadingSlashes(StringHelper.trimTrailingSlashes(socketRoute.path));
288
295
  const pathParts = path.split("/");
289
- await this._loggingConnector?.log({
296
+ const namespace = `/${pathParts[0]}`;
297
+ const topic = pathParts.slice(1).join("/");
298
+ await this._logging?.log({
290
299
  level: "info",
291
300
  ts: Date.now(),
292
301
  source: this.CLASS_NAME,
293
302
  message: `${FastifyWebServer._CLASS_NAME_CAMEL_CASE}.socketRouteAdded`,
294
- data: { route: `/${path}` }
303
+ data: {
304
+ handshakePath: this._socketConfig.path,
305
+ namespace,
306
+ eventName: topic
307
+ }
295
308
  });
296
- const socketNamespace = io.of(`/${pathParts[0]}`);
297
- const topic = pathParts.slice(1).join("/");
309
+ const socketNamespace = io.of(namespace);
298
310
  socketNamespace.on("connection", async (socket) => {
299
- const httpServerRequest = {
311
+ const socketServerRequest = {
300
312
  method: HttpMethod.GET,
301
313
  url: socket.handshake.url,
302
314
  query: socket.handshake.query,
303
- headers: socket.handshake.headers
315
+ headers: socket.handshake.headers,
316
+ socketId: socket.id
304
317
  };
305
318
  // Pass the connected information on to any processors
306
319
  try {
307
- const processorState = {
308
- socketId: socket.id
309
- };
310
320
  for (const socketRouteProcessor of socketRouteProcessors) {
311
- if (Is.function(socketRouteProcessor.connected)) {
312
- await socketRouteProcessor.connected(httpServerRequest, socketRoute, processorState);
321
+ if (socketRouteProcessor.connected) {
322
+ await socketRouteProcessor.connected(socketServerRequest, socketRoute, this._loggingComponentType);
313
323
  }
314
324
  }
315
325
  }
@@ -321,13 +331,10 @@ class FastifyWebServer {
321
331
  }
322
332
  socket.on("disconnect", async () => {
323
333
  try {
324
- const processorState = {
325
- socketId: socket.id
326
- };
327
334
  // The socket disconnected so notify any processors
328
335
  for (const socketRouteProcessor of socketRouteProcessors) {
329
- if (Is.function(socketRouteProcessor.disconnected)) {
330
- await socketRouteProcessor.disconnected(httpServerRequest, socketRoute, processorState);
336
+ if (socketRouteProcessor.disconnected) {
337
+ await socketRouteProcessor.disconnected(socketServerRequest, socketRoute, this._loggingComponentType);
331
338
  }
332
339
  }
333
340
  }
@@ -384,19 +391,20 @@ class FastifyWebServer {
384
391
  */
385
392
  async runProcessorsRest(restRouteProcessors, restRoute, httpServerRequest, httpResponse, httpRequestIdentity, processorState) {
386
393
  try {
387
- for (const routeProcessor of restRouteProcessors) {
388
- if (Is.function(routeProcessor.pre)) {
389
- 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);
390
398
  }
391
399
  }
392
- for (const routeProcessor of restRouteProcessors) {
393
- if (Is.function(routeProcessor.process)) {
394
- 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);
395
403
  }
396
404
  }
397
- for (const routeProcessor of restRouteProcessors) {
398
- if (Is.function(routeProcessor.post)) {
399
- 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);
400
408
  }
401
409
  }
402
410
  }
@@ -405,6 +413,33 @@ class FastifyWebServer {
405
413
  HttpErrorHelper.buildResponse(httpResponse, error, httpStatusCode);
406
414
  }
407
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
+ }
408
443
  /**
409
444
  * Handle the incoming socket request.
410
445
  * @param socketRouteProcessors The hooks to process the incoming requests.
@@ -416,49 +451,50 @@ class FastifyWebServer {
416
451
  * @internal
417
452
  */
418
453
  async handleRequestSocket(socketRouteProcessors, socketRoute, socket, fullPath, emitTopic, request) {
419
- const httpServerRequest = {
454
+ const socketServerRequest = {
420
455
  method: HttpMethod.GET,
421
456
  url: fullPath,
422
457
  query: socket.handshake.query,
423
458
  headers: socket.handshake.headers,
424
- body: request.body
459
+ body: request.body,
460
+ socketId: socket.id
425
461
  };
426
462
  const httpResponse = {};
427
463
  const httpRequestIdentity = {};
428
- const processorState = {
429
- socketId: socket.id
430
- };
431
- delete httpServerRequest.query?.EIO;
432
- delete httpServerRequest.query?.transport;
433
- 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) => {
434
468
  await socket.emit(topic, response);
435
469
  });
436
470
  }
437
471
  /**
438
472
  * Run the socket processors for the route.
473
+ * @param socketId The id of the socket.
439
474
  * @param socketRouteProcessors The processors to run.
440
475
  * @param socketRoute The route to process.
441
- * @param httpServerRequest The incoming request.
476
+ * @param socketServerRequest The incoming request.
442
477
  * @param httpResponse The outgoing response.
443
478
  * @param httpRequestIdentity The identity context for the request.
444
479
  * @param processorState The state handed through the processors.
445
480
  * @param requestTopic The topic of the request.
446
481
  * @internal
447
482
  */
448
- 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);
449
485
  // Custom emit method which will also call the post processors
450
486
  const postProcessEmit = async (topic, response, responseProcessorState) => {
451
487
  await responseEmitter(topic, response);
452
488
  try {
453
489
  // The post processors are called after the response has been emitted
454
- for (const postSocketRouteProcessor of socketRouteProcessors) {
455
- if (Is.function(postSocketRouteProcessor.post)) {
456
- 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);
457
493
  }
458
494
  }
459
495
  }
460
496
  catch (err) {
461
- this._loggingConnector?.log({
497
+ this._logging?.log({
462
498
  level: "error",
463
499
  ts: Date.now(),
464
500
  source: this.CLASS_NAME,
@@ -471,9 +507,9 @@ class FastifyWebServer {
471
507
  }
472
508
  };
473
509
  try {
474
- for (const socketRouteProcessor of socketRouteProcessors) {
475
- if (Is.function(socketRouteProcessor.pre)) {
476
- 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);
477
513
  }
478
514
  }
479
515
  // We always call all the processors regardless of any response set by a previous processor.
@@ -482,11 +518,11 @@ class FastifyWebServer {
482
518
  if (!Is.empty(httpResponse.statusCode)) {
483
519
  await postProcessEmit(requestTopic, httpResponse, processorState);
484
520
  }
485
- for (const socketRouteProcessor of socketRouteProcessors) {
486
- if (Is.function(socketRouteProcessor.process)) {
487
- 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) => {
488
524
  await postProcessEmit(topic, processResponse, processorState);
489
- });
525
+ }, this._loggingComponentType);
490
526
  }
491
527
  }
492
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,154 @@
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
+
3
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)
4
153
 
5
154
 
@@ -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.2-next.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,16 +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.2-next.1",
20
- "@twin.org/api-models": "0.0.2-next.1",
21
- "@twin.org/api-processors": "0.0.2-next.1",
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
22
  "@twin.org/core": "next",
23
23
  "@twin.org/logging-models": "next",
24
24
  "@twin.org/nameof": "next",
25
25
  "@twin.org/web": "next",
26
- "fastify": "5.3.3",
26
+ "fastify": "5.5.0",
27
27
  "socket.io": "4.8.1"
28
28
  },
29
29
  "main": "./dist/cjs/index.cjs",