Optimized Car service
MODIFY
src/server/rest/v1/service/CarService.ts
src/server/rest/v1/service/CarService.ts
@@ -74,24 +74,18 @@ export default class CarService {
// Filter
const filteredRequest = CarSecurity.filterCarCatalogRequest(req.query);
UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetCarCatalog', req.user);
- let carCatalog;
- if (!Authorizations.isSuperAdmin(req.user)) {
// Get the car
- carCatalog = await CarStorage.getCarCatalog(filteredRequest.ID,
+ const carCatalog = await CarStorage.getCarCatalog(filteredRequest.ID,
[
'id', 'vehicleModel', 'vehicleMake', 'vehicleModelVersion', 'batteryCapacityFull', 'fastchargeChargeSpeed',
'performanceTopspeed', 'performanceAcceleration', 'rangeWLTP', 'rangeReal', 'efficiencyReal', 'drivetrainPropulsion',
'drivetrainTorque', 'batteryCapacityUseable', 'chargePlug', 'fastChargePlug', 'fastChargePowerMax', 'chargePlugLocation',
'drivetrainPowerHP', 'chargeStandardChargeSpeed', 'chargeStandardChargeTime', 'miscSeats', 'miscBody', 'miscIsofix', 'miscTurningCircle',
- 'miscSegment', 'miscIsofixSeats', 'chargeStandardPower', 'chargeStandardPhase', 'chargeAlternativePower',
+ 'miscSegment', 'miscIsofixSeats', 'chargeStandardPower', 'chargeStandardPhase', 'chargeAlternativePower', 'hash',
'chargeAlternativePhase', 'chargeOptionPower', 'chargeOptionPhase', 'image', 'chargeOptionPhaseAmp', 'chargeAlternativePhaseAmp'
]);
- } else {
- // Get the car
- carCatalog = await CarStorage.getCarCatalog(filteredRequest.ID);
- }
// Return
- res.json(CarSecurity.filterCarCatalogResponse(carCatalog, req.user));
+ res.json(carCatalog);
next();
}
@@ -193,9 +187,7 @@ export default class CarService {
// Filter
const filteredRequest = CarSecurity.filterCarMakersRequest(req.query);
// Get car makers
- const carMakers = await CarStorage.getCarMakers({ search: filteredRequest.Search });
- // Filter
- CarSecurity.filterCarMakersResponse(carMakers, req.user);
+ const carMakers = await CarStorage.getCarMakers({ search: filteredRequest.Search }, [ 'carMaker' ]);
res.json(carMakers);
next();
}
@@ -408,6 +400,12 @@ export default class CarService {
});
}
const filteredRequest = CarSecurity.filterCarsRequest(req.query);
+ // Check User
+ let userProject: string[] = [];
+ if (Authorizations.canListUsers(req.user)) {
+ userProject = [ 'createdBy.name', 'createdBy.firstName', 'lastChangedBy.name', 'lastChangedBy.firstName',
+ 'carUsers.user.name', 'carUsers.user.firstName', ];
+ }
// Get cars
const cars = await CarStorage.getCars(req.user.tenantID,
{
@@ -416,10 +414,12 @@ export default class CarService {
carMakers: filteredRequest.CarMaker ? filteredRequest.CarMaker.split('|') : null,
withUsers: filteredRequest.WithUsers
},
- { limit: filteredRequest.Limit, skip: filteredRequest.Skip, sort: filteredRequest.Sort, onlyRecordCount: filteredRequest.OnlyRecordCount });
- // Filter
- CarSecurity.filterCarsResponse(cars, req.user);
- // Return
+ { limit: filteredRequest.Limit, skip: filteredRequest.Skip, sort: filteredRequest.Sort, onlyRecordCount: filteredRequest.OnlyRecordCount },
+ [
+ 'id', 'type', 'vin', 'licensePlate', 'converter', 'default', 'owner', 'createdOn', 'lastChangedOn',
+ 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion', 'carCatalog.image',
+ ...userProject
+ ]);
res.json(cars);
next();
}
@@ -439,13 +439,28 @@ export default class CarService {
}
const filteredRequest = CarSecurity.filterCarRequest(req.query);
UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetCar', req.user);
+ // Check User
+ let userProject: string[] = [];
+ if (Authorizations.canListUsers(req.user)) {
+ userProject = [ 'createdBy.name', 'createdBy.firstName', 'lastChangedBy.name', 'lastChangedBy.firstName',
+ 'carUsers.user.id', 'carUsers.user.name', 'carUsers.user.firstName', 'carUsers.user.email', 'carUsers.default', 'carUsers.owner'
+ ];
+ }
// Get the car
const car = await CarStorage.getCar(req.user.tenantID, filteredRequest.ID, {
withUsers: true,
userIDs: Authorizations.isBasic(req.user) ? [req.user.id] : null
- });
+ },
+ [
+ 'id', 'type', 'vin', 'licensePlate', 'converter', 'default', 'owner', 'createdOn', 'lastChangedOn',
+ 'carCatalogID', 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion', 'carCatalog.image',
+ 'carCatalog.chargeStandardPower', 'carCatalog.chargeStandardPhaseAmp', 'carCatalog.chargeStandardPhase',
+ 'carCatalog.chargeAlternativePower', 'carCatalog.chargeAlternativePhaseAmp', 'carCatalog.chargeAlternativePhase',
+ 'carCatalog.chargeOptionPower', 'carCatalog.chargeOptionPhaseAmp', 'carCatalog.chargeOptionPhase',
+ ...userProject
+ ]);
// Return
- res.json(CarSecurity.filterCarResponse(car, req.user));
+ res.json(car);
next();
}
@@ -471,9 +486,8 @@ export default class CarService {
search: filteredRequest.Search,
carIDs: [filteredRequest.CarID]
},
- { limit: filteredRequest.Limit, skip: filteredRequest.Skip, sort: filteredRequest.Sort, onlyRecordCount: filteredRequest.OnlyRecordCount });
- // Filter
- CarSecurity.filterUsersCarsResponse(usersCars, req.user);
+ { limit: filteredRequest.Limit, skip: filteredRequest.Skip, sort: filteredRequest.Sort, onlyRecordCount: filteredRequest.OnlyRecordCount },
+ [ 'id', 'carID', 'default', 'owner', 'user.id', 'user.name', 'user.firstName', 'user.email' ]);
// Return
res.json(usersCars);
next();

