perf: cache only JSON payload validation functions
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 2.0 / OCPP20IncomingRequestService.ts
CommitLineData
a19b897d 1// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
953d6b02 2
24d15716 3import type { ValidateFunction } from 'ajv'
953d6b02 4
66a7748d
JB
5import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
6import type { ChargingStation } from '../../../charging-station/index.js'
7import { OCPPError } from '../../../exception/index.js'
d270cc87 8import {
268a74bb
JB
9 ErrorType,
10 type IncomingRequestHandler,
268a74bb 11 type JsonType,
81533a20 12 type OCPP20ClearCacheRequest,
d270cc87 13 OCPP20IncomingRequestCommand,
66a7748d
JB
14 OCPPVersion
15} from '../../../types/index.js'
16import { logger } from '../../../utils/index.js'
17import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'
953d6b02 18
66a7748d 19const moduleName = 'OCPP20IncomingRequestService'
953d6b02 20
268a74bb 21export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
24d15716
JB
22 protected jsonSchemasValidateFunction: Map<
23 OCPP20IncomingRequestCommand,
24 ValidateFunction<JsonType>
25 >
26
66a7748d
JB
27 private readonly incomingRequestHandlers: Map<
28 OCPP20IncomingRequestCommand,
29 IncomingRequestHandler
30 >
953d6b02 31
66a7748d 32 public constructor () {
5199f9fd
JB
33 // if (new.target.name === moduleName) {
34 // throw new TypeError(`Cannot construct ${new.target.name} instances directly`)
b768993d 35 // }
66a7748d 36 super(OCPPVersion.VERSION_20)
d270cc87 37 this.incomingRequestHandlers = new Map<OCPP20IncomingRequestCommand, IncomingRequestHandler>([
66a7748d
JB
38 [OCPP20IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)]
39 ])
24d15716
JB
40 this.jsonSchemasValidateFunction = new Map<
41 OCPP20IncomingRequestCommand,
42 ValidateFunction<JsonType>
43 >([
d270cc87
JB
44 [
45 OCPP20IncomingRequestCommand.CLEAR_CACHE,
24d15716
JB
46 this.ajv
47 .compile(
48 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20ClearCacheRequest>(
49 'assets/json-schemas/ocpp/2.0/ClearCacheRequest.json',
50 moduleName,
51 'constructor'
52 )
53 )
54 .bind(this)
66a7748d
JB
55 ]
56 ])
ba9a56a6 57 this.validatePayload = this.validatePayload.bind(this)
953d6b02
JB
58 }
59
9429aa42 60 public async incomingRequestHandler<ReqType extends JsonType, ResType extends JsonType>(
953d6b02
JB
61 chargingStation: ChargingStation,
62 messageId: string,
63 commandName: OCPP20IncomingRequestCommand,
66a7748d 64 commandPayload: ReqType
953d6b02 65 ): Promise<void> {
66a7748d 66 let response: ResType
953d6b02 67 if (
5398cecf 68 chargingStation.stationInfo?.ocppStrictCompliance === true &&
66a7748d 69 chargingStation.inPendingState() &&
81533a20
JB
70 (commandName === OCPP20IncomingRequestCommand.REQUEST_START_TRANSACTION ||
71 commandName === OCPP20IncomingRequestCommand.REQUEST_STOP_TRANSACTION)
953d6b02
JB
72 ) {
73 throw new OCPPError(
74 ErrorType.SECURITY_ERROR,
75 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
76 commandPayload,
4ed03b6e 77 undefined,
66a7748d 78 2
953d6b02
JB
79 )} while the charging station is in pending state on the central server`,
80 commandName,
66a7748d
JB
81 commandPayload
82 )
953d6b02
JB
83 }
84 if (
66a7748d 85 chargingStation.isRegistered() ||
5398cecf 86 (chargingStation.stationInfo?.ocppStrictCompliance === false &&
66a7748d 87 chargingStation.inUnknownState())
953d6b02
JB
88 ) {
89 if (
66a7748d
JB
90 this.incomingRequestHandlers.has(commandName) &&
91 OCPP20ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName)
953d6b02
JB
92 ) {
93 try {
66a7748d 94 this.validatePayload(chargingStation, commandName, commandPayload)
953d6b02 95 // Call the method to build the response
66a7748d 96 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
9429aa42 97 response = (await this.incomingRequestHandlers.get(commandName)!(
953d6b02 98 chargingStation,
66a7748d
JB
99 commandPayload
100 )) as ResType
953d6b02
JB
101 } catch (error) {
102 // Log
103 logger.error(
104 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
66a7748d
JB
105 error
106 )
107 throw error
953d6b02
JB
108 }
109 } else {
110 // Throw exception
111 throw new OCPPError(
112 ErrorType.NOT_IMPLEMENTED,
113 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
114 commandPayload,
4ed03b6e 115 undefined,
66a7748d 116 2
953d6b02
JB
117 )}`,
118 commandName,
66a7748d
JB
119 commandPayload
120 )
953d6b02
JB
121 }
122 } else {
123 throw new OCPPError(
124 ErrorType.SECURITY_ERROR,
125 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
126 commandPayload,
4ed03b6e 127 undefined,
66a7748d 128 2
953d6b02
JB
129 )} while the charging station is not registered on the central server.`,
130 commandName,
66a7748d
JB
131 commandPayload
132 )
953d6b02
JB
133 }
134 // Send the built response
135 await chargingStation.ocppRequestService.sendResponse(
136 chargingStation,
137 messageId,
138 response,
66a7748d
JB
139 commandName
140 )
953d6b02
JB
141 }
142
66a7748d 143 private validatePayload (
953d6b02
JB
144 chargingStation: ChargingStation,
145 commandName: OCPP20IncomingRequestCommand,
66a7748d 146 commandPayload: JsonType
953d6b02 147 ): boolean {
24d15716
JB
148 if (this.jsonSchemasValidateFunction.has(commandName)) {
149 return this.validateIncomingRequestPayload(chargingStation, commandName, commandPayload)
953d6b02
JB
150 }
151 logger.warn(
24d15716 152 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation`
66a7748d
JB
153 )
154 return false
953d6b02
JB
155 }
156}