From 028878919a937f9307634c0316e27c677a87d305 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sun, 8 Jan 2023 16:04:42 +0100 Subject: [PATCH] Add incoming request response sent payloads validatio MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../ocpp/1.6/OCPP16ResponseService.ts | 186 +++++++++++++++++- .../ocpp/2.0/OCPP20ResponseService.ts | 20 +- .../ocpp/OCPPRequestService.ts | 15 +- src/charging-station/ocpp/OCPPServiceUtils.ts | 10 +- 4 files changed, 217 insertions(+), 14 deletions(-) diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index 061466e8..e29957be 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -22,11 +22,20 @@ import { type OCPP16StatusNotificationRequest, } from '../../../types/ocpp/1.6/Requests'; import type { + ChangeAvailabilityResponse, + ChangeConfigurationResponse, + ClearChargingProfileResponse, DiagnosticsStatusNotificationResponse, + GetConfigurationResponse, + GetDiagnosticsResponse, OCPP16BootNotificationResponse, OCPP16DataTransferResponse, OCPP16HeartbeatResponse, OCPP16StatusNotificationResponse, + OCPP16TriggerMessageResponse, + OCPP16UpdateFirmwareResponse, + SetChargingProfileResponse, + UnlockConnectorResponse, } from '../../../types/ocpp/1.6/Responses'; import { OCPP16AuthorizationStatus, @@ -39,7 +48,11 @@ import { } from '../../../types/ocpp/1.6/Transaction'; import { ErrorType } from '../../../types/ocpp/ErrorType'; import { OCPPVersion } from '../../../types/ocpp/OCPPVersion'; -import { RegistrationStatusEnumType, type ResponseHandler } from '../../../types/ocpp/Responses'; +import { + type DefaultResponse, + RegistrationStatusEnumType, + type ResponseHandler, +} from '../../../types/ocpp/Responses'; import Constants from '../../../utils/Constants'; import logger from '../../../utils/Logger'; import Utils from '../../../utils/Utils'; @@ -185,7 +198,176 @@ export default class OCPP16ResponseService extends OCPPResponseService { ) as JSONSchemaType, ], ]); - this.jsonIncomingRequestResponseSchemas = new Map(); + this.jsonIncomingRequestResponseSchemas = new Map([ + [ + OCPP16IncomingRequestCommand.RESET, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/ResetResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.CLEAR_CACHE, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/ClearCacheResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.GET_CONFIGURATION, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.DATA_TRANSFER, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + [ + OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + ]); this.validatePayload.bind(this); } diff --git a/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts b/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts index acef3c97..ef83ad43 100644 --- a/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts @@ -12,7 +12,10 @@ import { OCPP20IncomingRequestCommand, OCPP20RequestCommand, } from '../../../types/ocpp/2.0/Requests'; -import type { OCPP20BootNotificationResponse } from '../../../types/ocpp/2.0/Responses'; +import type { + OCPP20BootNotificationResponse, + OCPP20ClearCacheResponse, +} from '../../../types/ocpp/2.0/Responses'; import { ErrorType } from '../../../types/ocpp/ErrorType'; import { OCPPVersion } from '../../../types/ocpp/OCPPVersion'; import { RegistrationStatusEnumType, ResponseHandler } from '../../../types/ocpp/Responses'; @@ -54,7 +57,20 @@ export default class OCPP20ResponseService extends OCPPResponseService { ) as JSONSchemaType, ], ]); - this.jsonIncomingRequestResponseSchemas = new Map(); + this.jsonIncomingRequestResponseSchemas = new Map([ + [ + OCPP20IncomingRequestCommand.CLEAR_CACHE, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/2.0/ClearCacheResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], + ]); this.validatePayload.bind(this); } diff --git a/src/charging-station/ocpp/OCPPRequestService.ts b/src/charging-station/ocpp/OCPPRequestService.ts index 6c0e7b0d..d14fb570 100644 --- a/src/charging-station/ocpp/OCPPRequestService.ts +++ b/src/charging-station/ocpp/OCPPRequestService.ts @@ -50,6 +50,7 @@ export default abstract class OCPPRequestService { this.internalSendMessage.bind(this); this.buildMessageToSend.bind(this); this.validateRequestPayload.bind(this); + this.validateIncomingRequestResponsePayload.bind(this); } public static getInstance( @@ -128,7 +129,7 @@ export default abstract class OCPPRequestService { } } - protected validateRequestPayload( + private validateRequestPayload( chargingStation: ChargingStation, commandName: RequestCommand | IncomingRequestCommand, payload: T @@ -161,7 +162,7 @@ export default abstract class OCPPRequestService { ); } - protected validateResponsePayload( + private validateIncomingRequestResponsePayload( chargingStation: ChargingStation, commandName: RequestCommand | IncomingRequestCommand, payload: T @@ -175,7 +176,7 @@ export default abstract class OCPPRequestService { ) === false ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validateResponsePayload: No JSON schema found for command '${commandName}' PDU validation` + `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: No JSON schema found for command '${commandName}' PDU validation` ); return true; } @@ -190,7 +191,7 @@ export default abstract class OCPPRequestService { return true; } logger.error( - `${chargingStation.logPrefix()} ${moduleName}.validateResponsePayload: Command '${commandName}' reponse PDU is invalid: %j`, + `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: Command '${commandName}' reponse PDU is invalid: %j`, validate.errors ); // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). @@ -389,7 +390,11 @@ export default abstract class OCPPRequestService { // Response case MessageType.CALL_RESULT_MESSAGE: // Build response - this.validateResponsePayload(chargingStation, commandName, messagePayload as JsonObject); + this.validateIncomingRequestResponsePayload( + chargingStation, + commandName, + messagePayload as JsonObject + ); messageToSend = JSON.stringify([messageType, messageId, messagePayload] as Response); break; // Error Message diff --git a/src/charging-station/ocpp/OCPPServiceUtils.ts b/src/charging-station/ocpp/OCPPServiceUtils.ts index 574476c6..3c415251 100644 --- a/src/charging-station/ocpp/OCPPServiceUtils.ts +++ b/src/charging-station/ocpp/OCPPServiceUtils.ts @@ -106,11 +106,11 @@ export class OCPPServiceUtils { } public static convertDateToISOString(obj: T): void { - for (const k in obj) { - if (obj[k] instanceof Date) { - (obj as JsonObject)[k] = (obj[k] as Date).toISOString(); - } else if (obj[k] !== null && typeof obj[k] === 'object') { - this.convertDateToISOString(obj[k] as T); + for (const key in obj) { + if (obj[key] instanceof Date) { + (obj as JsonObject)[key] = (obj[key] as Date).toISOString(); + } else if (obj[key] !== null && typeof obj[key] === 'object') { + this.convertDateToISOString(obj[key] as T); } } } -- 2.34.1