MODIFY
src/server/rest/v1/service/security/CarSecurity.ts
src/server/rest/v1/service/security/CarSecurity.ts
@@ -47,139 +47,6 @@ export default class CarSecurity {
return filteredRequest;
}
- public static filterCarCatalogResponse(carCatalog: CarCatalog, loggedUser: UserToken): CarCatalog {
- let filteredCarCatalog: CarCatalog;
- if (!carCatalog) {
- return null;
- }
- // Check auth
- if (Authorizations.isSuperAdmin(loggedUser)) {
- filteredCarCatalog = carCatalog;
- } else if (Authorizations.canReadCarCatalog(loggedUser)) {
- filteredCarCatalog = {
- id: carCatalog.id,
- vehicleModel: carCatalog.vehicleModel,
- vehicleMake: carCatalog.vehicleMake,
- batteryCapacityFull: carCatalog.batteryCapacityFull,
- fastChargeSpeed: carCatalog.fastChargeSpeed,
- performanceTopspeed: carCatalog.performanceTopspeed,
- chargeStandardPower: carCatalog.chargeStandardPower,
- chargeStandardPhase: carCatalog.chargeStandardPhase,
- vehicleModelVersion: carCatalog.vehicleModelVersion,
- performanceAcceleration: carCatalog.performanceAcceleration,
- rangeReal: carCatalog.rangeReal,
- rangeWLTP: carCatalog.rangeWLTP,
- efficiencyReal: carCatalog.efficiencyReal,
- chargeStandardChargeSpeed: carCatalog.chargeStandardChargeSpeed,
- drivetrainPropulsion: carCatalog.drivetrainPropulsion,
- drivetrainTorque: carCatalog.drivetrainTorque,
- batteryCapacityUseable: carCatalog.batteryCapacityUseable,
- chargePlug: carCatalog.chargePlug,
- fastChargePlug: carCatalog.fastChargePlug,
- fastChargePowerMax: carCatalog.fastChargePowerMax,
- chargePlugLocation: carCatalog.chargePlugLocation,
- drivetrainPowerHP: carCatalog.drivetrainPowerHP,
- chargeStandardChargeTime: carCatalog.chargeStandardChargeTime,
- miscSeats: carCatalog.miscSeats,
- miscBody: carCatalog.miscBody,
- miscIsofix: carCatalog.miscIsofix,
- miscTurningCircle: carCatalog.miscTurningCircle,
- miscSegment: carCatalog.miscSegment,
- miscIsofixSeats: carCatalog.miscIsofixSeats,
- image: carCatalog.image,
- chargeAlternativePower: carCatalog.chargeAlternativePower,
- chargeAlternativePhase: carCatalog.chargeAlternativePhase,
- chargeOptionPower: carCatalog.chargeOptionPower,
- chargeOptionPhase: carCatalog.chargeOptionPhase,
- chargeOptionPhaseAmp: carCatalog.chargeOptionPhaseAmp,
- chargeAlternativePhaseAmp: carCatalog.chargeAlternativePhaseAmp,
- chargeStandardPhaseAmp: carCatalog.chargeStandardPhaseAmp
- } as CarCatalog;
- }
- // Created By / Last Changed By
- UtilsSecurity.filterCreatedAndLastChanged(filteredCarCatalog, carCatalog, loggedUser);
- return filteredCarCatalog;
- }
-
- public static filterMinimalCarCatalogResponse(carCatalog: CarCatalog, loggedUser: UserToken): CarCatalog {
- let filteredCarCatalog: CarCatalog;
- if (!carCatalog) {
- return null;
- }
- // Check auth
- if (Authorizations.canReadCarCatalog(loggedUser)) {
- filteredCarCatalog = {
- id: carCatalog.id,
- vehicleModel: carCatalog.vehicleModel,
- vehicleMake: carCatalog.vehicleMake,
- vehicleModelVersion: carCatalog.vehicleModelVersion,
- image: `${Utils.buildRestServerURL()}/client/util/CarCatalogImage?ID=${carCatalog.id}`,
- } as CarCatalog;
- }
- return filteredCarCatalog;
- }
-
- public static filterCarMakersResponse(carMakers: DataResult<CarMaker>, loggedUser: UserToken): void {
- const filteredCarConstructors: CarMaker[] = [];
- if (!carMakers) {
- return null;
- }
- if (!Authorizations.canReadCarCatalog(loggedUser)) {
- return null;
- }
- for (const carMaker of carMakers.result) {
- filteredCarConstructors.push({
- carMaker: carMaker.carMaker
- });
- }
- carMakers.result = filteredCarConstructors;
- }
-
- public static filterCarCatalogsResponse(carCatalogs: DataResult<CarCatalog>, loggedUser: UserToken): void {
- const filteredCarCatalogs = [];
-
- if (!carCatalogs.result) {
- return null;
- }
- if (!Authorizations.canListCarCatalogs(loggedUser)) {
- return null;
- }
- for (const carCatalog of carCatalogs.result) {
- // Add
- if (carCatalog) {
- filteredCarCatalogs.push({
- id: carCatalog.id,
- vehicleModel: carCatalog.vehicleModel,
- vehicleMake: carCatalog.vehicleMake,
- batteryCapacityFull: carCatalog.batteryCapacityFull,
- fastChargeSpeed: carCatalog.fastChargeSpeed,
- performanceTopspeed: carCatalog.performanceTopspeed,
- chargeStandardPower: carCatalog.chargeStandardPower,
- chargePlug: carCatalog.chargePlug,
- fastChargePlug: carCatalog.fastChargePlug,
- fastChargePowerMax: carCatalog.fastChargePowerMax,
- drivetrainPowerHP: carCatalog.drivetrainPowerHP,
- chargeStandardPhase: carCatalog.chargeStandardPhase,
- vehicleModelVersion: carCatalog.vehicleModelVersion,
- performanceAcceleration: carCatalog.performanceAcceleration,
- rangeReal: carCatalog.rangeReal,
- rangeWLTP: carCatalog.rangeWLTP,
- efficiencyReal: carCatalog.efficiencyReal,
- image: `${Utils.buildRestServerURL()}/client/util/CarCatalogImage?ID=${carCatalog.id}`,
- chargeStandardChargeSpeed: carCatalog.chargeStandardChargeSpeed,
- chargeOptionPower: carCatalog.chargeOptionPower,
- chargeAlternativePower: carCatalog.chargeAlternativePower,
- chargeStandardPhaseAmp: carCatalog.chargeStandardPhaseAmp,
- chargeOptionPhaseAmp: carCatalog.chargeOptionPhaseAmp,
- chargeAlternativePhaseAmp: carCatalog.chargeAlternativePhaseAmp,
- chargeAlternativePhase: carCatalog.chargeAlternativePhase,
- chargeOptionPhase: carCatalog.chargeOptionPhase
- });
- }
- }
- carCatalogs.result = filteredCarCatalogs;
- }
-
public static filterCarCreateRequest(request: any): HttpCarCreateRequest {
return {
vin: sanitize(request.vin),
@@ -230,87 +97,6 @@ export default class CarSecurity {
return filteredRequest;
}
- public static filterCarsResponse(cars: DataResult<Car>, loggedUser: UserToken): void {
- const filteredCars: Car[] = [];
- if (!cars.result) {
- return null;
- }
- if (!Authorizations.canListCars(loggedUser)) {
- return null;
- }
- for (const car of cars.result) {
- // Add
- if (car) {
- filteredCars.push(
- this.filterCarResponse(car, loggedUser, true));
- }
- }
- cars.result = filteredCars;
- }
-
- public static filterCarResponse(car: Car, loggedUser: UserToken, forList = false): Car {
- let filteredCar: Car;
- if (!car) {
- return null;
- }
- // Admin?
- if (Authorizations.canUpdateCar(loggedUser)) {
- // Set only necessary info
- filteredCar = {
- id: car.id,
- vin: car.vin,
- licensePlate: car.licensePlate,
- carCatalogID: car.carCatalogID,
- type: car.type,
- converter: car.converter
- };
- if (car.carUsers) {
- filteredCar.carUsers = car.carUsers.map((carUser) => ({
- ...carUser, user: UserSecurity.filterMinimalUserResponse(carUser.user, loggedUser)
- }));
- }
- if (car.carCatalog) {
- filteredCar.carCatalog = forList ? this.filterMinimalCarCatalogResponse(car.carCatalog, loggedUser) :
- this.filterCarCatalogResponse(car.carCatalog, loggedUser);
- }
- UtilsSecurity.filterCreatedAndLastChanged(filteredCar, car, loggedUser);
- }
- return filteredCar;
- }
-
- public static filterUsersCarsResponse(usersCars: DataResult<UserCar>, loggedUser: UserToken): void {
- const filteredUsersCars: UserCar[] = [];
- if (!usersCars.result) {
- return null;
- }
- if (!Authorizations.canListUsersCars(loggedUser)) {
- return null;
- }
- for (const userCar of usersCars.result) {
- // Add
- if (userCar) {
- filteredUsersCars.push(
- this.filterUserCarResponse(userCar, loggedUser)
- );
- }
- }
- usersCars.result = filteredUsersCars;
- }
-
- public static filterUserCarResponse(userCar: UserCar, loggedUser: UserToken): UserCar {
- if (!userCar) {
- return null;
- }
- const filteredUserCar: UserCar = {
- id: userCar.id,
- user: UserSecurity.filterMinimalUserResponse(userCar.user, loggedUser),
- carID: userCar.carID,
- default: userCar.default,
- owner: userCar.owner
- };
- return filteredUserCar;
- }
-
public static filterCarsRequest(request: any): HttpCarsRequest {
const filteredRequest: HttpCarsRequest = {
Search: sanitize(request.Search),

MODIFY
src/storage/mongodb/CarStorage.ts
src/storage/mongodb/CarStorage.ts
@@ -409,13 +409,11 @@ export default class CarStorage {
};
}
- public static async getCarMakers(
- params: { search?: string } = {}): Promise<DataResult<CarMaker>> {
+ public static async getCarMakers(params: { search?: string } = {}, projectFields?: string[]): Promise<DataResult<CarMaker>> {
// Debug
const uniqueTimerID = Logging.traceStart(Constants.DEFAULT_TENANT, MODULE_NAME, 'getCarMakers');
// Set the filters
const filters: ({ $or?: any[] } | undefined) = {};
-
if (params.search) {
filters.$or = [
{ 'vehicleMake': { $regex: Utils.escapeSpecialCharsInRegex(params.search), $options: 'i' } },
@@ -454,6 +452,8 @@ export default class CarStorage {
carMaker: 1
}
});
+ // Project
+ DatabaseUtils.projectFields(aggregation, projectFields);
const carMakersMDB = await global.database.getCollection<any>(Constants.DEFAULT_TENANT, 'carcatalogs')
.aggregate(aggregation, {
collation: { locale: Constants.DEFAULT_LOCALE, strength: 2 },
@@ -678,6 +678,19 @@ export default class CarStorage {
aggregation.push({
$limit: (dbParams.limit > 0 && dbParams.limit < Constants.DB_RECORD_COUNT_CEIL) ? dbParams.limit : Constants.DB_RECORD_COUNT_CEIL
});
+ // Car Image
+ aggregation.push({
+ $addFields: {
+ 'carCatalog.image': {
+ $concat: [
+ `${Utils.buildRestServerURL()}/client/util/CarCatalogImage?ID=`,
+ '$carCatalog.id',
+ '&LastChangedOn=',
+ { $toString: '$carCatalog.lastChangedOn' }
+ ]
+ }
+ }
+ });
// Add Created By / Last Changed By
DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation);
// Handle the ID

MODIFY
src/storage/mongodb/CompanyStorage.ts
src/storage/mongodb/CompanyStorage.ts
@@ -193,7 +193,8 @@ export default class CompanyStorage {
{ $toString: '$_id' },
`&TenantID=${tenantID}&LastChangedOn=`,
{ $toString: '$lastChangedOn' }
- ] }
+ ]
+ }
}
});
}

MODIFY
test/api/CarTest.ts
test/api/CarTest.ts
@@ -114,12 +114,6 @@ describe('Car Tests', function() {
expect(response.status).to.equal(500);
});
- it('Should not be able to get car catalog debug object', async () => {
- const response = await testData.centralService.carApi.readCarCatalog(carID);
- expect(response.status).to.equal(200);
- expect(response.data).to.not.have.property('hash');
- });
-
it('Should be able to create a new car', async () => {
// Create
const newCar = Factory.car.build();

Optimized Car service