From 50d8129f82a6bb48999ccc44f68515551c9ee2df Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Wed, 12 Nov 2025 16:15:59 +0100 Subject: [PATCH] refactor: add UUIDv4 type definition MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../ui-server/AbstractUIServer.ts | 20 ++++------- .../ui-services/AbstractUIService.ts | 34 ++++++------------- src/types/ChargingStationWorker.ts | 3 +- src/types/UIProtocol.ts | 14 +++----- src/types/UUID.ts | 6 ++++ src/types/WorkerBroadcastChannel.ts | 8 ++--- src/types/index.ts | 1 + src/types/ocpp/2.0/Requests.ts | 2 +- src/types/ocpp/2.0/Responses.ts | 2 +- src/types/ocpp/2.0/Transaction.ts | 2 +- src/utils/Utils.ts | 7 ++-- src/worker/WorkerSet.ts | 11 ++---- src/worker/WorkerTypes.ts | 9 ++++- ...uestService-RequestStopTransaction.test.ts | 22 ++++++------ 14 files changed, 61 insertions(+), 80 deletions(-) create mode 100644 src/types/UUID.ts diff --git a/src/charging-station/ui-server/AbstractUIServer.ts b/src/charging-station/ui-server/AbstractUIServer.ts index f9bd5575..312a97c4 100644 --- a/src/charging-station/ui-server/AbstractUIServer.ts +++ b/src/charging-station/ui-server/AbstractUIServer.ts @@ -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 protected readonly uiServices: Map @@ -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() this.uiServices = new Map() } 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) } diff --git a/src/charging-station/ui-server/ui-services/AbstractUIService.ts b/src/charging-station/ui-server/ui-services/AbstractUIService.ts index c67f21b3..4bfbb1ba 100644 --- a/src/charging-station/ui-server/ui-services/AbstractUIService.ts +++ b/src/charging-station/ui-server/ui-services/AbstractUIService.ts @@ -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 - private readonly broadcastChannelRequests: Map< - `${string}-${string}-${string}-${string}-${string}`, - number - > + private readonly broadcastChannelRequests: Map 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() } - 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 { - 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 { @@ -363,7 +351,7 @@ export abstract class AbstractUIService { } private sendBroadcastChannelRequest ( - uuid: `${string}-${string}-${string}-${string}-${string}`, + uuid: UUIDv4, procedureName: BroadcastChannelProcedureName, payload: BroadcastChannelRequestPayload ): void { diff --git a/src/types/ChargingStationWorker.ts b/src/types/ChargingStationWorker.ts index 80d1a13f..a0fff875 100644 --- a/src/types/ChargingStationWorker.ts +++ b/src/types/ChargingStationWorker.ts @@ -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 { data: T event: ChargingStationWorkerMessageEvents - uuid?: string + uuid?: UUIDv4 } export type ChargingStationWorkerMessageData = ChargingStationData | Statistics diff --git a/src/types/UIProtocol.ts b/src/types/UIProtocol.ts index 9418d8be..22238732 100644 --- a/src/types/UIProtocol.ts +++ b/src/types/UIProtocol.ts @@ -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 | Promise | 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 index 00000000..bb2a5d87 --- /dev/null +++ b/src/types/UUID.ts @@ -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}` diff --git a/src/types/WorkerBroadcastChannel.ts b/src/types/WorkerBroadcastChannel.ts index 97811d93..5234e58a 100644 --- a/src/types/WorkerBroadcastChannel.ts +++ b/src/types/WorkerBroadcastChannel.ts @@ -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 { diff --git a/src/types/index.ts b/src/types/index.ts index be3cb70c..6d9b10f4 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -340,6 +340,7 @@ export { type ResponsePayload, ResponseStatus, } from './UIProtocol.js' +export type { UUIDv4 } from './UUID.js' export { WebSocketCloseEventStatusCode, WebSocketCloseEventStatusString, diff --git a/src/types/ocpp/2.0/Requests.ts b/src/types/ocpp/2.0/Requests.ts index cf7be317..add98144 100644 --- a/src/types/ocpp/2.0/Requests.ts +++ b/src/types/ocpp/2.0/Requests.ts @@ -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 { diff --git a/src/types/ocpp/2.0/Responses.ts b/src/types/ocpp/2.0/Responses.ts index f85cefd5..1365c6a9 100644 --- a/src/types/ocpp/2.0/Responses.ts +++ b/src/types/ocpp/2.0/Responses.ts @@ -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 { diff --git a/src/types/ocpp/2.0/Transaction.ts b/src/types/ocpp/2.0/Transaction.ts index 4cc21496..50fc4a9e 100644 --- a/src/types/ocpp/2.0/Transaction.ts +++ b/src/types/ocpp/2.0/Transaction.ts @@ -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 { diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 87adf4e6..1fbcf22a 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -18,6 +18,7 @@ import { type JsonType, MapStringifyFormat, type TimestampedData, + type UUIDv4, WebSocketCloseEventStatusString, } from '../types/index.js' @@ -122,13 +123,11 @@ export const mergeDeepRight = (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 } diff --git a/src/worker/WorkerSet.ts b/src/worker/WorkerSet.ts index bdaa108d..c5ae6ed2 100644 --- a/src/worker/WorkerSet.ts +++ b/src/worker/WorkerSet.ts @@ -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 extends Worke return this.workerSet.size } - private readonly promiseResponseMap: Map< - `${string}-${string}-${string}-${string}-${string}`, - ResponseWrapper - > + private readonly promiseResponseMap: Map> private started: boolean private readonly workerSet: Set @@ -76,10 +74,7 @@ export class WorkerSet extends Worke throw new RangeError('Elements per worker must be greater than zero') } this.workerSet = new Set() - this.promiseResponseMap = new Map< - `${string}-${string}-${string}-${string}-${string}`, - ResponseWrapper - >() + this.promiseResponseMap = new Map>() if (this.workerOptions.poolOptions?.enableEvents === true) { this.emitter = new EventEmitterAsyncResource({ name: 'workerset' }) } diff --git a/src/worker/WorkerTypes.ts b/src/worker/WorkerTypes.ts index 9a800870..b4410cc0 100644 --- a/src/worker/WorkerTypes.ts +++ b/src/worker/WorkerTypes.ts @@ -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 export interface WorkerDataError extends WorkerData { @@ -52,7 +59,7 @@ export type WorkerEvents = PoolEvent | WorkerSetEvents export interface WorkerMessage { data: T event: WorkerMessageEvents - uuid: `${string}-${string}-${string}-${string}-${string}` + uuid: UUIDv4 } export interface WorkerOptions extends Record { diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts index 51deb676..46f1b7f5 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-RequestStopTransaction.test.ts @@ -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( -- 2.43.0