build: switch to NodeNext module resolution
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / OCPPResponseService.ts
1 import Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv';
2 import ajvFormats from 'ajv-formats';
3
4 import { OCPPServiceUtils } from './OCPPServiceUtils.js';
5 import type { ChargingStation } from '../../charging-station/index.js';
6 import { OCPPError } from '../../exception/index.js';
7 import type {
8 IncomingRequestCommand,
9 JsonType,
10 OCPPVersion,
11 RequestCommand,
12 } from '../../types/index.js';
13 import { logger } from '../../utils/index.js';
14
15 const moduleName = 'OCPPResponseService';
16
17 export abstract class OCPPResponseService {
18 private static instance: OCPPResponseService | null = null;
19
20 public jsonIncomingRequestResponseValidateFunctions: Map<
21 IncomingRequestCommand,
22 ValidateFunction<JsonType>
23 >;
24
25 private readonly version: OCPPVersion;
26 private readonly ajv: Ajv;
27 private jsonRequestValidateFunctions: Map<RequestCommand, ValidateFunction<JsonType>>;
28
29 public abstract jsonIncomingRequestResponseSchemas: Map<
30 IncomingRequestCommand,
31 JSONSchemaType<JsonType>
32 >;
33
34 protected constructor(version: OCPPVersion) {
35 this.version = version;
36 this.ajv = new Ajv({
37 keywords: ['javaType'],
38 multipleOfPrecision: 2,
39 });
40 ajvFormats(this.ajv);
41 this.jsonRequestValidateFunctions = new Map<RequestCommand, ValidateFunction<JsonType>>();
42 this.jsonIncomingRequestResponseValidateFunctions = new Map<
43 IncomingRequestCommand,
44 ValidateFunction<JsonType>
45 >();
46 this.responseHandler = this.responseHandler.bind(this) as <
47 ReqType extends JsonType,
48 ResType extends JsonType,
49 >(
50 chargingStation: ChargingStation,
51 commandName: RequestCommand,
52 payload: ResType,
53 requestPayload: ReqType,
54 ) => Promise<void>;
55 this.validateResponsePayload = this.validateResponsePayload.bind(this) as <T extends JsonType>(
56 chargingStation: ChargingStation,
57 commandName: RequestCommand,
58 schema: JSONSchemaType<T>,
59 payload: T,
60 ) => boolean;
61 }
62
63 public static getInstance<T extends OCPPResponseService>(this: new () => T): T {
64 if (OCPPResponseService.instance === null) {
65 OCPPResponseService.instance = new this();
66 }
67 return OCPPResponseService.instance as T;
68 }
69
70 protected validateResponsePayload<T extends JsonType>(
71 chargingStation: ChargingStation,
72 commandName: RequestCommand,
73 schema: JSONSchemaType<T>,
74 payload: T,
75 ): boolean {
76 if (chargingStation.stationInfo?.ocppStrictCompliance === false) {
77 return true;
78 }
79 const validate = this.getJsonRequestValidateFunction<T>(commandName, schema);
80 if (validate(payload)) {
81 return true;
82 }
83 logger.error(
84 `${chargingStation.logPrefix()} ${moduleName}.validateResponsePayload: Command '${commandName}' response PDU is invalid: %j`,
85 validate.errors,
86 );
87 throw new OCPPError(
88 OCPPServiceUtils.ajvErrorsToErrorType(validate.errors),
89 'Response PDU is invalid',
90 commandName,
91 JSON.stringify(validate.errors, undefined, 2),
92 );
93 }
94
95 protected emptyResponseHandler() {
96 /* This is intentional */
97 }
98
99 private getJsonRequestValidateFunction<T extends JsonType>(
100 commandName: RequestCommand,
101 schema: JSONSchemaType<T>,
102 ) {
103 if (this.jsonRequestValidateFunctions.has(commandName) === false) {
104 this.jsonRequestValidateFunctions.set(commandName, this.ajv.compile<T>(schema).bind(this));
105 }
106 return this.jsonRequestValidateFunctions.get(commandName)!;
107 }
108
109 public abstract responseHandler<ReqType extends JsonType, ResType extends JsonType>(
110 chargingStation: ChargingStation,
111 commandName: RequestCommand,
112 payload: ResType,
113 requestPayload: ReqType,
114 ): Promise<void>;
115 }