]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
refactor: add UUIDv4 type definition
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 12 Nov 2025 15:15:59 +0000 (16:15 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 12 Nov 2025 15:15:59 +0000 (16:15 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
14 files changed:
src/charging-station/ui-server/AbstractUIServer.ts
src/charging-station/ui-server/ui-services/AbstractUIService.ts
src/types/ChargingStationWorker.ts
src/types/UIProtocol.ts
src/types/UUID.ts [new file with mode: 0644]
src/types/WorkerBroadcastChannel.ts
src/types/index.ts
src/types/ocpp/2.0/Requests.ts
src/types/ocpp/2.0/Responses.ts
src/types/ocpp/2.0/Transaction.ts
src/utils/Utils.ts
src/worker/WorkerSet.ts
src/worker/WorkerTypes.ts
tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts

index f9bd5575441fb4a5322d349964d514639d244a17..312a97c4aab93dc8146a9f8e55528a0c5673fe5a 100644 (file)
@@ -18,6 +18,7 @@ import {
   type RequestPayload,
   type ResponsePayload,
   type UIServerConfiguration,
+  type UUIDv4,
 } from '../../types/index.js'
 import { isEmpty, logger } from '../../utils/index.js'
 import { UIServiceFactory } from './ui-services/UIServiceFactory.js'
@@ -27,10 +28,7 @@ const moduleName = 'AbstractUIServer'
 
 export abstract class AbstractUIServer {
   protected readonly httpServer: Http2Server | Server
-  protected readonly responseHandlers: Map<
-    `${string}-${string}-${string}-${string}-${string}`,
-    ServerResponse | WebSocket
-  >
+  protected readonly responseHandlers: Map<UUIDv4, ServerResponse | WebSocket>
 
   protected readonly uiServices: Map<ProtocolVersion, AbstractUIService>
 
@@ -53,25 +51,19 @@ export abstract class AbstractUIServer {
           `Unsupported application protocol version ${this.uiServerConfiguration.version} in '${ConfigurationSection.uiServer}' configuration section`
         )
     }
-    this.responseHandlers = new Map<
-      `${string}-${string}-${string}-${string}-${string}`,
-      ServerResponse | WebSocket
-    >()
+    this.responseHandlers = new Map<UUIDv4, ServerResponse | WebSocket>()
     this.uiServices = new Map<ProtocolVersion, AbstractUIService>()
   }
 
   public buildProtocolRequest (
-    uuid: `${string}-${string}-${string}-${string}-${string}`,
+    uuid: UUIDv4,
     procedureName: ProcedureName,
     requestPayload: RequestPayload
   ): ProtocolRequest {
     return [uuid, procedureName, requestPayload]
   }
 
-  public buildProtocolResponse (
-    uuid: `${string}-${string}-${string}-${string}-${string}`,
-    responsePayload: ResponsePayload
-  ): ProtocolResponse {
+  public buildProtocolResponse (uuid: UUIDv4, responsePayload: ResponsePayload): ProtocolResponse {
     return [uuid, responsePayload]
   }
 
@@ -104,7 +96,7 @@ export abstract class AbstractUIServer {
     return this.chargingStationTemplates.has(template)
   }
 
-  public hasResponseHandler (uuid: `${string}-${string}-${string}-${string}-${string}`): boolean {
+  public hasResponseHandler (uuid: UUIDv4): boolean {
     return this.responseHandlers.has(uuid)
   }
 
index c67f21b3925f4bd7cdef91bf0de54db7986bfc5b..4bfbb1ba999de166173a6bd5b15ea8ee5efccc61 100644 (file)
@@ -18,6 +18,7 @@ import {
   type ResponsePayload,
   ResponseStatus,
   type StorageConfiguration,
+  type UUIDv4,
 } from '../../../types/index.js'
 import { Configuration, isAsyncFunction, isNotEmptyArray, logger } from '../../../utils/index.js'
 import { Bootstrap } from '../../Bootstrap.js'
@@ -72,10 +73,7 @@ export abstract class AbstractUIService {
   ])
 
   protected readonly requestHandlers: Map<ProcedureName, ProtocolRequestHandler>
-  private readonly broadcastChannelRequests: Map<
-    `${string}-${string}-${string}-${string}-${string}`,
-    number
-  >
+  private readonly broadcastChannelRequests: Map<UUIDv4, number>
 
   private readonly uiServer: AbstractUIServer
   private readonly uiServiceWorkerBroadcastChannel: UIServiceWorkerBroadcastChannel
@@ -94,21 +92,14 @@ export abstract class AbstractUIService {
       [ProcedureName.STOP_SIMULATOR, this.handleStopSimulator.bind(this)],
     ])
     this.uiServiceWorkerBroadcastChannel = new UIServiceWorkerBroadcastChannel(this)
-    this.broadcastChannelRequests = new Map<
-      `${string}-${string}-${string}-${string}-${string}`,
-      number
-    >()
+    this.broadcastChannelRequests = new Map<UUIDv4, number>()
   }
 
