From 24d15716245469249b57e5aa790147d703d60a4e Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Thu, 25 Jan 2024 23:36:49 +0100 Subject: [PATCH] perf: cache only JSON payload validation functions MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../ocpp/1.6/OCPP16IncomingRequestService.ts | 263 +++++++----- .../ocpp/1.6/OCPP16RequestService.ts | 146 ++++--- .../ocpp/1.6/OCPP16ResponseService.ts | 405 +++++++++++------- .../ocpp/2.0/OCPP20IncomingRequestService.ts | 39 +- .../ocpp/2.0/OCPP20RequestService.ts | 48 ++- .../ocpp/2.0/OCPP20ResponseService.ts | 83 ++-- .../ocpp/OCPPIncomingRequestService.ts | 33 +- .../ocpp/OCPPRequestService.ts | 71 +-- .../ocpp/OCPPResponseService.ts | 44 +- 9 files changed, 655 insertions(+), 477 deletions(-) diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index f934d418..5d6172c4 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -4,7 +4,7 @@ import { createWriteStream, readdirSync } from 'node:fs' import { dirname, join, resolve } from 'node:path' import { URL, fileURLToPath } from 'node:url' -import type { JSONSchemaType } from 'ajv' +import type { ValidateFunction } from 'ajv' import { Client, type FTPResponse } from 'basic-ftp' import { type Interval, @@ -113,7 +113,11 @@ import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js' const moduleName = 'OCPP16IncomingRequestService' export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { - protected jsonSchemas: Map> + protected jsonSchemasValidateFunction: Map< + OCPP16IncomingRequestCommand, + ValidateFunction + > + private readonly incomingRequestHandlers: Map< OCPP16IncomingRequestCommand, IncomingRequestHandler @@ -194,142 +198,213 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { this.handleRequestCancelReservation.bind(this) as unknown as IncomingRequestHandler ] ]) - this.jsonSchemas = new Map>([ + this.jsonSchemasValidateFunction = new Map< + OCPP16IncomingRequestCommand, + ValidateFunction + >([ [ OCPP16IncomingRequestCommand.RESET, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/Reset.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/Reset.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CLEAR_CACHE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ClearCache.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ClearCache.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/UnlockConnector.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/UnlockConnector.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.GET_CONFIGURATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetConfiguration.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/GetConfiguration.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/TriggerMessage.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/TriggerMessage.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.DATA_TRANSFER, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DataTransfer.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/DataTransfer.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.RESERVE_NOW, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ReserveNow.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ReserveNow.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CANCEL_RESERVATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/CancelReservation.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/CancelReservation.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ] ]) this.validatePayload = this.validatePayload.bind(this) @@ -423,17 +498,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { commandName: OCPP16IncomingRequestCommand, commandPayload: JsonType ): boolean { - if (this.jsonSchemas.has(commandName)) { - return this.validateIncomingRequestPayload( - chargingStation, - commandName, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.jsonSchemas.get(commandName)!, - commandPayload - ) + if (this.jsonSchemasValidateFunction.has(commandName)) { + return this.validateIncomingRequestPayload(chargingStation, commandName, commandPayload) } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation` ) return false } diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index 37a5b2ba..a7bacae9 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -1,6 +1,6 @@ // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved. -import type { JSONSchemaType } from 'ajv' +import type { ValidateFunction } from 'ajv' import { OCPP16Constants } from './OCPP16Constants.js' import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js' @@ -32,93 +32,133 @@ import type { OCPPResponseService } from '../OCPPResponseService.js' const moduleName = 'OCPP16RequestService' export class OCPP16RequestService extends OCPPRequestService { - protected jsonSchemas: Map> + protected jsonSchemasValidateFunction: Map> public constructor (ocppResponseService: OCPPResponseService) { // if (new.target.name === moduleName) { // throw new TypeError(`Cannot construct ${new.target.name} instances directly`) // } super(OCPPVersion.VERSION_16, ocppResponseService) - this.jsonSchemas = new Map>([ + this.jsonSchemasValidateFunction = new Map>([ [ OCPP16RequestCommand.AUTHORIZE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/Authorize.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/Authorize.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.BOOT_NOTIFICATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/BootNotification.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/BootNotification.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.HEARTBEAT, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/Heartbeat.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/Heartbeat.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.METER_VALUES, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/MeterValues.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/MeterValues.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.STATUS_NOTIFICATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StatusNotification.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/StatusNotification.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.START_TRANSACTION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StartTransaction.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/StartTransaction.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.STOP_TRANSACTION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StopTransaction.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/StopTransaction.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.DATA_TRANSFER, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DataTransfer.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/DataTransfer.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotification.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotification.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ] ]) this.buildRequestPayload = this.buildRequestPayload.bind(this) diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index c47d2630..e65f8a8c 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -1,6 +1,6 @@ // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved. -import type { JSONSchemaType } from 'ajv' +import type { ValidateFunction } from 'ajv' import { secondsToMilliseconds } from 'date-fns' import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js' @@ -57,13 +57,13 @@ import { OCPPResponseService } from '../OCPPResponseService.js' const moduleName = 'OCPP16ResponseService' export class OCPP16ResponseService extends OCPPResponseService { - public jsonIncomingRequestResponseSchemas: Map< + public jsonSchemasIncomingRequestResponseValidateFunction: Map< OCPP16IncomingRequestCommand, - JSONSchemaType + ValidateFunction > + protected jsonSchemasValidateFunction: Map> private readonly responseHandlers: Map - private readonly jsonSchemas: Map> public constructor () { // if (new.target.name === moduleName) { @@ -97,224 +97,335 @@ export class OCPP16ResponseService extends OCPPResponseService { [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler], [OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, this.emptyResponseHandler] ]) - this.jsonSchemas = new Map>([ + this.jsonSchemasValidateFunction = new Map>([ [ OCPP16RequestCommand.BOOT_NOTIFICATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/BootNotificationResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/BootNotificationResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.HEARTBEAT, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/HeartbeatResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/HeartbeatResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.AUTHORIZE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/AuthorizeResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/AuthorizeResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.START_TRANSACTION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StartTransactionResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/StartTransactionResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.STOP_TRANSACTION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StopTransactionResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/StopTransactionResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.STATUS_NOTIFICATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.METER_VALUES, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/MeterValuesResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/MeterValuesResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.DATA_TRANSFER, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotificationResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotificationResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ] ]) - this.jsonIncomingRequestResponseSchemas = new Map([ + this.jsonSchemasIncomingRequestResponseValidateFunction = new Map< + OCPP16IncomingRequestCommand, + ValidateFunction + >([ [ OCPP16IncomingRequestCommand.RESET, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ResetResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ResetResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CLEAR_CACHE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ClearCacheResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ClearCacheResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.GET_CONFIGURATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetCompositeScheduleResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/GetCompositeScheduleResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.DATA_TRANSFER, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.RESERVE_NOW, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ReserveNowResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/ReserveNowResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP16IncomingRequestCommand.CANCEL_RESERVATION, - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/CancelReservationResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP16ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/1.6/CancelReservationResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ] ]) this.validatePayload = this.validatePayload.bind(this) @@ -374,17 +485,11 @@ export class OCPP16ResponseService extends OCPPResponseService { commandName: OCPP16RequestCommand, payload: JsonType ): boolean { - if (this.jsonSchemas.has(commandName)) { - return this.validateResponsePayload( - chargingStation, - commandName, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.jsonSchemas.get(commandName)!, - payload - ) + if (this.jsonSchemasValidateFunction.has(commandName)) { + return this.validateResponsePayload(chargingStation, commandName, payload) } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation` ) return false } diff --git a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts index 66418981..cfc58323 100644 --- a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts @@ -1,6 +1,6 @@ // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved. -import type { JSONSchemaType } from 'ajv' +import type { ValidateFunction } from 'ajv' import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js' import type { ChargingStation } from '../../../charging-station/index.js' @@ -19,7 +19,11 @@ import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js' const moduleName = 'OCPP20IncomingRequestService' export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { - protected jsonSchemas: Map> + protected jsonSchemasValidateFunction: Map< + OCPP20IncomingRequestCommand, + ValidateFunction + > + private readonly incomingRequestHandlers: Map< OCPP20IncomingRequestCommand, IncomingRequestHandler @@ -33,14 +37,21 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { this.incomingRequestHandlers = new Map([ [OCPP20IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)] ]) - this.jsonSchemas = new Map>([ + this.jsonSchemasValidateFunction = new Map< + OCPP20IncomingRequestCommand, + ValidateFunction + >([ [ OCPP20IncomingRequestCommand.CLEAR_CACHE, - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/ClearCacheRequest.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP20ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/2.0/ClearCacheRequest.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ] ]) this.validatePayload = this.validatePayload.bind(this) @@ -134,17 +145,11 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { commandName: OCPP20IncomingRequestCommand, commandPayload: JsonType ): boolean { - if (this.jsonSchemas.has(commandName)) { - return this.validateIncomingRequestPayload( - chargingStation, - commandName, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.jsonSchemas.get(commandName)!, - commandPayload - ) + if (this.jsonSchemasValidateFunction.has(commandName)) { + return this.validateIncomingRequestPayload(chargingStation, commandName, commandPayload) } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation` ) return false } diff --git a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts index e73b33cc..20bd333f 100644 --- a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts @@ -1,6 +1,6 @@ // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved. -import type { JSONSchemaType } from 'ajv' +import type { ValidateFunction } from 'ajv' import { OCPP20Constants } from './OCPP20Constants.js' import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js' @@ -24,37 +24,49 @@ import type { OCPPResponseService } from '../OCPPResponseService.js' const moduleName = 'OCPP20RequestService' export class OCPP20RequestService extends OCPPRequestService { - protected jsonSchemas: Map> + protected jsonSchemasValidateFunction: Map> public constructor (ocppResponseService: OCPPResponseService) { // if (new.target.name === moduleName) { // throw new TypeError(`Cannot construct ${new.target.name} instances directly`) // } super(OCPPVersion.VERSION_20, ocppResponseService) - this.jsonSchemas = new Map>([ + this.jsonSchemasValidateFunction = new Map>([ [ OCPP20RequestCommand.BOOT_NOTIFICATION, - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/BootNotificationRequest.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP20ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/2.0/BootNotificationRequest.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP20RequestCommand.HEARTBEAT, - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/HeartbeatRequest.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP20ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/2.0/HeartbeatRequest.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP20RequestCommand.STATUS_NOTIFICATION, - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/StatusNotificationRequest.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP20ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/2.0/StatusNotificationRequest.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ] ]) this.buildRequestPayload = this.buildRequestPayload.bind(this) diff --git a/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts b/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts index f35a8898..99ca6566 100644 --- a/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts @@ -1,6 +1,6 @@ // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved. -import type { JSONSchemaType } from 'ajv' +import type { ValidateFunction } from 'ajv' import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js' import { type ChargingStation, addConfigurationKey } from '../../../charging-station/index.js' @@ -25,13 +25,13 @@ import { OCPPResponseService } from '../OCPPResponseService.js' const moduleName = 'OCPP20ResponseService' export class OCPP20ResponseService extends OCPPResponseService { - public jsonIncomingRequestResponseSchemas: Map< + public jsonSchemasIncomingRequestResponseValidateFunction: Map< OCPP20IncomingRequestCommand, - JSONSchemaType + ValidateFunction > + protected jsonSchemasValidateFunction: Map> private readonly responseHandlers: Map - private readonly jsonSchemas: Map> public constructor () { // if (new.target.name === moduleName) { @@ -46,40 +46,59 @@ export class OCPP20ResponseService extends OCPPResponseService { [OCPP20RequestCommand.HEARTBEAT, this.emptyResponseHandler], [OCPP20RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler] ]) - this.jsonSchemas = new Map>([ + this.jsonSchemasValidateFunction = new Map>([ [ OCPP20RequestCommand.BOOT_NOTIFICATION, - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/BootNotificationResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP20ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/2.0/BootNotificationResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP20RequestCommand.HEARTBEAT, - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/HeartbeatResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP20ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/2.0/HeartbeatResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ], [ OCPP20RequestCommand.STATUS_NOTIFICATION, - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP20ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ] ]) - this.jsonIncomingRequestResponseSchemas = new Map([ + this.jsonSchemasIncomingRequestResponseValidateFunction = new Map< + OCPP20IncomingRequestCommand, + ValidateFunction + >([ [ OCPP20IncomingRequestCommand.CLEAR_CACHE, - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/ClearCacheResponse.json', - moduleName, - 'constructor' - ) + this.ajv + .compile( + OCPP20ServiceUtils.parseJsonSchemaFile( + 'assets/json-schemas/ocpp/2.0/ClearCacheResponse.json', + moduleName, + 'constructor' + ) + ) + .bind(this) ] ]) this.validatePayload = this.validatePayload.bind(this) @@ -139,17 +158,11 @@ export class OCPP20ResponseService extends OCPPResponseService { commandName: OCPP20RequestCommand, payload: JsonType ): boolean { - if (this.jsonSchemas.has(commandName)) { - return this.validateResponsePayload( - chargingStation, - commandName, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.jsonSchemas.get(commandName)!, - payload - ) + if (this.jsonSchemasValidateFunction.has(commandName)) { + return this.validateResponsePayload(chargingStation, commandName, payload) } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation` ) return false } diff --git a/src/charging-station/ocpp/OCPPIncomingRequestService.ts b/src/charging-station/ocpp/OCPPIncomingRequestService.ts index 4285e585..d0ff0229 100644 --- a/src/charging-station/ocpp/OCPPIncomingRequestService.ts +++ b/src/charging-station/ocpp/OCPPIncomingRequestService.ts @@ -1,4 +1,4 @@ -import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv' +import _Ajv, { type ValidateFunction } from 'ajv' import _ajvFormats from 'ajv-formats' import { OCPPConstants } from './OCPPConstants.js' @@ -23,9 +23,11 @@ const moduleName = 'OCPPIncomingRequestService' export abstract class OCPPIncomingRequestService { private static instance: OCPPIncomingRequestService | null = null private readonly version: OCPPVersion - private readonly ajv: Ajv - private readonly jsonValidateFunctions: Map> - protected abstract jsonSchemas: Map> + protected readonly ajv: Ajv + protected abstract jsonSchemasValidateFunction: Map< + IncomingRequestCommand, + ValidateFunction + > protected constructor (version: OCPPVersion) { this.version = version @@ -34,7 +36,6 @@ export abstract class OCPPIncomingRequestService { multipleOfPrecision: 2 }) ajvFormats(this.ajv) - this.jsonValidateFunctions = new Map>() this.incomingRequestHandler = this.incomingRequestHandler.bind(this) this.validateIncomingRequestPayload = this.validateIncomingRequestPayload.bind(this) } @@ -71,25 +72,24 @@ export abstract class OCPPIncomingRequestService { protected validateIncomingRequestPayload( chargingStation: ChargingStation, commandName: IncomingRequestCommand, - schema: JSONSchemaType, payload: T ): boolean { if (chargingStation.stationInfo?.ocppStrictCompliance === false) { return true } - const validate = this.getJsonIncomingRequestValidateFunction(commandName, schema) - if (validate(payload)) { + const validate = this.jsonSchemasValidateFunction.get(commandName) + if (validate?.(payload) === true) { return true } logger.error( `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestPayload: Command '${commandName}' incoming request PDU is invalid: %j`, - validate.errors + validate?.errors ) throw new OCPPError( - OCPPServiceUtils.ajvErrorsToErrorType(validate.errors), + OCPPServiceUtils.ajvErrorsToErrorType(validate?.errors), 'Incoming request PDU is invalid', commandName, - JSON.stringify(validate.errors, undefined, 2) + JSON.stringify(validate?.errors, undefined, 2) ) } @@ -101,17 +101,6 @@ export abstract class OCPPIncomingRequestService { return OCPPConstants.OCPP_RESPONSE_REJECTED } - private getJsonIncomingRequestValidateFunction( - commandName: IncomingRequestCommand, - schema: JSONSchemaType - ): ValidateFunction { - if (!this.jsonValidateFunctions.has(commandName)) { - this.jsonValidateFunctions.set(commandName, this.ajv.compile(schema).bind(this)) - } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return this.jsonValidateFunctions.get(commandName)! - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars public abstract incomingRequestHandler( chargingStation: ChargingStation, diff --git a/src/charging-station/ocpp/OCPPRequestService.ts b/src/charging-station/ocpp/OCPPRequestService.ts index c20a1d38..f956d381 100644 --- a/src/charging-station/ocpp/OCPPRequestService.ts +++ b/src/charging-station/ocpp/OCPPRequestService.ts @@ -1,4 +1,4 @@ -import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv' +import _Ajv, { type ValidateFunction } from 'ajv' import _ajvFormats from 'ajv-formats' import { OCPPConstants } from './OCPPConstants.js' @@ -45,10 +45,9 @@ const defaultRequestParams: RequestParams = { export abstract class OCPPRequestService { private static instance: OCPPRequestService | null = null private readonly version: OCPPVersion - private readonly ajv: Ajv private readonly ocppResponseService: OCPPResponseService - private readonly jsonValidateFunctions: Map> - protected abstract jsonSchemas: Map> + protected readonly ajv: Ajv + protected abstract jsonSchemasValidateFunction: Map> protected constructor (version: OCPPVersion, ocppResponseService: OCPPResponseService) { this.version = version @@ -57,7 +56,6 @@ export abstract class OCPPRequestService { multipleOfPrecision: 2 }) ajvFormats(this.ajv) - this.jsonValidateFunctions = new Map>() this.ocppResponseService = ocppResponseService this.requestHandler = this.requestHandler.bind(this) this.sendMessage = this.sendMessage.bind(this) @@ -160,45 +158,31 @@ export abstract class OCPPRequestService { if (chargingStation.stationInfo?.ocppStrictCompliance === false) { return true } - if (!this.jsonSchemas.has(commandName as RequestCommand)) { + if (!this.jsonSchemasValidateFunction.has(commandName as RequestCommand)) { logger.warn( `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: No JSON schema found for command '${commandName}' PDU validation` ) return true } - const validate = this.getJsonRequestValidateFunction(commandName as RequestCommand) + const validate = this.jsonSchemasValidateFunction.get(commandName as RequestCommand) payload = clone(payload) OCPPServiceUtils.convertDateToISOString(payload) - if (validate(payload)) { + if (validate?.(payload) === true) { return true } logger.error( `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: Command '${commandName}' request PDU is invalid: %j`, - validate.errors + validate?.errors ) // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). throw new OCPPError( - OCPPServiceUtils.ajvErrorsToErrorType(validate.errors), + OCPPServiceUtils.ajvErrorsToErrorType(validate?.errors), 'Request PDU is invalid', commandName, - JSON.stringify(validate.errors, undefined, 2) + JSON.stringify(validate?.errors, undefined, 2) ) } - private getJsonRequestValidateFunction( - commandName: RequestCommand - ): ValidateFunction { - if (!this.jsonValidateFunctions.has(commandName)) { - this.jsonValidateFunctions.set( - commandName, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.ajv.compile(this.jsonSchemas.get(commandName)!).bind(this) - ) - } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return this.jsonValidateFunctions.get(commandName)! - } - private validateIncomingRequestResponsePayload( chargingStation: ChargingStation, commandName: RequestCommand | IncomingRequestCommand, @@ -208,52 +192,37 @@ export abstract class OCPPRequestService { return true } if ( - !this.ocppResponseService.jsonIncomingRequestResponseSchemas.has( + !this.ocppResponseService.jsonSchemasIncomingRequestResponseValidateFunction.has( commandName as IncomingRequestCommand ) ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: No JSON schema found for command '${commandName}' PDU validation` + `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: No JSON schema validation function found for command '${commandName}' PDU validation` ) return true } - const validate = this.getJsonRequestResponseValidateFunction( - commandName as IncomingRequestCommand - ) + const validate = + this.ocppResponseService.jsonSchemasIncomingRequestResponseValidateFunction.get( + commandName as IncomingRequestCommand + ) payload = clone(payload) OCPPServiceUtils.convertDateToISOString(payload) - if (validate(payload)) { + if (validate?.(payload) === true) { return true } logger.error( - `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: Command '${commandName}' reponse PDU is invalid: %j`, - validate.errors + `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: Command '${commandName}' response PDU is invalid: %j`, + validate?.errors ) // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). throw new OCPPError( - OCPPServiceUtils.ajvErrorsToErrorType(validate.errors), + OCPPServiceUtils.ajvErrorsToErrorType(validate?.errors), 'Response PDU is invalid', commandName, - JSON.stringify(validate.errors, undefined, 2) + JSON.stringify(validate?.errors, undefined, 2) ) } - private getJsonRequestResponseValidateFunction( - commandName: IncomingRequestCommand - ): ValidateFunction { - if (!this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.has(commandName)) { - this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.set( - commandName, - this.ajv - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .compile(this.ocppResponseService.jsonIncomingRequestResponseSchemas.get(commandName)!) - .bind(this) - ) - } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.get(commandName)! - } - private async internalSendMessage ( chargingStation: ChargingStation, messageId: string, diff --git a/src/charging-station/ocpp/OCPPResponseService.ts b/src/charging-station/ocpp/OCPPResponseService.ts index e4745089..5ee6e2d0 100644 --- a/src/charging-station/ocpp/OCPPResponseService.ts +++ b/src/charging-station/ocpp/OCPPResponseService.ts @@ -1,4 +1,4 @@ -import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv' +import _Ajv, { type ValidateFunction } from 'ajv' import _ajvFormats from 'ajv-formats' import { OCPPServiceUtils } from './OCPPServiceUtils.js' @@ -20,19 +20,12 @@ const moduleName = 'OCPPResponseService' export abstract class OCPPResponseService { private static instance: OCPPResponseService | null = null - - public jsonIncomingRequestResponseValidateFunctions: Map< - IncomingRequestCommand, - ValidateFunction - > - private readonly version: OCPPVersion - private readonly ajv: Ajv - private readonly jsonRequestValidateFunctions: Map> - - public abstract jsonIncomingRequestResponseSchemas: Map< + protected readonly ajv: Ajv + protected abstract jsonSchemasValidateFunction: Map> + public abstract jsonSchemasIncomingRequestResponseValidateFunction: Map< IncomingRequestCommand, - JSONSchemaType + ValidateFunction > protected constructor (version: OCPPVersion) { @@ -42,11 +35,6 @@ export abstract class OCPPResponseService { multipleOfPrecision: 2 }) ajvFormats(this.ajv) - this.jsonRequestValidateFunctions = new Map>() - this.jsonIncomingRequestResponseValidateFunctions = new Map< - IncomingRequestCommand, - ValidateFunction - >() this.responseHandler = this.responseHandler.bind(this) this.validateResponsePayload = this.validateResponsePayload.bind(this) } @@ -61,41 +49,29 @@ export abstract class OCPPResponseService { protected validateResponsePayload( chargingStation: ChargingStation, commandName: RequestCommand, - schema: JSONSchemaType, payload: T ): boolean { if (chargingStation.stationInfo?.ocppStrictCompliance === false) { return true } - const validate = this.getJsonRequestValidateFunction(commandName, schema) - if (validate(payload)) { + const validate = this.jsonSchemasValidateFunction.get(commandName) + if (validate?.(payload) === true) { return true } logger.error( `${chargingStation.logPrefix()} ${moduleName}.validateResponsePayload: Command '${commandName}' response PDU is invalid: %j`, - validate.errors + validate?.errors ) throw new OCPPError( - OCPPServiceUtils.ajvErrorsToErrorType(validate.errors), + OCPPServiceUtils.ajvErrorsToErrorType(validate?.errors), 'Response PDU is invalid', commandName, - JSON.stringify(validate.errors, undefined, 2) + JSON.stringify(validate?.errors, undefined, 2) ) } protected emptyResponseHandler = Constants.EMPTY_FUNCTION - private getJsonRequestValidateFunction( - commandName: RequestCommand, - schema: JSONSchemaType - ): ValidateFunction { - if (!this.jsonRequestValidateFunctions.has(commandName)) { - this.jsonRequestValidateFunctions.set(commandName, this.ajv.compile(schema).bind(this)) - } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return this.jsonRequestValidateFunctions.get(commandName)! - } - public abstract responseHandler( chargingStation: ChargingStation, commandName: RequestCommand, -- 2.34.1