Provide car catalog image URL in getCars/CarCatalogs right away + Send back the decoded image with right content-type + Removed WithImages param + Rename HTTP GET param + Moved Utils constants in class
MODIFY
src/server/rest/CentralRestServer.ts
src/server/rest/CentralRestServer.ts
@@ -82,17 +82,6 @@ export default class CentralRestServer {
req.query.FileName = 'r7_update_3.3.0.12_d4.epk';
await CentralRestServerService.restServiceUtil(req, res, next);
});
- // Catchall for util with logging
- this.expressApplication.all(['/client/util/*', '/client%2Futil%2F*'], (req: Request, res: Response) => {
- Logging.logDebug({
- tenantID: Constants.DEFAULT_TENANT,
- module: MODULE_NAME, method: 'constructor',
- action: ServerAction.EXPRESS_SERVER,
- message: `Unhandled URL ${req.method} request (original URL ${req.originalUrl})`,
- detailedMessages: 'Request: ' + util.inspect(req)
- });
- res.sendStatus(HttpStatusCodes.NOT_FOUND);
- });
// Create HTTP server to serve the express app
CentralRestServer.restHttpServer = expressTools.createHttpServer(CentralRestServer.centralSystemRestConfig, this.expressApplication);
}

MODIFY
src/server/rest/CentralRestServerService.ts
src/server/rest/CentralRestServerService.ts
@@ -270,11 +270,12 @@ class RequestMapper {
}
}
-export default {
+export default class CentralRestServerService {
// Util Service
- async restServiceUtil(req: Request, res: Response, next: NextFunction): Promise<void> {
+ public static async restServiceUtil(req: Request, res: Response, next: NextFunction): Promise<void> {
// Parse the action
const action = req.params.action as ServerAction;
+ try {
// Check Context
switch (req.method) {
// Create Request
@@ -285,8 +286,8 @@ export default {
case ServerAction.PING:
res.sendStatus(200);
break;
- case ServerAction.CAR_IMAGE:
- await CarService.handleGetCarImage(action, req, res, next);
+ case ServerAction.CAR_CATALOG_IMAGE:
+ await CarService.handleGetCarCatalogImage(action, req, res, next);
break;
// Firmware Download
case ServerAction.FIRMWARE_DOWNLOAD:
@@ -302,9 +303,12 @@ export default {
}
break;
}
- },
+ } catch (error) {
+ Logging.logActionExceptionMessageAndSendResponse(action, error, req, res, next);
+ }
+ }
- async restServiceSecured(req: Request, res: Response, next: NextFunction): Promise<void> {
+ public static async restServiceSecured(req: Request, res: Response, next: NextFunction): Promise<void> {
// Parse the action
const action = req.params.action as ServerAction;
// Check if User has been updated and require new login
@@ -326,4 +330,4 @@ export default {
Logging.logActionExceptionMessageAndSendResponse(action, error, req, res, next);
}
}
-};
+}

MODIFY
src/server/rest/service/CarService.ts
src/server/rest/service/CarService.ts
-import { Action, Entity } from '../../../types/Authorization';
-import { Car, CarType } from '../../../types/Car';
-import { HTTPAuthError, HTTPError } from '../../../types/HTTPError';
import { NextFunction, Request, Response } from 'express';
-
+import Authorizations from '../../../authorization/Authorizations';
import AppAuthError from '../../../exception/AppAuthError';
import AppError from '../../../exception/AppError';
-import Authorizations from '../../../authorization/Authorizations';
import BackendError from '../../../exception/BackendError';
import CarFactory from '../../../integration/car/CarFactory';
-import CarSecurity from './security/CarSecurity';
import CarStorage from '../../../storage/mongodb/CarStorage';
-import Constants from '../../../utils/Constants';
-import Logging from '../../../utils/Logging';
+import UserStorage from '../../../storage/mongodb/UserStorage';
+import { Action, Entity } from '../../../types/Authorization';
+import { Car, CarType } from '../../../types/Car';
+import { HTTPAuthError, HTTPError } from '../../../types/HTTPError';
import { ServerAction } from '../../../types/Server';
import TenantComponents from '../../../types/TenantComponents';
import { UserCar } from '../../../types/User';
-import UserStorage from '../../../storage/mongodb/UserStorage';
import UserToken from '../../../types/UserToken';
+import Constants from '../../../utils/Constants';
+import Logging from '../../../utils/Logging';
import Utils from '../../../utils/Utils';
+import CarSecurity from './security/CarSecurity';
import UtilsService from './UtilsService';
+
const MODULE_NAME = 'CarService';
export default class CarService {
@@ -45,9 +45,6 @@ export default class CarService {
'performanceAcceleration', 'rangeWLTP', 'rangeReal', 'efficiencyReal',
'chargeStandardPower', 'chargeStandardPhase', 'chargeStandardPhaseAmp', 'chargeAlternativePower', 'chargeOptionPower',
'chargeOptionPhaseAmp', 'chargeOptionPhase', 'chargeAlternativePhaseAmp', 'chargeAlternativePhase', 'chargePlug', 'fastChargePlug', 'fastChargePowerMax', 'drivetrainPowerHP'];
- if (filteredRequest.WithImages) {
- projectFields.push('image');
- }
// Get the cars
const carCatalogs = await CarStorage.getCarCatalogs(
{
@@ -102,16 +99,25 @@ export default class CarService {
next();
}
-
- public static async handleGetCarImage(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ public static async handleGetCarCatalogImage(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Filter
const filteredRequest = CarSecurity.filterCarCatalogRequest(req.query);
- UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetCarCatalog', req.user);
- let carCatalog;
+ UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetCarCatalogImage', req.user);
// Get the car Image
- carCatalog = await CarStorage.getCarCatalog(filteredRequest.ID, ['image']);
+ const carCatalog = await CarStorage.getCarCatalog(filteredRequest.ID, ['image']);
// Return
- res.send(carCatalog.image);
+ let header = 'image';
+ let encoding: BufferEncoding = 'base64';
+ // Remove encoding header
+ if (carCatalog.image.startsWith('data:image/')) {
+ header = carCatalog.image.substring(5, carCatalog.image.indexOf(';'));
+ encoding = carCatalog.image.substring(carCatalog.image.indexOf(';') + 1, carCatalog.image.indexOf(',')) as BufferEncoding;
+ carCatalog.image = carCatalog.image.substring(carCatalog.image.indexOf(',') + 1);
+ }
+ // Revert to binary
+ res.setHeader('content-type', header);
+ res.send(Buffer.from(carCatalog.image, encoding));
+ next();
}
public static async handleGetCarCatalogImages(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {

MODIFY
src/server/rest/service/security/CarSecurity.ts
src/server/rest/service/security/CarSecurity.ts
@@ -25,7 +25,6 @@ export default class CarSecurity {
const filteredRequest: HttpCarCatalogsRequest = {
Search: sanitize(request.Search),
CarMaker: sanitize(request.CarMaker),
- WithImages: UtilsSecurity.filterBoolean(request.WithImages),
} as HttpCarCatalogsRequest;
UtilsSecurity.filterSkipAndLimit(request, filteredRequest);
UtilsSecurity.filterSort(request, filteredRequest);
@@ -34,7 +33,7 @@ export default class CarSecurity {
public static filterCarCatalogImagesRequest(request: any): HttpCarCatalogImagesRequest {
const filteredRequest: HttpCarCatalogImagesRequest = {
- CarID: sanitize(request.CarCatalogID),
+ CarID: sanitize(request.ID),
} as HttpCarCatalogImagesRequest;
UtilsSecurity.filterSkipAndLimit(request, filteredRequest);
UtilsSecurity.filterSort(request, filteredRequest);
@@ -43,7 +42,7 @@ export default class CarSecurity {
public static filterCarCatalogRequest(request: any): HttpCarCatalogByIDRequest {
const filteredRequest: HttpCarCatalogByIDRequest = {
- ID: +sanitize(request.CarCatalogID),
+ ID: sanitize(request.ID),
} as HttpCarCatalogByIDRequest;
return filteredRequest;
}
@@ -114,6 +113,7 @@ export default class CarSecurity {
vehicleModel: carCatalog.vehicleModel,
vehicleMake: carCatalog.vehicleMake,
vehicleModelVersion: carCatalog.vehicleModelVersion,
+ image: `${Utils.buildRestServerURL()}/client/util/CarCatalogImage?ID=${carCatalog.id}`,
} as CarCatalog;
}
return filteredCarCatalog;
@@ -165,7 +165,7 @@ export default class CarSecurity {
rangeReal: carCatalog.rangeReal,
rangeWLTP: carCatalog.rangeWLTP,
efficiencyReal: carCatalog.efficiencyReal,
- image: carCatalog.image,
+ image: `${Utils.buildRestServerURL()}/client/util/CarCatalogImage?ID=${carCatalog.id}`,
chargeStandardChargeSpeed: carCatalog.chargeStandardChargeSpeed,
chargeOptionPower: carCatalog.chargeOptionPower,
chargeAlternativePower: carCatalog.chargeAlternativePower,

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -57,12 +57,12 @@ export enum ServerAction {
CAR_CATALOGS = 'CarCatalogs',
CAR_CATALOG = 'CarCatalog',
+ CAR_CATALOG_IMAGE = 'CarCatalogImage',
CAR_CATALOG_IMAGES = 'CarCatalogImages',
CAR_MAKERS = 'CarMakers',
CAR_CREATE = 'CarCreate',
CAR_UPDATE = 'CarUpdate',
CAR_DELETE = 'CarDelete',
- CAR_IMAGE = 'CarImage',
CARS = 'Cars',
CAR = 'Car',
CAR_USERS = 'CarUsers',

MODIFY
src/types/requests/HttpCarRequest.ts
src/types/requests/HttpCarRequest.ts
@@ -6,7 +6,6 @@ import { UserCar } from '../User';
export interface HttpCarCatalogsRequest extends HttpDatabaseRequest {
Search?: string;
CarMaker?: string;
- WithImages?: boolean;
}
export interface HttpCarMakersRequest extends HttpDatabaseRequest {

MODIFY
src/utils/Utils.ts
src/utils/Utils.ts
@@ -43,11 +43,13 @@ import url from 'url';
import { v4 as uuid } from 'uuid';
import validator from 'validator';
-const _centralSystemFrontEndConfig = Configuration.getCentralSystemFrontEndConfig();
-const _tenants = [];
const MODULE_NAME = 'Utils';
export default class Utils {
+ private static tenants = [];
+ private static centralSystemFrontEndConfig = Configuration.getCentralSystemFrontEndConfig();
+ private static centralSystemRestConfig = Configuration.getCentralSystemRestServiceConfig();
+
public static isTransactionInProgressOnThreePhases(chargingStation: ChargingStation, transaction: Transaction): boolean {
const currentType = Utils.getChargingStationCurrentType(chargingStation, null, transaction.connectorId);
let threePhases = true;
@@ -373,7 +375,7 @@ export default class Utils {
});
}
// Check in cache
- if (_tenants.includes(tenantID)) {
+ if (Utils.tenants.includes(tenantID)) {
return Promise.resolve(null);
}
if (tenantID !== Constants.DEFAULT_TENANT) {
@@ -397,7 +399,7 @@ export default class Utils {
});
}
}
- _tenants.push(tenantID);
+ Utils.tenants.push(tenantID);
}
static convertToBoolean(value: any): boolean {
@@ -911,12 +913,15 @@ export default class Utils {
return Math.floor((Math.random() * 2147483648) + 1); // INT32 (signed: issue in Schneider)
}
+ public static buildRestServerURL() {
+ return `${Utils.centralSystemRestConfig.protocol}://${Utils.centralSystemRestConfig.host}:${Utils.centralSystemRestConfig.port}`;
+ }
+
public static buildEvseURL(subdomain: string = null): string {
if (subdomain) {
- return `${_centralSystemFrontEndConfig.protocol}://${subdomain}.${_centralSystemFrontEndConfig.host}:${_centralSystemFrontEndConfig.port}`;
+ return `${Utils.centralSystemFrontEndConfig.protocol}://${subdomain}.${Utils.centralSystemFrontEndConfig.host}:${Utils.centralSystemFrontEndConfig.port}`;
}
- return `${_centralSystemFrontEndConfig.protocol}://${_centralSystemFrontEndConfig.host}:${
- _centralSystemFrontEndConfig.port}`;
+ return `${Utils.centralSystemFrontEndConfig.protocol}://${Utils.centralSystemFrontEndConfig.host}:${Utils.centralSystemFrontEndConfig.port}`;
}
public static buildOCPPServerURL(tenantID: string, ocppVersion: OCPPVersion, ocppProtocol: OCPPProtocol, token?: string): string {

Provide car catalog image URL in getCars/CarCatalogs right away + Send back the decoded image with right content-type + Removed WithImages param + Rename HTTP GET param + Moved Utils constants in class