fix: ensure error at WS message sending is handled
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / OCPPRequestService.ts
CommitLineData
ec0eebcc 1import Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv';
b52c969d
JB
2import ajvFormats from 'ajv-formats';
3
4c3c0d59
JB
4import { OCPPConstants } from './OCPPConstants';
5import type { OCPPResponseService } from './OCPPResponseService';
6import { OCPPServiceUtils } from './OCPPServiceUtils';
2896e06d 7import type { ChargingStation } from '../../charging-station';
268a74bb 8import { OCPPError } from '../../exception';
b84bca85 9import { PerformanceStatistics } from '../../performance';
e7aeea18 10import {
27782dbc 11 type ErrorCallback,
268a74bb
JB
12 type ErrorResponse,
13 ErrorType,
27782dbc 14 type IncomingRequestCommand,
268a74bb
JB
15 type JsonObject,
16 type JsonType,
17 MessageType,
18 type OCPPVersion,
27782dbc 19 type OutgoingRequest,
e7aeea18 20 RequestCommand,
27782dbc 21 type RequestParams,
268a74bb 22 type Response,
27782dbc 23 type ResponseCallback,
e0b0ee21 24 type ResponseType,
268a74bb 25} from '../../types';
d42379d8
JB
26import {
27 Constants,
28 cloneObject,
29 handleSendMessageError,
30 isNullOrUndefined,
31 logger,
32} from '../../utils';
c0560973 33
e3018bc4
JB
34const moduleName = 'OCPPRequestService';
35
b9da1bc2
JB
36const defaultRequestParams: RequestParams = {
37 skipBufferingOnError: false,
38 triggerMessage: false,
39 throwError: false,
40};
41
268a74bb 42export abstract class OCPPRequestService {
08f130a0 43 private static instance: OCPPRequestService | null = null;
d270cc87 44 private readonly version: OCPPVersion;
012ae1a9 45 private readonly ajv: Ajv;
9f2e3130 46 private readonly ocppResponseService: OCPPResponseService;
291b5ec8
JB
47 private readonly jsonValidateFunctions: Map<RequestCommand, ValidateFunction<JsonType>>;
48 protected abstract jsonSchemas: Map<RequestCommand, JSONSchemaType<JsonType>>;
c0560973 49
d270cc87
JB
50 protected constructor(version: OCPPVersion, ocppResponseService: OCPPResponseService) {
51 this.version = version;
45988780 52 this.ajv = new Ajv({
98fc1389 53 keywords: ['javaType'],
45988780
JB
54 multipleOfPrecision: 2,
55 });
9952c548 56 ajvFormats(this.ajv);
291b5ec8 57 this.jsonValidateFunctions = new Map<RequestCommand, ValidateFunction<JsonType>>();
d270cc87 58 this.ocppResponseService = ocppResponseService;
31f59c6d 59 this.requestHandler = this.requestHandler.bind(this) as <
e1d9a0f4 60 // eslint-disable-next-line @typescript-eslint/no-unused-vars
31f59c6d 61 ReqType extends JsonType,
5edd8ba0 62 ResType extends JsonType,
31f59c6d
JB
63 >(
64 chargingStation: ChargingStation,
65 commandName: RequestCommand,
66 commandParams?: JsonType,
5edd8ba0 67 params?: RequestParams,
31f59c6d
JB
68 ) => Promise<ResType>;
69 this.sendMessage = this.sendMessage.bind(this) as (
70 chargingStation: ChargingStation,
71 messageId: string,
72 messagePayload: JsonType,
73 commandName: RequestCommand,
5edd8ba0 74 params?: RequestParams,
31f59c6d
JB
75 ) => Promise<ResponseType>;
76 this.sendResponse = this.sendResponse.bind(this) as (
77 chargingStation: ChargingStation,
78 messageId: string,
79 messagePayload: JsonType,
5edd8ba0 80 commandName: IncomingRequestCommand,
31f59c6d
JB
81 ) => Promise<ResponseType>;
82 this.sendError = this.sendError.bind(this) as (
83 chargingStation: ChargingStation,
84 messageId: string,
85 ocppError: OCPPError,
5edd8ba0 86 commandName: RequestCommand | IncomingRequestCommand,
31f59c6d
JB
87 ) => Promise<ResponseType>;
88 this.internalSendMessage = this.internalSendMessage.bind(this) as (
89 chargingStation: ChargingStation,
90 messageId: string,
91 messagePayload: JsonType | OCPPError,
92 messageType: MessageType,
93 commandName: RequestCommand | IncomingRequestCommand,
5edd8ba0 94 params?: RequestParams,
31f59c6d
JB
95 ) => Promise<ResponseType>;
96 this.buildMessageToSend = this.buildMessageToSend.bind(this) as (
97 chargingStation: ChargingStation,
98 messageId: string,
99 messagePayload: JsonType | OCPPError,
100 messageType: MessageType,
101 commandName: RequestCommand | IncomingRequestCommand,
31f59c6d 102 ) => string;
291b5ec8 103 this.validateRequestPayload = this.validateRequestPayload.bind(this) as <T extends JsonType>(
31f59c6d
JB
104 chargingStation: ChargingStation,
105 commandName: RequestCommand | IncomingRequestCommand,
5edd8ba0 106 payload: T,
31f59c6d
JB
107 ) => boolean;
108 this.validateIncomingRequestResponsePayload = this.validateIncomingRequestResponsePayload.bind(
5edd8ba0 109 this,
291b5ec8 110 ) as <T extends JsonType>(
31f59c6d
JB
111 chargingStation: ChargingStation,
112 commandName: RequestCommand | IncomingRequestCommand,
5edd8ba0 113 payload: T,
31f59c6d 114 ) => boolean;
c0560973
JB
115 }
116
e7aeea18 117 public static getInstance<T extends OCPPRequestService>(
08f130a0 118 this: new (ocppResponseService: OCPPResponseService) => T,
5edd8ba0 119 ocppResponseService: OCPPResponseService,
e7aeea18 120 ): T {
1ca780f9 121 if (OCPPRequestService.instance === null) {
08f130a0 122 OCPPRequestService.instance = new this(ocppResponseService);
9f2e3130 123 }
08f130a0 124 return OCPPRequestService.instance as T;
9f2e3130
JB
125 }
126
c75a6675 127 public async sendResponse(
08f130a0 128 chargingStation: ChargingStation,
e7aeea18 129 messageId: string,
5cc4b63b 130 messagePayload: JsonType,
5edd8ba0 131 commandName: IncomingRequestCommand,
5eaabe90 132 ): Promise<ResponseType> {
5e0c67e8 133 try {
c75a6675 134 // Send response message
e7aeea18 135 return await this.internalSendMessage(
08f130a0 136 chargingStation,
e7aeea18
JB
137 messageId,
138 messagePayload,
139 MessageType.CALL_RESULT_MESSAGE,
5edd8ba0 140 commandName,
e7aeea18 141 );
5e0c67e8 142 } catch (error) {
fa5995d6 143 handleSendMessageError(chargingStation, commandName, error as Error, {
07561812 144 throwError: true,
dc922667 145 });
e1d9a0f4 146 return null;
5e0c67e8
JB
147 }
148 }
149
e7aeea18 150 public async sendError(
08f130a0 151 chargingStation: ChargingStation,
e7aeea18
JB
152 messageId: string,
153 ocppError: OCPPError,
5edd8ba0 154 commandName: RequestCommand | IncomingRequestCommand,
e7aeea18 155 ): Promise<ResponseType> {
5e0c67e8
JB
156 try {
157 // Send error message
e7aeea18 158 return await this.internalSendMessage(
08f130a0 159 chargingStation,
e7aeea18
JB
160 messageId,
161 ocppError,
162 MessageType.CALL_ERROR_MESSAGE,
5edd8ba0 163 commandName,
e7aeea18 164 );
5e0c67e8 165 } catch (error) {
fa5995d6 166 handleSendMessageError(chargingStation, commandName, error as Error);
e1d9a0f4 167 return null;
5e0c67e8
JB
168 }
169 }
170
e7aeea18 171 protected async sendMessage(
08f130a0 172 chargingStation: ChargingStation,
e7aeea18 173 messageId: string,
5cc4b63b 174 messagePayload: JsonType,
e7aeea18 175 commandName: RequestCommand,
7f3decca 176 params?: RequestParams,
e7aeea18 177 ): Promise<ResponseType> {
7b5dbe91 178 params = {
b9da1bc2 179 ...defaultRequestParams,
7b5dbe91
JB
180 ...params,
181 };
5e0c67e8 182 try {
e7aeea18 183 return await this.internalSendMessage(
08f130a0 184 chargingStation,
e7aeea18
JB
185 messageId,
186 messagePayload,
187 MessageType.CALL_MESSAGE,
188 commandName,
5edd8ba0 189 params,
e7aeea18 190 );
5e0c67e8 191 } catch (error) {
fa5995d6 192 handleSendMessageError(chargingStation, commandName, error as Error, {
8ec8e3d0
JB
193 throwError: params.throwError,
194 });
e1d9a0f4 195 return null;
5e0c67e8
JB
196 }
197 }
198
291b5ec8 199 private validateRequestPayload<T extends JsonType>(
b52c969d 200 chargingStation: ChargingStation,
45988780 201 commandName: RequestCommand | IncomingRequestCommand,
5edd8ba0 202 payload: T,
b52c969d 203 ): boolean {
5398cecf 204 if (chargingStation.stationInfo?.ocppStrictCompliance === false) {
b52c969d
JB
205 return true;
206 }
b3fc3ff5
JB
207 if (this.jsonSchemas.has(commandName as RequestCommand) === false) {
208 logger.warn(
5edd8ba0 209 `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: No JSON schema found for command '${commandName}' PDU validation`,
b3fc3ff5 210 );
45988780
JB
211 return true;
212 }
0b0ca54f 213 const validate = this.getJsonRequestValidateFunction<T>(commandName as RequestCommand);
9bf0ef23 214 payload = cloneObject<T>(payload);
1799761a 215 OCPPServiceUtils.convertDateToISOString<T>(payload);
b52c969d
JB
216 if (validate(payload)) {
217 return true;
218 }
219 logger.error(
45988780 220 `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: Command '${commandName}' request PDU is invalid: %j`,
5edd8ba0 221 validate.errors,
b52c969d 222 );
e909d2a7 223 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
b52c969d 224 throw new OCPPError(
9ff486f4 225 OCPPServiceUtils.ajvErrorsToErrorType(validate.errors),
b52c969d
JB
226 'Request PDU is invalid',
227 commandName,
4ed03b6e 228 JSON.stringify(validate.errors, undefined, 2),
b52c969d
JB
229 );
230 }
231
0b0ca54f
JB
232 private getJsonRequestValidateFunction<T extends JsonType>(commandName: RequestCommand) {
233 if (this.jsonValidateFunctions.has(commandName) === false) {
234 this.jsonValidateFunctions.set(
235 commandName,
236 this.ajv.compile<T>(this.jsonSchemas.get(commandName)!).bind(this),
237 );
238 }
239 return this.jsonValidateFunctions.get(commandName)!;
240 }
241
291b5ec8 242 private validateIncomingRequestResponsePayload<T extends JsonType>(
b3fc3ff5
JB
243 chargingStation: ChargingStation,
244 commandName: RequestCommand | IncomingRequestCommand,
5edd8ba0 245 payload: T,
b3fc3ff5 246 ): boolean {
5398cecf 247 if (chargingStation.stationInfo?.ocppStrictCompliance === false) {
b3fc3ff5
JB
248 return true;
249 }
250 if (
251 this.ocppResponseService.jsonIncomingRequestResponseSchemas.has(
5edd8ba0 252 commandName as IncomingRequestCommand,
b3fc3ff5
JB
253 ) === false
254 ) {
255 logger.warn(
5edd8ba0 256 `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: No JSON schema found for command '${commandName}' PDU validation`,
b3fc3ff5
JB
257 );
258 return true;
259 }
0b0ca54f 260 const validate = this.getJsonRequestResponseValidateFunction<T>(
ec0eebcc 261 commandName as IncomingRequestCommand,
0b0ca54f 262 );
9bf0ef23 263 payload = cloneObject<T>(payload);
b3fc3ff5
JB
264 OCPPServiceUtils.convertDateToISOString<T>(payload);
265 if (validate(payload)) {
266 return true;
267 }
268 logger.error(
02887891 269 `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: Command '${commandName}' reponse PDU is invalid: %j`,
5edd8ba0 270 validate.errors,
b3fc3ff5
JB
271 );
272 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
273 throw new OCPPError(
9ff486f4 274 OCPPServiceUtils.ajvErrorsToErrorType(validate.errors),
b3fc3ff5
JB
275 'Response PDU is invalid',
276 commandName,
4ed03b6e 277 JSON.stringify(validate.errors, undefined, 2),
b3fc3ff5
JB
278 );
279 }
280
0b0ca54f
JB
281 private getJsonRequestResponseValidateFunction<T extends JsonType>(
282 commandName: IncomingRequestCommand,
283 ) {
284 if (
285 this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.has(commandName) ===
286 false
287 ) {
288 this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.set(
289 commandName,
290 this.ajv
291 .compile<T>(this.ocppResponseService.jsonIncomingRequestResponseSchemas.get(commandName)!)
292 .bind(this),
293 );
294 }
295 return this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.get(commandName)!;
296 }
297
e7aeea18 298 private async internalSendMessage(
08f130a0 299 chargingStation: ChargingStation,
e7aeea18 300 messageId: string,
5cc4b63b 301 messagePayload: JsonType | OCPPError,
e7aeea18 302 messageType: MessageType,
72092cfc 303 commandName: RequestCommand | IncomingRequestCommand,
7f3decca 304 params?: RequestParams,
e7aeea18 305 ): Promise<ResponseType> {
7b5dbe91 306 params = {
b9da1bc2 307 ...defaultRequestParams,
7b5dbe91
JB
308 ...params,
309 };
e7aeea18 310 if (
f7c2994d 311 (chargingStation.inUnknownState() === true &&
3a13fc92 312 commandName === RequestCommand.BOOT_NOTIFICATION) ||
5398cecf 313 (chargingStation.stationInfo?.ocppStrictCompliance === false &&
f7c2994d
JB
314 chargingStation.inUnknownState() === true) ||
315 chargingStation.inAcceptedState() === true ||
316 (chargingStation.inPendingState() === true &&
3a13fc92 317 (params.triggerMessage === true || messageType === MessageType.CALL_RESULT_MESSAGE))
e7aeea18 318 ) {
caad9d6b
JB
319 // eslint-disable-next-line @typescript-eslint/no-this-alias
320 const self = this;
321 // Send a message through wsConnection
5b2721db 322 return new Promise<ResponseType>((resolve, reject) => {
1b2acf4e
JB
323 /**
324 * Function that will receive the request's response
325 *
326 * @param payload -
327 * @param requestPayload -
328 */
329 const responseCallback = (payload: JsonType, requestPayload: JsonType): void => {
330 if (chargingStation.stationInfo?.enableStatistics === true) {
331 chargingStation.performanceStatistics?.addRequestStatistic(
332 commandName,
333 MessageType.CALL_RESULT_MESSAGE,
e8a92d57 334 );
1b2acf4e
JB
335 }
336 // Handle the request's response
337 self.ocppResponseService
338 .responseHandler(
339 chargingStation,
340 commandName as RequestCommand,
341 payload,
342 requestPayload,
343 )
344 .then(() => {
345 resolve(payload);
346 })
347 .catch((error) => {
348 reject(error);
349 })
350 .finally(() => {
351 chargingStation.requests.delete(messageId);
352 });
353 };
e8a92d57 354
1b2acf4e
JB
355 /**
356 * Function that will receive the request's error response
357 *
9d7b5fa3 358 * @param ocppError -
1b2acf4e
JB
359 * @param requestStatistic -
360 */
9d7b5fa3 361 const errorCallback = (ocppError: OCPPError, requestStatistic = true): void => {
1b2acf4e
JB
362 if (requestStatistic === true && chargingStation.stationInfo?.enableStatistics === true) {
363 chargingStation.performanceStatistics?.addRequestStatistic(
364 commandName,
365 MessageType.CALL_ERROR_MESSAGE,
366 );
764d2c91 367 }
1b2acf4e
JB
368 logger.error(
369 `${chargingStation.logPrefix()} Error occurred at ${OCPPServiceUtils.getMessageTypeString(
370 messageType,
371 )} command ${commandName} with PDU %j:`,
e7aeea18 372 messagePayload,
9d7b5fa3 373 ocppError,
e7aeea18 374 );
1b2acf4e 375 chargingStation.requests.delete(messageId);
9d7b5fa3
JB
376 reject(ocppError);
377 };
378
379 const rejectWithOcppError = (ocppError: OCPPError): void => {
380 // Reject response
381 if (messageType !== MessageType.CALL_MESSAGE) {
382 return reject(ocppError);
383 }
384 // Reject and remove request from the cache
385 return errorCallback(ocppError, false);
1b2acf4e
JB
386 };
387
d42379d8
JB
388 const bufferAndRejectWithOcppError = (ocppError: OCPPError): void => {
389 // Buffer
390 chargingStation.bufferMessage(messageToSend);
391 if (messageType === MessageType.CALL_MESSAGE) {
392 this.cacheRequestPromise(
393 chargingStation,
394 messageId,
395 messagePayload as JsonType,
396 commandName,
397 responseCallback,
398 errorCallback,
399 );
400 }
401 // Reject and keep request in the cache
402 return reject(ocppError);
403 };
404
1b2acf4e
JB
405 if (chargingStation.stationInfo?.enableStatistics === true) {
406 chargingStation.performanceStatistics?.addRequestStatistic(commandName, messageType);
407 }
408 const messageToSend = this.buildMessageToSend(
409 chargingStation,
410 messageId,
411 messagePayload,
412 messageType,
413 commandName,
1b2acf4e 414 );
1b2acf4e 415 // Check if wsConnection opened
1a32c36b 416 if (chargingStation.isWebSocketConnectionOpened() === true) {
1b2acf4e 417 const beginId = PerformanceStatistics.beginMeasure(commandName);
1a32c36b 418 const sendTimeout = setTimeout(() => {
9d7b5fa3 419 return rejectWithOcppError(
1a32c36b
JB
420 new OCPPError(
421 ErrorType.GENERIC_ERROR,
422 `Timeout for message id '${messageId}'`,
423 commandName,
424 (messagePayload as JsonObject)?.details ?? Constants.EMPTY_FROZEN_OBJECT,
425 ),
1a32c36b
JB
426 );
427 }, OCPPConstants.OCPP_WEBSOCKET_TIMEOUT);
428 chargingStation.wsConnection?.send(messageToSend, (error?: Error) => {
d42379d8 429 PerformanceStatistics.endMeasure(commandName, beginId);
82fa1110 430 clearTimeout(sendTimeout);
d42379d8
JB
431 if (isNullOrUndefined(error)) {
432 logger.debug(
433 `${chargingStation.logPrefix()} >> Command '${commandName}' sent ${OCPPServiceUtils.getMessageTypeString(
434 messageType,
435 )} payload: ${messageToSend}`,
436 );
437 if (messageType === MessageType.CALL_MESSAGE) {
438 this.cacheRequestPromise(
439 chargingStation,
440 messageId,
441 messagePayload as JsonType,
442 commandName,
443 responseCallback,
444 errorCallback,
445 );
446 }
447 // Resolve response
448 if (messageType !== MessageType.CALL_MESSAGE) {
449 return resolve(messagePayload);
450 }
451 } else if (error) {
1a32c36b
JB
452 const ocppError = new OCPPError(
453 ErrorType.GENERIC_ERROR,
82fa1110
JB
454 `WebSocket errored for ${
455 params?.skipBufferingOnError === false ? '' : 'non '
456 }buffered message id '${messageId}' with content '${messageToSend}'`,
1a32c36b 457 commandName,
ca483a45 458 { name: error.name, message: error.message, stack: error.stack },
1a32c36b 459 );
82fa1110 460 if (params?.skipBufferingOnError === false) {
d42379d8 461 return bufferAndRejectWithOcppError(ocppError);
82fa1110 462 }
9d7b5fa3 463 return rejectWithOcppError(ocppError);
1a32c36b 464 }
1a32c36b 465 });
82fa1110 466 } else {
9d7b5fa3
JB
467 const ocppError = new OCPPError(
468 ErrorType.GENERIC_ERROR,
469 `WebSocket closed for ${
470 params?.skipBufferingOnError === false ? '' : 'non '
471 }buffered message id '${messageId}' with content '${messageToSend}'`,
472 commandName,
473 (messagePayload as JsonObject)?.details ?? Constants.EMPTY_FROZEN_OBJECT,
474 );
82fa1110 475 if (params?.skipBufferingOnError === false) {
d42379d8 476 return bufferAndRejectWithOcppError(ocppError);
82fa1110 477 }
9d7b5fa3 478 return rejectWithOcppError(ocppError);
1b2acf4e 479 }
1b2acf4e 480 });
caad9d6b 481 }
e7aeea18
JB
482 throw new OCPPError(
483 ErrorType.SECURITY_ERROR,
e3018bc4 484 `Cannot send command ${commandName} PDU when the charging station is in ${chargingStation.getRegistrationStatus()} state on the central server`,
5edd8ba0 485 commandName,
e7aeea18 486 );
c0560973
JB
487 }
488
e7aeea18 489 private buildMessageToSend(
08f130a0 490 chargingStation: ChargingStation,
e7aeea18 491 messageId: string,
5cc4b63b 492 messagePayload: JsonType | OCPPError,
e7aeea18 493 messageType: MessageType,
72092cfc 494 commandName: RequestCommand | IncomingRequestCommand,
e7aeea18 495 ): string {
e7accadb
JB
496 let messageToSend: string;
497 // Type of message
498 switch (messageType) {
499 // Request
500 case MessageType.CALL_MESSAGE:
501 // Build request
291b5ec8 502 this.validateRequestPayload(chargingStation, commandName, messagePayload as JsonType);
b3ec7bc1
JB
503 messageToSend = JSON.stringify([
504 messageType,
505 messageId,
506 commandName,
507 messagePayload,
508 ] as OutgoingRequest);
e7accadb
JB
509 break;
510 // Response
511 case MessageType.CALL_RESULT_MESSAGE:
512 // Build response
02887891
JB
513 this.validateIncomingRequestResponsePayload(
514 chargingStation,
515 commandName,
291b5ec8 516 messagePayload as JsonType,
02887891 517 );
b3ec7bc1 518 messageToSend = JSON.stringify([messageType, messageId, messagePayload] as Response);
e7accadb
JB
519 break;
520 // Error Message
521 case MessageType.CALL_ERROR_MESSAGE:
522 // Build Error Message
e7aeea18
JB
523 messageToSend = JSON.stringify([
524 messageType,
525 messageId,
b3ec7bc1
JB
526 (messagePayload as OCPPError)?.code ?? ErrorType.GENERIC_ERROR,
527 (messagePayload as OCPPError)?.message ?? '',
528 (messagePayload as OCPPError)?.details ?? { commandName },
529 ] as ErrorResponse);
e7accadb
JB
530 break;
531 }
532 return messageToSend;
533 }
534
82fa1110
JB
535 private cacheRequestPromise(
536 chargingStation: ChargingStation,
82fa1110
JB
537 messageId: string,
538 messagePayload: JsonType,
539 commandName: RequestCommand | IncomingRequestCommand,
54a8fbc7
JB
540 responseCallback: ResponseCallback,
541 errorCallback: ErrorCallback,
82fa1110
JB
542 ): void {
543 chargingStation.requests.set(messageId, [
544 responseCallback,
545 errorCallback,
546 commandName,
547 messagePayload,
548 ]);
549 }
550
ef6fa3fb 551 // eslint-disable-next-line @typescript-eslint/no-unused-vars
e0b0ee21 552 public abstract requestHandler<ReqType extends JsonType, ResType extends JsonType>(
08f130a0 553 chargingStation: ChargingStation,
94a464f9 554 commandName: RequestCommand,
e1d9a0f4 555 // FIXME: should be ReqType
5cc4b63b 556 commandParams?: JsonType,
5edd8ba0 557 params?: RequestParams,
e0b0ee21 558 ): Promise<ResType>;
c0560973 559}