Align more request and reponse handlers name
[e-mobility-charging-stations-simulator.git] / src / charging-station / ui-server / ui-services / AbstractUIService.ts
1 import { RawData } from 'ws';
2
3 import BaseError from '../../../exception/BaseError';
4 import { JsonType } from '../../../types/JsonType';
5 import {
6 ProcedureName,
7 ProtocolRequest,
8 ProtocolRequestHandler,
9 ProtocolResponse,
10 ProtocolVersion,
11 RequestPayload,
12 ResponsePayload,
13 ResponseStatus,
14 } from '../../../types/UIProtocol';
15 import logger from '../../../utils/Logger';
16 import Utils from '../../../utils/Utils';
17 import Bootstrap from '../../Bootstrap';
18 import WorkerBroadcastChannel from '../../WorkerBroadcastChannel';
19 import { AbstractUIServer } from '../AbstractUIServer';
20
21 const moduleName = 'AbstractUIService';
22
23 export default abstract class AbstractUIService {
24 protected readonly version: ProtocolVersion;
25 protected readonly uiServer: AbstractUIServer;
26 protected readonly requestHandlers: Map<ProcedureName, ProtocolRequestHandler>;
27 protected workerBroadcastChannel: WorkerBroadcastChannel;
28
29 constructor(uiServer: AbstractUIServer, version: ProtocolVersion) {
30 this.version = version;
31 this.uiServer = uiServer;
32 this.requestHandlers = new Map<ProcedureName, ProtocolRequestHandler>([
33 [ProcedureName.LIST_CHARGING_STATIONS, this.handleListChargingStations.bind(this)],
34 [ProcedureName.START_SIMULATOR, this.handleStartSimulator.bind(this)],
35 [ProcedureName.STOP_SIMULATOR, this.handleStopSimulator.bind(this)],
36 ]);
37 this.workerBroadcastChannel = new WorkerBroadcastChannel();
38 }
39
40 public async requestHandler(request: RawData): Promise<void> {
41 let messageId: string;
42 let command: ProcedureName;
43 let requestPayload: RequestPayload;
44 let responsePayload: ResponsePayload;
45 try {
46 [messageId, command, requestPayload] = this.requestValidation(request);
47
48 if (this.requestHandlers.has(command) === false) {
49 throw new BaseError(
50 `${command} is not implemented to handle message payload ${JSON.stringify(
51 requestPayload,
52 null,
53 2
54 )}`
55 );
56 }
57
58 // Call the message handler to build the response payload
59 responsePayload = await this.requestHandlers.get(command)(messageId, requestPayload);
60 } catch (error) {
61 // Log
62 logger.error(
63 `${this.uiServer.logPrefix(moduleName, 'messageHandler')} Handle message error:`,
64 error
65 );
66 // Send the message response failure
67 this.uiServer.sendResponse(
68 this.buildProtocolResponse(messageId ?? 'error', {
69 status: ResponseStatus.FAILURE,
70 command,
71 requestPayload,
72 errorMessage: (error as Error).message,
73 errorStack: (error as Error).stack,
74 })
75 );
76 throw error;
77 }
78
79 // Send the message response success
80 this.uiServer.sendResponse(this.buildProtocolResponse(messageId, responsePayload));
81 }
82
83 protected buildProtocolRequest(
84 messageId: string,
85 procedureName: ProcedureName,
86 payload: RequestPayload
87 ): string {
88 return JSON.stringify([messageId, procedureName, payload] as ProtocolRequest);
89 }
90
91 protected buildProtocolResponse(messageId: string, payload: ResponsePayload): string {
92 return JSON.stringify([messageId, payload] as ProtocolResponse);
93 }
94
95 // Validate the raw data received from the WebSocket
96 // TODO: should probably be moved to the ws verify clients callback
97 private requestValidation(rawData: RawData): ProtocolRequest {
98 // logger.debug(
99 // `${this.uiServer.logPrefix(
100 // moduleName,
101 // 'dataValidation'
102 // )} Raw data received: ${rawData.toString()}`
103 // );
104
105 const data = JSON.parse(rawData.toString()) as JsonType[];
106
107 if (Utils.isIterable(data) === false) {
108 throw new BaseError('UI protocol request is not iterable');
109 }
110
111 if (data.length !== 3) {
112 throw new BaseError('UI protocol request is malformed');
113 }
114
115 return data as ProtocolRequest;
116 }
117
118 private handleListChargingStations(): ResponsePayload {
119 // TODO: remove cast to unknown
120 return {
121 status: ResponseStatus.SUCCESS,
122 ...Array.from(this.uiServer.chargingStations.values()),
123 } as unknown as ResponsePayload;
124 }
125
126 private async handleStartSimulator(): Promise<ResponsePayload> {
127 await Bootstrap.getInstance().start();
128 return { status: ResponseStatus.SUCCESS };
129 }
130
131 private async handleStopSimulator(): Promise<ResponsePayload> {
132 await Bootstrap.getInstance().stop();
133 return { status: ResponseStatus.SUCCESS };
134 }
135 }