Remove active flag / separate assign to add-remove methods / remove unused code
MODIFY
src/authorization/Authorizations.ts
src/authorization/Authorizations.ts
@@ -484,10 +484,6 @@ export default class Authorizations {
return Authorizations.canPerformAction(loggedUser, Entity.USER_CAR, Action.CREATE);
}
- public static canUpdateUsersCar(loggedUser: UserToken): boolean {
- return Authorizations.canPerformAction(loggedUser, Entity.USERS_CARS, Action.UPDATE);
- }
-
public static canSynchronizeCarCatalogs(loggedUser: UserToken): boolean {
return Authorizations.canPerformAction(loggedUser, Entity.CAR_CATALOGS, Action.SYNCHRONIZE_CAR_CATALOGS);
}

MODIFY
src/integration/pricing/convergent-charging
src/integration/pricing/convergent-charging
-Subproject commit a6e7e7acefd3a7f6fd302d12932946c1d117f62f
+Subproject commit 0c215b3934227dee43c7921784254e6b52139407

MODIFY
src/integration/refund/concur
src/integration/refund/concur
-Subproject commit 059e0ed0a96c43d4bdb796e04951befdb80ff2be
+Subproject commit 9beebdbfb701a1671b16038ff27a1b83f0424a8a

MODIFY
src/integration/smart-charging/export/sap-smart-charging/index.ts
src/integration/smart-charging/export/sap-smart-charging/index.ts
-export { default } from '../../sap-smart-charging';
+export { default } from '../../dummy/DummySmartChargingIntegration';

MODIFY
src/integration/smart-charging/sap-smart-charging
src/integration/smart-charging/sap-smart-charging
-Subproject commit 4ff9007bc6eb7300a34ad768cb4470eed512bba5
+Subproject commit 879f094b38825db4e1ef871cdbbf052b2c5ecd9b

MODIFY
src/server/rest/CentralRestServerService.ts
src/server/rest/CentralRestServerService.ts
@@ -97,7 +97,7 @@ class RequestMapper {
[ServerAction.INTEGRATION_CONNECTION_CREATE]: ConnectionService.handleCreateConnection.bind(this),
[ServerAction.CHARGING_STATION_REQUEST_OCPP_PARAMETERS]: ChargingStationService.handleRequestChargingStationOcppParameters.bind(this),
[ServerAction.CAR_CREATE]: CarService.handleCarCreate.bind(this),
- [ServerAction.ASSIGN_USERS_CAR]: CarService.handleAssignUsersCar.bind(this)
+ [ServerAction.ADD_USERS_TO_CAR]: CarService.handleAddUsersToCar.bind(this)
});
break;
@@ -234,6 +234,7 @@ class RequestMapper {
[ServerAction.INTEGRATION_CONNECTION_DELETE]: ConnectionService.handleDeleteConnection.bind(this),
[ServerAction.SETTING_DELETE]: SettingService.handleDeleteSetting.bind(this),
[ServerAction.OCPI_ENDPOINT_DELETE]: OCPIEndpointService.handleDeleteOcpiEndpoint.bind(this),
+ [ServerAction.REMOVE_USERS_FROM_CAR]: CarService.handleRemoveUsersFromCar.bind(this),
});
break;
}

