Optimized Company service
MODIFY
src/server/rest/CentralRestServerService.ts
src/server/rest/CentralRestServerService.ts
@@ -129,7 +129,6 @@ class RequestMapper {
[ServerAction.BOOT_NOTIFICATION]: ChargingStationService.handleGetBootNotifications.bind(this),
[ServerAction.COMPANIES]: CompanyService.handleGetCompanies.bind(this),
[ServerAction.COMPANY]: CompanyService.handleGetCompany.bind(this),
- [ServerAction.COMPANY_LOGO]: CompanyService.handleGetCompanyLogo.bind(this),
[ServerAction.ASSETS]: AssetService.handleGetAssets.bind(this),
[ServerAction.ASSET]: AssetService.handleGetAsset.bind(this),
[ServerAction.ASSET_IMAGE]: AssetService.handleGetAssetImage.bind(this),
@@ -300,6 +299,9 @@ export default class CentralRestServerService {
case ServerAction.CAR_CATALOG_IMAGE:
await CarService.handleGetCarCatalogImage(action, req, res, next);
break;
+ case ServerAction.COMPANY_LOGO:
+ await CompanyService.handleGetCompanyLogo(action, req, res, next);
+ break;
// Firmware Download
case ServerAction.FIRMWARE_DOWNLOAD:
await ChargingStationService.handleGetFirmware(action, req, res, next);

MODIFY
src/server/rest/v1/service/CarService.ts
src/server/rest/v1/service/CarService.ts
@@ -33,10 +33,8 @@ export default class CarService {
throw new AppAuthError({
errorCode: HTTPAuthError.ERROR,
user: req.user,
- action: Action.LIST,
- entity: Entity.CAR_CATALOGS,
- module: MODULE_NAME,
- method: 'handleGetCarCatalogs'
+ action: Action.LIST, entity: Entity.CAR_CATALOGS,
+ module: MODULE_NAME, method: 'handleGetCarCatalogs'
});
}
// Filter
@@ -71,10 +69,8 @@ export default class CarService {
throw new AppAuthError({
errorCode: HTTPAuthError.ERROR,
user: req.user,
- action: Action.LIST,
- entity: Entity.CAR_CATALOG,
- module: MODULE_NAME,
- method: 'handleGetCarCatalog'
+ action: Action.LIST, entity: Entity.CAR_CATALOG,
+ module: MODULE_NAME, method: 'handleGetCarCatalog'
});
}
// Filter
@@ -84,12 +80,14 @@ export default class CarService {
if (!Authorizations.isSuperAdmin(req.user)) {
// Get the car
carCatalog = await CarStorage.getCarCatalog(filteredRequest.ID,
- ['id', 'vehicleModel', 'vehicleMake', 'vehicleModelVersion', 'batteryCapacityFull', 'fastchargeChargeSpeed',
+ [
+ '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',
- 'chargeAlternativePhase', 'chargeOptionPower', 'chargeOptionPhase', 'image', 'chargeOptionPhaseAmp', 'chargeAlternativePhaseAmp']);
+ 'chargeAlternativePhase', 'chargeOptionPower', 'chargeOptionPhase', 'image', 'chargeOptionPhaseAmp', 'chargeAlternativePhaseAmp'
+ ]);
} else {
// Get the car
carCatalog = await CarStorage.getCarCatalog(filteredRequest.ID);
@@ -130,10 +128,8 @@ export default class CarService {
throw new AppAuthError({
errorCode: HTTPAuthError.ERROR,
user: req.user,
- action: Action.READ,
- entity: Entity.CAR_CATALOG,
- module: MODULE_NAME,
- method: 'handleGetCarCatalogImages'
+ action: Action.READ, entity: Entity.CAR_CATALOG,
+ module: MODULE_NAME, method: 'handleGetCarCatalogImages'
});
}
// Filter

MODIFY
src/server/rest/v1/service/CompanyService.ts
src/server/rest/v1/service/CompanyService.ts
@@ -86,42 +86,36 @@ export default class CompanyService {
});
}
// Get it
- const company = await CompanyStorage.getCompany(req.user.tenantID, filteredRequest.ID);
+ const company = await CompanyStorage.getCompany(req.user.tenantID, filteredRequest.ID,
+ [ 'id', 'name', 'issuer', 'logo', 'address' ]);
UtilsService.assertObjectExists(action, company, `Company with ID '${filteredRequest.ID}' does not exist`,
MODULE_NAME, 'handleGetCompany', req.user);
// Return
- res.json(
- // Filter
- CompanySecurity.filterCompanyResponse(company, req.user)
- );
+ res.json(company);
next();
}
public static async handleGetCompanyLogo(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
- // Check if component is active
- UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.ORGANIZATION,
- Action.READ, Entity.COMPANY, MODULE_NAME, 'handleGetCompanyLogo');
// Filter
- const companyID = CompanySecurity.filterCompanyRequestByID(req.query);
- UtilsService.assertIdIsProvided(action, companyID, MODULE_NAME, 'handleGetCompanyLogo', req.user);
- // Check auth
- if (!Authorizations.canReadCompany(req.user, companyID)) {
- throw new AppAuthError({
- errorCode: HTTPAuthError.ERROR,
- user: req.user,
- action: Action.READ, entity: Entity.COMPANY,
- module: MODULE_NAME, method: 'handleGetCompanyLogo',
- value: companyID
- });
- }
- // Get Company
- const company = await CompanyStorage.getCompany(req.user.tenantID, companyID);
- UtilsService.assertObjectExists(action, company, `Company with ID '${companyID}' does not exist`,
- MODULE_NAME, 'handleGetCompanyLogo', req.user);
+ const filteredRequest = CompanySecurity.filterCompanyLogoRequest(req.query);
+ UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetCompanyLogo', req.user);
// Get the Logo
- const companyLogo = await CompanyStorage.getCompanyLogo(req.user.tenantID, companyID);
+ const companyLogo = await CompanyStorage.getCompanyLogo(filteredRequest.TenantID, filteredRequest.ID);
// Return
- res.json(companyLogo);
+ if (companyLogo?.logo) {
+ let header = 'image';
+ let encoding: BufferEncoding = 'base64';
+ // Remove encoding header
+ if (companyLogo.logo.startsWith('data:image/')) {
+ header = companyLogo.logo.substring(5, companyLogo.logo.indexOf(';'));
+ encoding = companyLogo.logo.substring(companyLogo.logo.indexOf(';') + 1, companyLogo.logo.indexOf(',')) as BufferEncoding;
+ companyLogo.logo = companyLogo.logo.substring(companyLogo.logo.indexOf(',') + 1);
+ }
+ res.setHeader('content-type', header);
+ res.send(companyLogo.logo ? Buffer.from(companyLogo.logo, encoding) : null);
+ } else {
+ res.send(null);
+ }
next();
}
@@ -138,6 +132,11 @@ export default class CompanyService {
module: MODULE_NAME, method: 'handleGetCompanies'
});
}
+ // Check User
+ let userProject: string[] = [];
+ if (Authorizations.canListUsers(req.user)) {
+ userProject = [ 'createdBy.name', 'createdBy.firstName', 'lastChangedBy.name', 'lastChangedBy.firstName' ];
+ }
// Filter
const filteredRequest = CompanySecurity.filterCompaniesRequest(req.query);
// Get the companies
@@ -152,10 +151,8 @@ export default class CompanyService {
locMaxDistanceMeters: filteredRequest.LocMaxDistanceMeters,
},
{ limit: filteredRequest.Limit, skip: filteredRequest.Skip, sort: filteredRequest.Sort, onlyRecordCount: filteredRequest.OnlyRecordCount },
- [ 'id', 'name', 'address', 'logo', 'issuer', 'distanceMeters', 'createdOn', 'createdBy', 'lastChangedOn', 'lastChangedBy']
+ [ 'id', 'name', 'address', 'logo', 'issuer', 'distanceMeters', 'createdOn', 'lastChangedOn', ...userProject ]
);
- // Filter
- CompanySecurity.filterCompaniesResponse(companies, req.user);
// Return
res.json(companies);
next();
@@ -236,11 +233,13 @@ export default class CompanyService {
// Update
company.name = filteredRequest.name;
company.address = filteredRequest.address;
+ if (Utils.objectHasProperty(filteredRequest, 'logo')) {
company.logo = filteredRequest.logo;
+ }
company.lastChangedBy = { 'id': req.user.id };
company.lastChangedOn = new Date();
// Update Company
- await CompanyStorage.saveCompany(req.user.tenantID, company);
+ await CompanyStorage.saveCompany(req.user.tenantID, company, Utils.objectHasProperty(filteredRequest, 'logo') ? true : false);
// Log
Logging.logSecurityInfo({
tenantID: req.user.tenantID,

MODIFY
src/server/rest/v1/service/security/CompanySecurity.ts
src/server/rest/v1/service/security/CompanySecurity.ts
-import Authorizations from '../../../../../authorization/Authorizations';
+import { HttpCompaniesRequest, HttpCompanyLogoRequest } from '../../../../../types/requests/HttpCompanyRequest';
+
import Company from '../../../../../types/Company';
-import { DataResult } from '../../../../../types/DataResult';
import HttpByIDRequest from '../../../../../types/requests/HttpByIDRequest';
-import { HttpCompaniesRequest } from '../../../../../types/requests/HttpCompanyRequest';
-import SiteSecurity from './SiteSecurity';
-import UserToken from '../../../../../types/UserToken';
import Utils from '../../../../../utils/Utils';
import UtilsSecurity from './UtilsSecurity';
import sanitize from 'mongo-sanitize';
@@ -15,6 +12,13 @@ export default class CompanySecurity {
return sanitize(request.ID);
}
+ public static filterCompanyLogoRequest(request: any): HttpCompanyLogoRequest {
+ return {
+ ID: sanitize(request.ID),
+ TenantID: sanitize(request.TenantID),
+ };
+ }
+
public static filterCompanyRequest(request: any): HttpByIDRequest {
return {
ID: sanitize(request.ID)
@@ -60,56 +64,13 @@ export default class CompanySecurity {
}
public static _filterCompanyRequest(request: any): Partial<Company> {
- return {
+ const filteredRequest = {
name: sanitize(request.name),
- address: UtilsSecurity.filterAddressRequest(request.address),
- logo: request.logo
- };
- }
-
- public static filterCompanyResponse(company: Company, loggedUser: UserToken): Company {
- let filteredCompany;
- if (!company) {
- return null;
- }
- // Check auth
- if (Authorizations.canReadCompany(loggedUser, company.id)) {
- // Set only necessary info
- filteredCompany = {};
- filteredCompany.id = company.id;
- filteredCompany.name = company.name;
- filteredCompany.issuer = company.issuer;
- filteredCompany.logo = company.logo;
- filteredCompany.address = UtilsSecurity.filterAddressRequest(company.address);
- if (company.sites) {
- filteredCompany.sites = company.sites.map((site) => SiteSecurity.filterSiteResponse(site, loggedUser));
+ address: UtilsSecurity.filterAddressRequest(request.address)
+ } as Partial<Company>;
+ if (Utils.objectHasProperty(request, 'logo')) {
+ filteredRequest.logo = sanitize(request.logo);
}
- if (Utils.objectHasProperty(company, 'distanceMeters')) {
- filteredCompany.distanceMeters = company.distanceMeters;
- }
- // Created By / Last Changed By
- if (Authorizations.canUpdateCompany(loggedUser)) {
- UtilsSecurity.filterCreatedAndLastChanged(filteredCompany, company, loggedUser);
- }
- }
- return filteredCompany;
- }
-
- public static filterCompaniesResponse(companies: DataResult<Company>, loggedUser: UserToken): void {
- const filteredCompanies = [];
- if (!companies.result) {
- return null;
- }
- if (!Authorizations.canListCompanies(loggedUser)) {
- return null;
- }
- for (const company of companies.result) {
- // Add
- const filteredCompany = CompanySecurity.filterCompanyResponse(company, loggedUser);
- if (filteredCompany) {
- filteredCompanies.push(filteredCompany);
- }
- }
- companies.result = filteredCompanies;
+ return filteredRequest;
}
}

MODIFY
src/server/rest/v1/service/security/SiteSecurity.ts
src/server/rest/v1/service/security/SiteSecurity.ts
@@ -131,7 +131,7 @@ export default class SiteSecurity {
filteredSite.address = UtilsSecurity.filterAddressRequest(site.address);
}
if (site.company) {
- filteredSite.company = CompanySecurity.filterCompanyResponse(site.company, loggedUser);
+ filteredSite.company = site.company;
}
if (site.siteAreas) {
filteredSite.siteAreas = SiteAreaSecurity.filterSiteAreasResponse({

MODIFY
src/storage/mongodb/CompanyStorage.ts
src/storage/mongodb/CompanyStorage.ts
@@ -14,10 +14,11 @@ const MODULE_NAME = 'CompanyStorage';
export default class CompanyStorage {
- public static async getCompany(tenantID: string, id: string = Constants.UNKNOWN_OBJECT_ID): Promise<Company> {
+ public static async getCompany(tenantID: string, id: string = Constants.UNKNOWN_OBJECT_ID, projectFields?: string[]): Promise<Company> {
const companiesMDB = await CompanyStorage.getCompanies(tenantID, {
- companyIDs: [id]
- }, Constants.DB_PARAMS_SINGLE_RECORD);
+ companyIDs: [id],
+ withLogo: true,
+ }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields);
return companiesMDB.count === 1 ? companiesMDB.result[0] : null;
}
@@ -184,14 +185,17 @@ export default class CompanyStorage {
}
// Company Logo
if (params.withLogo) {
- DatabaseUtils.pushCollectionLookupInAggregation('companylogos',
- {
- tenantID, aggregation, localField: '_id', foreignField: '_id',
- asField: 'companylogos', oneToOneCardinality: true
+ aggregation.push({
+ $addFields: {
+ logo: {
+ $concat: [
+ `${Utils.buildRestServerURL()}/client/util/CompanyLogo?ID=`,
+ { $toString: '$_id' },
+ `&TenantID=${tenantID}&LastChangedOn=`,
+ { $toString: '$lastChangedOn' }
+ ] }
}
- );
- // Rename
- DatabaseUtils.pushRenameField(aggregation, 'companylogos.logo', 'logo');
+ });
}
// Add Created By / Last Changed By
DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation);

MODIFY
src/types/requests/HttpCompanyRequest.ts
src/types/requests/HttpCompanyRequest.ts
+import HttpByIDRequest from './HttpByIDRequest';
import HttpDatabaseRequest from './HttpDatabaseRequest';
export interface HttpCompaniesRequest extends HttpDatabaseRequest {
@@ -8,3 +9,7 @@ export interface HttpCompaniesRequest extends HttpDatabaseRequest {
LocCoordinates?: number[];
LocMaxDistanceMeters?: number;
}
+
+export interface HttpCompanyLogoRequest extends HttpByIDRequest {
+ TenantID: string;
+}

Optimized Company service