-  public deleteBroadcastChannelRequest (
-    uuid: `${string}-${string}-${string}-${string}-${string}`
-  ): void {
+  public deleteBroadcastChannelRequest (uuid: UUIDv4): void {
     this.broadcastChannelRequests.delete(uuid)
   }
 
-  public getBroadcastChannelExpectedResponses (
-    uuid: `${string}-${string}-${string}-${string}-${string}`
-  ): number {
+  public getBroadcastChannelExpectedResponses (uuid: UUIDv4): number {
     return this.broadcastChannelRequests.get(uuid) ?? 0
   }
 
@@ -117,7 +108,7 @@ export abstract class AbstractUIService {
   }
 
   public async requestHandler (request: ProtocolRequest): Promise<ProtocolResponse | undefined> {
-    let uuid: `${string}-${string}-${string}-${string}-${string}` | undefined
+    let uuid: undefined | UUIDv4
     let command: ProcedureName | undefined
     let requestPayload: RequestPayload | undefined
     let responsePayload: ResponsePayload | undefined
@@ -175,7 +166,7 @@ export abstract class AbstractUIService {
   }
 
   // public sendRequest (
-  //   uuid: `${string}-${string}-${string}-${string}-${string}`,
+  //   uuid: UUIDv4,
   //   procedureName: ProcedureName,
   //   requestPayload: RequestPayload
   // ): void {
@@ -184,10 +175,7 @@ export abstract class AbstractUIService {
   //   )
   // }
 
-  public sendResponse (
-    uuid: `${string}-${string}-${string}-${string}-${string}`,
-    responsePayload: ResponsePayload
-  ): void {
+  public sendResponse (uuid: UUIDv4, responsePayload: ResponsePayload): void {
     if (this.uiServer.hasResponseHandler(uuid)) {
       this.uiServer.sendResponse(this.uiServer.buildProtocolResponse(uuid, responsePayload))
     } else {
@@ -204,7 +192,7 @@ export abstract class AbstractUIService {
   }
 
   protected handleProtocolRequest (
-    uuid: `${string}-${string}-${string}-${string}-${string}`,
+    uuid: UUIDv4,
     procedureName: ProcedureName,
     payload: RequestPayload
   ): void {
@@ -217,7 +205,7 @@ export abstract class AbstractUIService {
   }
 
   private async handleAddChargingStations (
-    _uuid?: `${string}-${string}-${string}-${string}-${string}`,
+    _uuid?: UUIDv4,
     _procedureName?: ProcedureName,
     requestPayload?: RequestPayload
   ): Promise<ResponsePayload> {
@@ -363,7 +351,7 @@ export abstract class AbstractUIService {
   }
 
   private sendBroadcastChannelRequest (
-    uuid: `${string}-${string}-${string}-${string}-${string}`,
+    uuid: UUIDv4,
     procedureName: BroadcastChannelProcedureName,
     payload: BroadcastChannelRequestPayload
   ): void {
index 80d1a13f35e7600c00bc91ec295a245226f4b002..a0fff8758eb72af2c723b05b8b5a2d4a3342d717 100644 (file)
@@ -9,6 +9,7 @@ import type { EvseStatus } from './Evse.js'
 import type { JsonObject } from './JsonType.js'
 import type { BootNotificationResponse } from './ocpp/Responses.js'
 import type { Statistics } from './Statistics.js'
+import type { UUIDv4 } from './UUID.js'
 
 import { ChargingStationEvents } from './ChargingStationEvents.js'
 
@@ -52,7 +53,7 @@ export interface ChargingStationWorkerData extends WorkerData {
 export interface ChargingStationWorkerMessage<T extends ChargingStationWorkerMessageData> {
   data: T
   event: ChargingStationWorkerMessageEvents
-  uuid?: string
+  uuid?: UUIDv4
 }
 
 export type ChargingStationWorkerMessageData = ChargingStationData | Statistics
index 9418d8be20eb1c14242c25695754f4526df070cb..222387322497e103784beed85d7505fa0c1418ff 100644 (file)
@@ -1,4 +1,5 @@
 import type { JsonObject } from './JsonType.js'
+import type { UUIDv4 } from './UUID.js'
 import type { BroadcastChannelResponsePayload } from './WorkerBroadcastChannel.js'
 
 export enum ApplicationProtocol {
@@ -52,22 +53,15 @@ export enum ResponseStatus {
   SUCCESS = 'success',
 }
 
-export type ProtocolRequest = [
-  `${string}-${string}-${string}-${string}-${string}`,
-  ProcedureName,
-  RequestPayload
-]
+export type ProtocolRequest = [UUIDv4, ProcedureName, RequestPayload]
 
 export type ProtocolRequestHandler = (
-  uuid?: `${string}-${string}-${string}-${string}-${string}`,
+  uuid?: UUIDv4,
   procedureName?: ProcedureName,
   payload?: RequestPayload
 ) => Promise<ResponsePayload> | Promise<undefined> | ResponsePayload | undefined
 
-export type ProtocolResponse = [
-  `${string}-${string}-${string}-${string}-${string}`,
-  ResponsePayload
-]
+export type ProtocolResponse = [UUIDv4, ResponsePayload]
 
 export interface RequestPayload extends JsonObject {
   connectorIds?: number[]
diff --git a/src/types/UUID.ts b/src/types/UUID.ts
new file mode 100644 (file)
index 0000000..bb2a5d8
--- /dev/null
@@ -0,0 +1,6 @@
+/**
+ * UUIDv4 type representing a standard UUID format
+ * Pattern: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
+ * where x is any hexadecimal digit and y is one of 8, 9, A, or B
+ */
+export type UUIDv4 = `${string}-${string}-${string}-${string}-${string}`
index 97811d934f1191be6d3e72fd08ad6070dbbf8020..5234e58af8fd76878b82438f3855eae04e3d9b2d 100644 (file)
@@ -1,4 +1,5 @@
 import type { RequestPayload, ResponsePayload } from './UIProtocol.js'
+import type { UUIDv4 } from './UUID.js'
 
 export enum BroadcastChannelProcedureName {
   AUTHORIZE = 'authorize',
@@ -22,7 +23,7 @@ export enum BroadcastChannelProcedureName {
 }
 
 export type BroadcastChannelRequest = [
-  `${string}-${string}-${string}-${string}-${string}`,
+  UUIDv4,
   BroadcastChannelProcedureName,
   BroadcastChannelRequestPayload
 ]
@@ -32,10 +33,7 @@ export interface BroadcastChannelRequestPayload extends RequestPayload {
   transactionId?: number
 }
 
-export type BroadcastChannelResponse = [
-  `${string}-${string}-${string}-${string}-${string}`,
-  BroadcastChannelResponsePayload
-]
+export type BroadcastChannelResponse = [UUIDv4, BroadcastChannelResponsePayload]
 
 export interface BroadcastChannelResponsePayload
   extends Omit<ResponsePayload, 'hashIdsFailed' | 'hashIdsSucceeded' | 'responsesFailed'> {
index be3cb70c41695e272cda2027e8126dda33040beb..6d9b10f410082ab0064d0933b467cc9cafbd8f91 100644 (file)
@@ -340,6 +340,7 @@ export {
   type ResponsePayload,
   ResponseStatus,
 } from './UIProtocol.js'
+export type { UUIDv4 } from './UUID.js'
 export {
   WebSocketCloseEventStatusCode,
   WebSocketCloseEventStatusString,
index cf7be3179d553b7c8a0350bb2a4ec1b6c61f4274..add9814470a0a18737ae7a7a112d61a94becd542 100644 (file)
@@ -84,7 +84,7 @@ export interface OCPP20RequestStartTransactionRequest extends JsonObject {
 
 export interface OCPP20RequestStopTransactionRequest extends JsonObject {
   customData?: CustomDataType
-  transactionId: `${string}-${string}-${string}-${string}-${string}`
+  transactionId: UUIDv4
 }
 
 export interface OCPP20ResetRequest extends JsonObject {
index f85cefd57723cdf40a7b1d4e2098dcaaf55f246a..1365c6a92336f2007f74d6e7f5ef9f7ea1002307 100644 (file)
@@ -54,7 +54,7 @@ export interface OCPP20RequestStartTransactionResponse extends JsonObject {
   customData?: CustomDataType
   status: RequestStartStopStatusEnumType
   statusInfo?: StatusInfoType
-  transactionId?: `${string}-${string}-${string}-${string}-${string}`
+  transactionId?: UUIDv4
 }
 
 export interface OCPP20RequestStopTransactionResponse extends JsonObject {
index 4cc2149673c1d758da5072c6ee0de94febca4ac8..50fc4a9e207a31221b6545cb3cf66c08a5992771 100644 (file)
@@ -252,7 +252,7 @@ export interface OCPP20TransactionType extends JsonObject {
   remoteStartId?: number
   stoppedReason?: OCPP20ReasonEnumType
   timeSpentCharging?: number
-  transactionId: `${string}-${string}-${string}-${string}-${string}`
+  transactionId: UUIDv4
 }
 
 export interface RelativeTimeIntervalType extends JsonObject {
index 87adf4e6a4fac1be14c1c94b3411b8787b9866c9..1fbcf22aaf9b1c9616c27d4d062f998f27f358aa 100644 (file)
@@ -18,6 +18,7 @@ import {
   type JsonType,
   MapStringifyFormat,
   type TimestampedData,
+  type UUIDv4,
   WebSocketCloseEventStatusString,
 } from '../types/index.js'
 
@@ -122,13 +123,11 @@ export const mergeDeepRight = <T extends object, S extends object>(target: T, so
   return output as T
 }
 
-export const generateUUID = (): `${string}-${string}-${string}-${string}-${string}` => {
+export const generateUUID = (): UUIDv4 => {
   return randomUUID()
 }
 
-export const validateUUID = (
-  uuid: unknown
-): uuid is `${string}-${string}-${string}-${string}-${string}` => {
+export const validateUUID = (uuid: unknown): uuid is UUIDv4 => {
   if (typeof uuid !== 'string') {
     return false
   }
index bdaa108d45a073f096b4dcb1cac232c0347f5d1c..c5ae6ed2b70c33a986bd02c04fb86a83d084ed8b 100644 (file)
@@ -8,6 +8,7 @@ import { WorkerAbstract } from './WorkerAbstract.js'
 import { EMPTY_FUNCTION, workerSetVersion } from './WorkerConstants.js'
 import {
   type SetInfo,
+  type UUIDv4,
   type WorkerData,
   type WorkerMessage,
   WorkerMessageEvents,
@@ -50,10 +51,7 @@ export class WorkerSet<D extends WorkerData, R extends WorkerData> extends Worke
     return this.workerSet.size
   }
 
-  private readonly promiseResponseMap: Map<
-    `${string}-${string}-${string}-${string}-${string}`,
-    ResponseWrapper<R>
-  >
+  private readonly promiseResponseMap: Map<UUIDv4, ResponseWrapper<R>>
 
   private started: boolean
   private readonly workerSet: Set<WorkerSetElement>
@@ -76,10 +74,7 @@ export class WorkerSet<D extends WorkerData, R extends WorkerData> extends Worke
       throw new RangeError('Elements per worker must be greater than zero')
     }
     this.workerSet = new Set<WorkerSetElement>()
-    this.promiseResponseMap = new Map<
-      `${string}-${string}-${string}-${string}-${string}`,
-      ResponseWrapper<R>
-    >()
+    this.promiseResponseMap = new Map<UUIDv4, ResponseWrapper<R>>()
     if (this.workerOptions.poolOptions?.enableEvents === true) {
       this.emitter = new EventEmitterAsyncResource({ name: 'workerset' })
     }
index 9a800870d9b4794d3fac63afbb643a2baf1e89ca..b4410cc0467277232130a2fb27bf60edb6e86307 100644 (file)
@@ -33,6 +33,13 @@ export interface SetInfo {
   worker: string
 }
 
+/**
+ * UUIDv4 type representing a standard UUID format
+ * Pattern: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
+ * where x is any hexadecimal digit and y is one of 8, 9, A, or B
+ */
+export type UUIDv4 = `${string}-${string}-${string}-${string}-${string}`
+
 export type WorkerData = Record<string, unknown>
 
 export interface WorkerDataError extends WorkerData {
@@ -52,7 +59,7 @@ export type WorkerEvents = PoolEvent | WorkerSetEvents
 export interface WorkerMessage<T extends WorkerData> {
   data: T
   event: WorkerMessageEvents
-  uuid: `${string}-${string}-${string}-${string}-${string}`
+  uuid: UUIDv4
 }
 
 export interface WorkerOptions extends Record<string, unknown> {
index 51deb6760e8f56dface21690f0fbb302d2e9874e..46f1b7f5e2ab06f4bf4690b9366cc3177a8e8c19 100644 (file)
@@ -10,6 +10,7 @@ import type {
   OCPP20RequestStartTransactionRequest,
   OCPP20RequestStopTransactionRequest,
   OCPP20TransactionEventRequest,
+  UUIDv4,
 } from '../../../../src/types/index.js'
 
 import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js'
@@ -130,7 +131,7 @@ await describe('E02 - Remote Stop Transaction', async () => {
 
     // Create stop transaction request
     const stopRequest: OCPP20RequestStopTransactionRequest = {
-      transactionId: transactionId as `${string}-${string}-${string}-${string}-${string}`,
+      transactionId: transactionId as UUIDv4,
     }
 
     // Execute stop transaction
@@ -168,7 +169,7 @@ await describe('E02 - Remote Stop Transaction', async () => {
 
     // Stop the second transaction
     const stopRequest: OCPP20RequestStopTransactionRequest = {
-      transactionId: transactionId2 as `${string}-${string}-${string}-${string}-${string}`,
+      transactionId: transactionId2 as UUIDv4,
     }
 
     const response = await (incomingRequestService as any).handleRequestStopTransaction(
@@ -198,8 +199,7 @@ await describe('E02 - Remote Stop Transaction', async () => {
 
     const nonExistentTransactionId = 'non-existent-transaction-id'
     const stopRequest: OCPP20RequestStopTransactionRequest = {
-      transactionId:
-        nonExistentTransactionId as `${string}-${string}-${string}-${string}-${string}`,
+      transactionId: nonExistentTransactionId as UUIDv4,
     }
 
     const response = await (incomingRequestService as any).handleRequestStopTransaction(
@@ -220,7 +220,7 @@ await describe('E02 - Remote Stop Transaction', async () => {
     sentTransactionEvents = []
 
     const invalidRequest: OCPP20RequestStopTransactionRequest = {
-      transactionId: '' as `${string}-${string}-${string}-${string}-${string}`,
+      transactionId: '' as UUIDv4,
     }
 
     const response = await (incomingRequestService as any).handleRequestStopTransaction(
@@ -243,7 +243,7 @@ await describe('E02 - Remote Stop Transaction', async () => {
     // Create a transaction ID longer than 36 characters
     const tooLongTransactionId = 'a'.repeat(37)
     const invalidRequest: OCPP20RequestStopTransactionRequest = {
-      transactionId: tooLongTransactionId as `${string}-${string}-${string}-${string}-${string}`,
+      transactionId: tooLongTransactionId as UUIDv4,
     }
 
     const response = await (incomingRequestService as any).handleRequestStopTransaction(
@@ -284,7 +284,7 @@ await describe('E02 - Remote Stop Transaction', async () => {
     }
 
     const stopRequest: OCPP20RequestStopTransactionRequest = {
-      transactionId: testTransactionId as `${string}-${string}-${string}-${string}-${string}`,
+      transactionId: testTransactionId as UUIDv4,
     }
 
     const response = await (incomingRequestService as any).handleRequestStopTransaction(
@@ -345,7 +345,7 @@ await describe('E02 - Remote Stop Transaction', async () => {
 
     // Attempt to stop the transaction
     const stopRequest: OCPP20RequestStopTransactionRequest = {
-      transactionId: transactionId as `${string}-${string}-${string}-${string}-${string}`,
+      transactionId: transactionId as UUIDv4,
     }
 
     const response = await (incomingRequestService as any).handleRequestStopTransaction(
@@ -366,7 +366,7 @@ await describe('E02 - Remote Stop Transaction', async () => {
     const transactionId = await startTransaction(1, 400)
 
     const stopRequest: OCPP20RequestStopTransactionRequest = {
-      transactionId: transactionId as `${string}-${string}-${string}-${string}-${string}`,
+      transactionId: transactionId as UUIDv4,
     }
 
     const response = await (incomingRequestService as any).handleRequestStopTransaction(
@@ -398,7 +398,7 @@ await describe('E02 - Remote Stop Transaction', async () => {
         data: 'Custom stop transaction data',
         vendorId: 'TestVendor',
       },
-      transactionId: transactionId as `${string}-${string}-${string}-${string}-${string}`,
+      transactionId: transactionId as UUIDv4,
     }
 
     const response = await (incomingRequestService as any).handleRequestStopTransaction(
@@ -422,7 +422,7 @@ await describe('E02 - Remote Stop Transaction', async () => {
     const transactionId = await startTransaction(2, 600) // Use EVSE 2
 
     const stopRequest: OCPP20RequestStopTransactionRequest = {
-      transactionId: transactionId as `${string}-${string}-${string}-${string}-${string}`,
+      transactionId: transactionId as UUIDv4,
     }
 
     const response = await (incomingRequestService as any).handleRequestStopTransaction(