Migrate all JSON schemas to draft-06 or higher
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / OCPPIncomingRequestService.ts
1 import { AsyncResource } from 'async_hooks';
2
3 import Ajv, { type JSONSchemaType } from 'ajv';
4 import ajvFormats from 'ajv-formats';
5
6 import OCPPError from '../../exception/OCPPError';
7 import type { HandleErrorParams } from '../../types/Error';
8 import type { JsonType } from '../../types/JsonType';
9 import type { OCPPVersion } from '../../types/ocpp/OCPPVersion';
10 import type { IncomingRequestCommand } from '../../types/ocpp/Requests';
11 import logger from '../../utils/Logger';
12 import type ChargingStation from '../ChargingStation';
13 import { OCPPServiceUtils } from './OCPPServiceUtils';
14
15 const moduleName = 'OCPPIncomingRequestService';
16
17 export default abstract class OCPPIncomingRequestService {
18 private static instance: OCPPIncomingRequestService | null = null;
19 protected asyncResource: AsyncResource;
20 private readonly version: OCPPVersion;
21 private readonly ajv: Ajv;
22
23 protected constructor(version: OCPPVersion) {
24 this.version = version;
25 this.ajv = new Ajv();
26 ajvFormats(this.ajv);
27 this.asyncResource = new AsyncResource(moduleName);
28 this.incomingRequestHandler.bind(this);
29 this.validateIncomingRequestPayload.bind(this);
30 }
31
32 public static getInstance<T extends OCPPIncomingRequestService>(this: new () => T): T {
33 if (OCPPIncomingRequestService.instance === null) {
34 OCPPIncomingRequestService.instance = new this();
35 }
36 return OCPPIncomingRequestService.instance as T;
37 }
38
39 protected handleIncomingRequestError<T>(
40 chargingStation: ChargingStation,
41 commandName: IncomingRequestCommand,
42 error: Error,
43 params: HandleErrorParams<T> = { throwError: true }
44 ): T | undefined {
45 logger.error(
46 `${chargingStation.logPrefix()} ${moduleName}.handleIncomingRequestError: Incoming request command '${commandName}' error:`,
47 error
48 );
49 if (!params?.throwError && params?.errorResponse) {
50 return params?.errorResponse;
51 }
52 if (params?.throwError && !params?.errorResponse) {
53 throw error;
54 }
55 if (params?.throwError && params?.errorResponse) {
56 return params?.errorResponse;
57 }
58 }
59
60 protected validateIncomingRequestPayload<T extends JsonType>(
61 chargingStation: ChargingStation,
62 commandName: IncomingRequestCommand,
63 schema: JSONSchemaType<T>,
64 payload: T
65 ): boolean {
66 if (chargingStation.getPayloadSchemaValidation() === false) {
67 return true;
68 }
69 const validate = this.ajv.compile(schema);
70 if (validate(payload)) {
71 return true;
72 }
73 logger.error(
74 `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestPayload: Incoming request PDU is invalid: %j`,
75 validate.errors
76 );
77 throw new OCPPError(
78 OCPPServiceUtils.ajvErrorsToErrorType(validate.errors),
79 'Incoming request PDU is invalid',
80 commandName,
81 JSON.stringify(validate.errors, null, 2)
82 );
83 }
84
85 public abstract incomingRequestHandler(
86 chargingStation: ChargingStation,
87 messageId: string,
88 commandName: IncomingRequestCommand,
89 commandPayload: JsonType
90 ): Promise<void>;
91 }