MODIFY
src/server/rest/service/CarService.ts
src/server/rest/service/CarService.ts
@@ -310,13 +310,13 @@ export default class CarService {
});
}
// Check Car
- const car = await CarStorage.getCar(req.user.tenantID, filteredRequest.id);
+ const car = await CarStorage.getCar(req.user.tenantID, filteredRequest.id, true);
UtilsService.assertObjectExists(action, car, `Car ID '${filteredRequest.id}' does not exist`,
MODULE_NAME, 'handleUpdateCar', req.user);
// Check Owner if Basic
let userCar;
if (Authorizations.isBasic(req.user)) {
- userCar = await CarStorage.getUserCarByCarUser(req.user.tenantID, filteredRequest.id, req.user.id);
+ userCar = car.usersCar.find((userCarToFind) => userCarToFind.userID.toString() === req.user.id);
if (!userCar || !userCar.owner) {
throw new AppAuthError({
errorCode: HTTPAuthError.ERROR,
@@ -429,9 +429,9 @@ export default class CarService {
const filteredRequest = CarSecurity.filterCarRequest(req.query);
UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetCar', req.user);
// Get the car
- const car = await CarStorage.getCar(req.user.tenantID, filteredRequest.ID);
+ const car = await CarStorage.getCar(req.user.tenantID, filteredRequest.ID, true);
if (Authorizations.isBasic(req.user)) {
- const userCar = await CarStorage.getUserCarByCarUser(req.user.tenantID, filteredRequest.ID, req.user.id);
+ const userCar = car.usersCar.find((userCarToFind) => userCarToFind.userID.toString() === req.user.id);
if (userCar) {
car.isDefault = userCar.default;
}
@@ -472,23 +472,23 @@ export default class CarService {
next();
}
- public static async handleAssignUsersCar(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ public static async handleAddUsersToCar(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.CAR, Action.CREATE, Entity.USERS_CARS,
- MODULE_NAME, 'handleAssignUsersCar');
+ MODULE_NAME, 'handleAddUsersToCar');
// Check auth
if (!Authorizations.canAssignUsersCar(req.user)) {
throw new AppAuthError({
errorCode: HTTPAuthError.ERROR,
user: req.user,
action: Action.CREATE, entity: Entity.USERS_CARS,
- module: MODULE_NAME, method: 'handleAssignUsersCar'
+ module: MODULE_NAME, method: 'handleAddUsersToCar'
});
}
const filteredRequest = CarSecurity.filterUsersAssignRequest(req.body);
// CarID is mandatory
UtilsService.assertIdIsProvided(action, filteredRequest.carID, 'CarSecurity', 'filterUsersAssignRequest', req.user);
- const result = await CarStorage.assignUsersCar(req.user.tenantID, filteredRequest.carID, filteredRequest.usersCar);
+ const result = await CarStorage.addUsersToCar(req.user.tenantID, filteredRequest.carID, filteredRequest.usersCar);
// Return
res.json({ ...result, ...Constants.REST_RESPONSE_SUCCESS });
next();
@@ -499,7 +499,7 @@ export default class CarService {
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.CAR, Action.UPDATE, Entity.USERS_CARS,
MODULE_NAME, 'handleUpdateUsersCar');
// Check auth
- if (!Authorizations.canUpdateUsersCar(req.user)) {
+ if (!Authorizations.canAssignUsersCar(req.user)) {
throw new AppAuthError({
errorCode: HTTPAuthError.ERROR,
user: req.user,
@@ -515,4 +515,25 @@ export default class CarService {
res.json({ ...result, ...Constants.REST_RESPONSE_SUCCESS });
next();
}
+
+ public static async handleRemoveUsersFromCar(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check if component is active
+ UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.CAR, Action.DELETE, Entity.USERS_CARS,
+ MODULE_NAME, 'handleRemoveUsersFromCar');
+ // Check auth
+ if (!Authorizations.canAssignUsersCar(req.user)) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.ERROR,
+ user: req.user,
+ action: Action.DELETE, entity: Entity.USERS_CARS,
+ module: MODULE_NAME, method: 'handleDeleteUsersCar'
+ });
+ }
+ const usersCarIDs = CarSecurity.filterUsersCarRequestByIDs(req.body);
+ const result = await CarStorage.removeUsersFromCar(req.user.tenantID, usersCarIDs);
+
+ res.json({ ...result, ...Constants.REST_RESPONSE_SUCCESS });
+ next();
+ }
+
}

MODIFY
src/server/rest/service/UserService.ts
src/server/rest/service/UserService.ts
@@ -796,7 +796,7 @@ export default class UserService {
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.ORGANIZATION,
Action.READ, Entity.USER, MODULE_NAME, 'handleGetUsers');
}
- if (filteredRequest.ExcludeUserIDs && filteredRequest.ExcludeUserIDs.length > 0) {
+ if (filteredRequest.ExcludeUserIDs || filteredRequest.NotAssignedToCarID) {
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.CAR,
Action.READ, Entity.USER, MODULE_NAME, 'handleGetUsers');
}
@@ -809,7 +809,8 @@ export default class UserService {
roles: (filteredRequest.Role ? filteredRequest.Role.split('|') : null),
statuses: (filteredRequest.Status ? filteredRequest.Status.split('|') : null),
excludeSiteID: filteredRequest.ExcludeSiteID,
- excludeUserIDs: (filteredRequest.ExcludeUserIDs ? filteredRequest.ExcludeUserIDs.split('|') : null)
+ excludeUserIDs: (filteredRequest.ExcludeUserIDs ? filteredRequest.ExcludeUserIDs.split('|') : null),
+ notAssignedToCarID: filteredRequest.NotAssignedToCarID
},
{
limit: filteredRequest.Limit,

MODIFY
src/server/rest/service/security/CarSecurity.ts
src/server/rest/service/security/CarSecurity.ts
@@ -288,6 +288,10 @@ export default class CarSecurity {
return filteredRequest;
}
+ public static filterUsersCarRequestByIDs(request: any): string[] {
+ return request.usersCarIDs.map(sanitize);
+ }
+
public static filterUsersAssignRequest(request: any): HttpUsersAssignRequest {
const usersCar: UserCar[] = [];
if (!Utils.isEmptyArray(request.usersCar)) {

MODIFY
src/server/rest/service/security/UserSecurity.ts
src/server/rest/service/security/UserSecurity.ts
@@ -49,6 +49,9 @@ export default class UserSecurity {
if (request.ExcludeUserIDs) {
request.ExcludeUserIDs = sanitize(request.ExcludeUserIDs);
}
+ if (request.NotAssignedToCarID) {
+ request.NotAssignedToCarID = sanitize(request.NotAssignedToCarID);
+ }
UtilsSecurity.filterSkipAndLimit(request, request);
UtilsSecurity.filterSort(request, request);
return request as HttpUsersRequest;

MODIFY
src/storage/mongodb/CarStorage.ts
src/storage/mongodb/CarStorage.ts
@@ -510,9 +510,9 @@ export default class CarStorage {
return userCarMDB._id.toHexString();
}
- public static async assignUsersCar(tenantID: string, carID: string, usersCarToSave: UserCar[]): Promise<ActionsResponse> {
+ public static async addUsersToCar(tenantID: string, carID: string, usersCarToSave: UserCar[]): Promise<ActionsResponse> {
// Debug
- const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'AssignUsersCar');
+ const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'addUsersToCar');
const actionsDone: ActionsResponse = {
inSuccess: 0,
inError: 0
@@ -527,7 +527,6 @@ export default class CarStorage {
}
userCar.carID = carID;
userCar.createdOn = new Date();
- userCar.active = true;
await CarStorage.saveUserCar(tenantID, userCar);
actionsDone.inSuccess++;
} catch (error) {
@@ -535,7 +534,7 @@ export default class CarStorage {
}
}
// Log
- Utils.logActionsResponse(tenantID, ServerAction.ASSIGN_USERS_CAR,
+ Utils.logActionsResponse(tenantID, ServerAction.ADD_USERS_TO_CAR,
MODULE_NAME, 'AssignUsersCar', actionsDone,
'{{inSuccess}} user(s) were successfully assigned',
'{{inError}} user(s) failed to be assigned',
@@ -545,23 +544,41 @@ export default class CarStorage {
return actionsDone;
}
- public static async updateUsersCar(tenantID: string, carID: string, usersCarToSave: UserCar[]): Promise<ActionsResponse> {
+ public static async removeUsersFromCar(tenantID: string, usersCarIDs: string[]): Promise<ActionsResponse> {
// Debug
const actionsDone: ActionsResponse = {
inSuccess: 0,
inError: 0
};
- const deactivated = await global.database.getCollection(tenantID, 'userscars').updateMany({
- $and: [{ carID: Utils.convertToObjectID(carID) },
- { userID: { $nin: usersCarToSave.map((userCarToSave) => Utils.convertToObjectID(userCarToSave.userID)) } }]
- }, {
- $set: {
- active: false
+ await Utils.checkTenant(tenantID);
+ // Set
+ for (const userCarID of usersCarIDs) {
+ try {
+ await global.database.getCollection(tenantID, 'userscars')
+ .findOneAndDelete({ '_id': Utils.convertToObjectID(userCarID) });
+ actionsDone.inSuccess++;
+ } catch (error) {
+ actionsDone.inError++;
}
}
+ // Log
+ Utils.logActionsResponse(tenantID, ServerAction.REMOVE_USERS_FROM_CAR,
+ MODULE_NAME, 'removeUsersFromCar', actionsDone,
+ '{{inSuccess}} user(s) were successfully deleted',
+ '{{inError}} user(s) failed to be deleted',
+ '{{inSuccess}} user(s) were successfully deleted and {{inError}} failed to be deleted',
+ 'All the users are up to date'
);
- actionsDone.inSuccess += deactivated.modifiedCount;
- // Check Tenant
+ return actionsDone;
+ }
+
+
+ public static async updateUsersCar(tenantID: string, carID: string, usersCarToSave: UserCar[]): Promise<ActionsResponse> {
+ // Debug
+ const actionsDone: ActionsResponse = {
+ inSuccess: 0,
+ inError: 0
+ };
await Utils.checkTenant(tenantID);
// Set
for (const userCar of usersCarToSave) {
@@ -609,12 +626,12 @@ export default class CarStorage {
return actionsDone;
}
- public static async getCar(tenantID: string, carID: string = Constants.UNKNOWN_STRING_ID, projectFields?: string[]): Promise<Car> {
+ public static async getCar(tenantID: string, carID: string = Constants.UNKNOWN_STRING_ID, withUsers?: boolean, projectFields?: string[]): Promise<Car> {
// Debug
const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'getCar');
// Query single Car
const carsMDB = await CarStorage.getCars(tenantID,
- { carIDs: [carID] },
+ { carIDs: [carID], withUsers: withUsers },
Constants.DB_PARAMS_SINGLE_RECORD, projectFields);
Logging.traceEnd(MODULE_NAME, 'getCar', uniqueTimerID, { carID });
return carsMDB.count > 0 ? carsMDB.result[0] : null;

MODIFY
src/storage/mongodb/UserStorage.ts
src/storage/mongodb/UserStorage.ts
@@ -490,7 +490,7 @@ export default class UserStorage {
public static async getUsers(tenantID: string,
params: {
- notificationsActive?: boolean; siteIDs?: string[]; excludeSiteID?: string; search?: string; excludeUserIDs?: string[];
+ notificationsActive?: boolean; siteIDs?: string[]; excludeSiteID?: string; search?: string; excludeUserIDs?: string[]; notAssignedToCarID?: string;
userID?: string; tagID?: string; email?: string; issuer?: boolean; passwordResetHash?: string; roles?: string[];
statuses?: string[]; withImage?: boolean; billingUserID?: string; notSynchronizedBillingData?: boolean;
notifications?: any; noLoginSince?: Date;
@@ -576,6 +576,12 @@ export default class UserStorage {
tenantID, aggregation, localField: '_id', foreignField: 'userID', asField: 'sessionsCount', countField: 'tagID'
});
}
+ if (params.notAssignedToCarID) {
+ DatabaseUtils.pushUserCarLookupInAggregation({
+ tenantID, aggregation, localField: '_id', foreignField: 'userID', asField: 'usersCar'
+ });
+ filters['usersCar.carID'] = { $ne: Utils.convertToObjectID(params.notAssignedToCarID) };
+ }
// Select non-synchronized billing data
if (params.notSynchronizedBillingData) {
filters.$or = [

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -67,7 +67,7 @@ export enum ServerAction {
CAR = 'Car',
USERS_CAR = 'UsersCar',
SYNCHRONIZE_CAR_CATALOGS = 'SynchronizeCarCatalogs',
- ASSIGN_USERS_CAR = 'AssignUsersCar',
+ ADD_USERS_TO_CAR = 'AddUsersToCar',
GET_CONNECTOR_CURRENT_LIMIT = 'GetConnectorCurrentLimit',
REGISTER_USER = 'RegisterUser',
@@ -213,7 +213,7 @@ export enum ServerAction {
CAR_CATALOG_SYNCHRONIZATION_FAILED = 'CarCatalogSynchronizationFailed',
CAR_CATALOG_SYNCHRONIZATION = 'CarCatalogSynchronization',
-
+ REMOVE_USERS_FROM_CAR = 'RemoveUsersFromCar',
SESSION_NOT_STARTED_AFTER_AUTHORIZE = 'SessionNotStartedAfterAuthorize',
UPDATE_CHARGING_STATION_WITH_TEMPLATE = 'UpdateChargingStationWithTemplate',

MODIFY
src/types/requests/HttpUserRequest.ts
src/types/requests/HttpUserRequest.ts
@@ -34,6 +34,7 @@ export interface HttpUsersRequest extends HttpDatabaseRequest {
ErrorType?: string;
ExcludeSiteID: string;
ExcludeUserIDs: string;
+ NotAssignedToCarID: string;
}
export interface HttpUserSitesRequest extends HttpDatabaseRequest {
@@ -52,7 +53,7 @@ export interface HttpResetPasswordRequest {
email: string;
tenant: string;
captcha: string;
- passwords: { password: string, repeatPassword: string }; // Frontend...
+ passwords: { password: string; repeatPassword: string }; // Frontend...
password?: string;
repeatPassword?: string;
hash: string;

Remove active flag / separate assign to add-remove methods / remove unused code