Get user default tag and car in the same endpoint
MODIFY
src/authorization/Authorizations.ts
src/authorization/Authorizations.ts
@@ -296,8 +296,9 @@ export default class Authorizations {
return Authorizations.canPerformAction(loggedUser, Entity.TAGS, Action.LIST);
}
- public static canReadTag(loggedUser: UserToken): boolean {
- return Authorizations.canPerformAction(loggedUser, Entity.TAG, Action.READ);
+ public static canReadTag(loggedUser: UserToken, userId: string): boolean {
+ return Authorizations.canPerformAction(loggedUser, Entity.TAG, Action.READ,
+ { user: userId, owner: loggedUser.id });
}
public static canDeleteTag(loggedUser: UserToken): boolean {

MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -66,8 +66,10 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
{ resource: Entity.TAXES, action: [Action.LIST], attributes: ['*'] },
{ resource: Entity.INVOICES, action: [Action.LIST, Action.SYNCHRONIZE], attributes: ['*'] },
{ resource: Entity.INVOICE, action: [Action.DOWNLOAD, Action.CREATE], attributes: ['*'] },
- { resource: Entity.ASSET, action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE,
- Action.CHECK_CONNECTION, Action.RETRIEVE_CONSUMPTION], attributes: ['*'] },
+ {
+ resource: Entity.ASSET, action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE,
+ Action.CHECK_CONNECTION, Action.RETRIEVE_CONSUMPTION], attributes: ['*']
+ },
{ resource: Entity.ASSETS, action: Action.LIST, attributes: ['*'] },
{ resource: Entity.SETTINGS, action: Action.LIST, attributes: ['*'] },
{ resource: Entity.SETTING, action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE], attributes: ['*'] },
@@ -155,6 +157,10 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
]
}
},
+ {
+ resource: Entity.TAG, action: Action.READ, attributes: ['*'],
+ condition: { Fn: 'EQUALS', args: { 'user': '$.owner' } }
+ },
{
resource: Entity.CHARGING_STATION,
action: [Action.REMOTE_STOP_TRANSACTION, Action.STOP_TRANSACTION],

MODIFY
src/integration/pricing/export/convergent-charging/index.ts
src/integration/pricing/export/convergent-charging/index.ts
-export { default } from '../../convergent-charging';
+export { default } from '../../dummy/DummyPricingIntegration';

MODIFY
src/integration/refund/export/concur/index.ts
src/integration/refund/export/concur/index.ts
-export { default } from '../../concur';
+export { default } from '../../dummy/DummyRefundIntegration';

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/server/rest/CentralRestServerService.ts
src/server/rest/CentralRestServerService.ts
@@ -117,7 +117,6 @@ class RequestMapper {
[ServerAction.CAR_MAKERS]: CarService.handleGetCarMakers.bind(this),
[ServerAction.CARS]: CarService.handleGetCars.bind(this),
[ServerAction.CAR]: CarService.handleGetCar.bind(this),
- [ServerAction.DEFAULT_CAR]: CarService.handleGetUserDefaultCar.bind(this),
[ServerAction.CAR_USERS]: CarService.handleGetCarUsers.bind(this),
[ServerAction.CAR_CATALOG_IMAGES]: CarService.handleGetCarCatalogImages.bind(this),
[ServerAction.CHARGING_STATIONS_EXPORT]: ChargingStationService.handleExportChargingStations.bind(this),
@@ -159,7 +158,7 @@ class RequestMapper {
[ServerAction.NOTIFICATIONS]: NotificationService.handleGetNotifications.bind(this),
[ServerAction.TAGS]: UserService.handleGetTags.bind(this),
[ServerAction.TAG]: UserService.handleGetTag.bind(this),
- [ServerAction.DEFAUlT_TAG]: UserService.handleGetUserDefaultTag.bind(this),
+ [ServerAction.USER_DEFAUlT_TAG_CAR]: UserService.handleGetUserDefaultTagCar.bind(this),
[ServerAction.TRANSACTIONS_COMPLETED]: TransactionService.handleGetTransactionsCompleted.bind(this),
[ServerAction.TRANSACTIONS_TO_REFUND]: TransactionService.handleGetTransactionsToRefund.bind(this),
[ServerAction.TRANSACTIONS_TO_REFUND_EXPORT]: TransactionService.handleExportTransactionsToRefund.bind(this),

MODIFY
src/server/rest/v1/service/CarService.ts
src/server/rest/v1/service/CarService.ts
@@ -450,30 +450,6 @@ export default class CarService {
next();
}
- public static async handleGetUserDefaultCar(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
- // Check if component is active
- UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.CAR, Action.READ, Entity.CAR,
- MODULE_NAME, 'handleGetUserDefaultCar');
- // Check auth
- if (!Authorizations.canReadCar(req.user)) {
- throw new AppAuthError({
- errorCode: HTTPAuthError.ERROR,
- user: req.user,
- action: Action.READ, entity: Entity.CAR,
- module: MODULE_NAME, method: 'handleGetUserDefaultCar'
- });
- }
- const userID = CarSecurity.filterCarRequestByUserID(req.query);
- UtilsService.assertIdIsProvided(action, userID, MODULE_NAME, 'handleGetUserDefaultCar', req.user);
- // Get the default car
- const car = await CarStorage.getUserDefaultCar(req.user.tenantID, userID);
- UtilsService.assertObjectExists(action, car, `User with ID '${userID}' does not have any cars`,
- MODULE_NAME, 'handleGetUserDefaultTag', req.user);
- // Return
- res.json(CarSecurity.filterCarResponse(car, req.user));
- next();
- }
-
public static async handleGetCarUsers(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.CAR, Action.LIST, Entity.USERS_CARS,

MODIFY
src/server/rest/v1/service/UserService.ts
src/server/rest/v1/service/UserService.ts
@@ -9,6 +9,7 @@ import AppAuthError from '../../../../exception/AppAuthError';
import AppError from '../../../../exception/AppError';
import Authorizations from '../../../../authorization/Authorizations';
import BillingFactory from '../../../../integration/billing/BillingFactory';
+import CarStorage from '../../../../storage/mongodb/CarStorage';
import ConnectionStorage from '../../../../storage/mongodb/ConnectionStorage';
import Constants from '../../../../utils/Constants';
import Cypher from '../../../../utils/Cypher';
@@ -1013,8 +1014,9 @@ export default class UserService {
}
public static async handleGetTag(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ const tagID = UserSecurity.filterTagRequestByID(req.query);
// Check auth
- if (!Authorizations.canReadTag(req.user)) {
+ if (!Authorizations.canReadTag(req.user, tagID)) {
throw new AppAuthError({
errorCode: HTTPAuthError.ERROR,
user: req.user,
@@ -1022,7 +1024,6 @@ export default class UserService {
module: MODULE_NAME, method: 'handleGetTag'
});
}
- const tagID = UserSecurity.filterTagRequestByID(req.query);
UtilsService.assertIdIsProvided(action, tagID, MODULE_NAME, 'handleGetTag', req.user);
// Get the tag
const tag = await UserStorage.getTag(req.user.tenantID, tagID, { withUser: true });
@@ -1033,24 +1034,49 @@ export default class UserService {
next();
}
- public static async handleGetUserDefaultTag(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ public static async handleGetUserDefaultTagCar(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ const userID = UserSecurity.filterDefaultTagCarRequestByUserID(req.query);
// Check auth
- if (!Authorizations.canReadTag(req.user)) {
+ if (!Authorizations.canReadTag(req.user, userID)) {
throw new AppAuthError({
errorCode: HTTPAuthError.ERROR,
user: req.user,
action: Action.READ, entity: Entity.TAG,
- module: MODULE_NAME, method: 'handleGetUserDefaultTag'
+ module: MODULE_NAME, method: 'handleGetUserDefaultTagCar'
+ });
+ }
+ // Check auth
+ if (!Authorizations.canReadCar(req.user)) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.ERROR,
+ user: req.user,
+ action: Action.READ, entity: Entity.CAR,
+ module: MODULE_NAME, method: 'handleGetUserDefaultTagCar'
});
}
- const userID = UserSecurity.filterTagRequestByUserID(req.query);
- UtilsService.assertIdIsProvided(action, userID, MODULE_NAME, 'handleGetUserDefaultTag', req.user);
+ UtilsService.assertIdIsProvided(action, userID, MODULE_NAME, 'handleGetUserDefaultTagCar', req.user);
// Get the tag
- const tag = await UserStorage.getUserDefaultTag(req.user.tenantID, userID);
- UtilsService.assertObjectExists(action, tag, `User with ID '${userID}' does not have any tags`,
- MODULE_NAME, 'handleGetUserDefaultTag', req.user);
+ let tagsMDB = await UserStorage.getTags(req.user.tenantID, {
+ userIDs: [userID],
+ defaultTag: true,
+ active: true
+ }, Constants.DB_PARAMS_SINGLE_RECORD);
+ if (tagsMDB.count < 1) {
+ tagsMDB = await UserStorage.getTags(req.user.tenantID, {
+ userIDs: [userID],
+ active: true
+ }, Constants.DB_PARAMS_SINGLE_RECORD);
+ }
+ const tag = tagsMDB.count > 0 ? tagsMDB.result[0] : null;
+ let carsMDB = await CarStorage.getCars(req.user.tenantID,
+ { userIDs: [userID], defaultCar: true },
+ Constants.DB_PARAMS_SINGLE_RECORD);
+ carsMDB = carsMDB.count > 0 ? carsMDB : await CarStorage.getCars(req.user.tenantID,
+ { userIDs: [userID] },
+ Constants.DB_PARAMS_SINGLE_RECORD);
+ const car = carsMDB.count > 0 ? carsMDB.result[0] : null;
// Return
- res.json(UserSecurity.filterTagResponse(tag, req.user));
+ res.json(UserSecurity.filterUserDefaultTagResponse(tag, car, req.user));
next();
}
@@ -1128,12 +1154,13 @@ export default class UserService {
}
// Delete the Tag
await UserStorage.deleteTag(req.user.tenantID, tag.userID, tag);
- const user = await UserStorage.getUser(req.user.tenantID, tag.userID,{ withTag: true });
- if (!Utils.isEmptyArray(user.tags) && user.tags.length === 1) {
- if (!user.tags[0].default) {
- tag = user.tags[0];
+ const tagsMDB = await UserStorage.getTags(req.user.tenantID, {
+ userIDs: [tag.userID]
+ }, Constants.DB_PARAMS_SINGLE_RECORD);
+ if (tagsMDB.count === 1) {
+ if (!tagsMDB.result[0].default) {
+ tag = tagsMDB.result[0];
tag.default = true;
- tag.userID = user.id;
await UserStorage.saveTag(req.user.tenantID, tag);
}
}
@@ -1203,7 +1230,7 @@ export default class UserService {
});
}
// Check User
- const user = await UserStorage.getUser(req.user.tenantID, filteredRequest.userID, { withTag: true });
+ const user = await UserStorage.getUser(req.user.tenantID, filteredRequest.userID);
UtilsService.assertObjectExists(action, user, `User ID '${filteredRequest.userID}' does not exist`,
MODULE_NAME, 'handleCreateTag', req.user);
// Only current organization User can be assigned to Tag
@@ -1230,7 +1257,9 @@ export default class UserService {
createdBy: { id: req.user.id },
createdOn: new Date(),
userID: filteredRequest.userID,
- default: filteredRequest.default ? filteredRequest.default : Utils.isEmptyArray(user.tags)
+ default: filteredRequest.default ? filteredRequest.default : (await UserStorage.getTags(req.user.tenantID, {
+ userIDs: [filteredRequest.userID]
+ }, Constants.DB_PARAMS_SINGLE_RECORD)).count === 0
} as Tag;
// Save
await UserStorage.saveTag(req.user.tenantID, newTag);
@@ -1303,7 +1332,7 @@ export default class UserService {
});
}
// Get User
- const user = await UserStorage.getUser(req.user.tenantID, filteredRequest.userID, { withTag: true });
+ const user = await UserStorage.getUser(req.user.tenantID, filteredRequest.userID);
UtilsService.assertObjectExists(action, user, `User ID '${filteredRequest.userID}' does not exist`,
MODULE_NAME, 'handleUpdateTag', req.user);
// Only current organization User can be assigned to Tag
@@ -1342,7 +1371,9 @@ export default class UserService {
tag.description = filteredRequest.description;
tag.active = filteredRequest.active;
tag.userID = filteredRequest.userID;
- tag.default = filteredRequest.default ? filteredRequest.default : Utils.isEmptyArray(user.tags);
+ tag.default = filteredRequest.default ? filteredRequest.default : (await UserStorage.getTags(req.user.tenantID, {
+ userIDs: [filteredRequest.userID]
+ }, Constants.DB_PARAMS_SINGLE_RECORD)).count === 0;
tag.lastChangedBy = { id: req.user.id };
tag.lastChangedOn = new Date();
// Save

MODIFY
src/server/rest/v1/service/security/CarSecurity.ts
src/server/rest/v1/service/security/CarSecurity.ts
@@ -331,10 +331,6 @@ export default class CarSecurity {
return filteredRequest;
}
- public static filterCarRequestByUserID(request: any): string {
- return sanitize(request.UserID);
- }
-
public static filterCarUsersRequest(request: any): HttpUsersCarsRequest {
const filteredRequest: HttpUsersCarsRequest = {
Search: sanitize(request.Search),

MODIFY
src/server/rest/v1/service/security/UserSecurity.ts
src/server/rest/v1/service/security/UserSecurity.ts
import { HttpSitesAssignUserRequest, HttpTagsRequest, HttpUserMobileTokenRequest, HttpUserRequest, HttpUserSitesRequest, HttpUsersRequest } from '../../../../../types/requests/HttpUserRequest';
-import User, { UserRole } from '../../../../../types/User';
+import User, { UserDefaultTagCar, UserRole } from '../../../../../types/User';
import Authorizations from '../../../../../authorization/Authorizations';
+import { Car } from '../../../../../types/Car';
+import CarSecurity from './CarSecurity';
import Constants from '../../../../../utils/Constants';
import { DataResult } from '../../../../../types/DataResult';
import Tag from '../../../../../types/Tag';
@@ -266,13 +268,25 @@ export default class UserSecurity {
return filteredTag;
}
+ public static filterUserDefaultTagResponse(tag: Tag, car: Car, loggedUser: UserToken): UserDefaultTagCar {
+ const filteredUserDefaultTagCar = {} as UserDefaultTagCar;
+ // Check auth
+ if (tag && Authorizations.canReadTag(loggedUser, tag.userID)) {
+ filteredUserDefaultTagCar.tag = this.filterTagResponse(tag, loggedUser);
+ }
+ if (Authorizations.canReadCar(loggedUser)) {
+ filteredUserDefaultTagCar.car = CarSecurity.filterCarResponse(car, loggedUser);
+ }
+ return filteredUserDefaultTagCar;
+ }
+
static filterTagResponse(tag: Tag, loggedUser: UserToken): Tag {
const filteredTag = {} as Tag;
if (!tag) {
return null;
}
// Check auth
- if (Authorizations.canReadTag(loggedUser)) {
+ if (Authorizations.canReadTag(loggedUser, tag.userID)) {
filteredTag.id = tag.id;
filteredTag.issuer = tag.issuer;
filteredTag.description = tag.description;
@@ -335,7 +349,7 @@ export default class UserSecurity {
return sanitize(request.ID);
}
- public static filterTagRequestByUserID(request: any): string {
+ public static filterDefaultTagCarRequestByUserID(request: any): string {
return sanitize(request.UserID);
}

MODIFY
src/storage/mongodb/CarStorage.ts
src/storage/mongodb/CarStorage.ts
@@ -716,20 +716,6 @@ export default class CarStorage {
Logging.traceEnd(MODULE_NAME, 'clearCarUserDefault', uniqueTimerID, { userID });
}
- public static async getUserDefaultCar(tenantID: string, userID: string, projectFields?: string[]): Promise<Car> {
- // Debug
- const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'getUserDefaultCar');
- // Query single Car
- let carsMDB = await CarStorage.getCars(tenantID,
- { userIDs: [userID], defaultCar: true },
- Constants.DB_PARAMS_SINGLE_RECORD, projectFields);
- carsMDB = carsMDB.count > 0 ? carsMDB : await CarStorage.getCars(tenantID,
- { userIDs: [userID] },
- Constants.DB_PARAMS_SINGLE_RECORD, projectFields);
- Logging.traceEnd(MODULE_NAME, 'getUserDefaultCar', uniqueTimerID, { userID });
- return carsMDB.count > 0 ? carsMDB.result[0] : null;
- }
-
public static async clearCarUserOwner(tenantID: string, carID: string): Promise<void> {
const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'clearCarUserOwner');
await Utils.checkTenant(tenantID);

MODIFY
src/storage/mongodb/UserStorage.ts
src/storage/mongodb/UserStorage.ts
@@ -760,27 +760,6 @@ export default class UserStorage {
return tag.count === 1 ? tag.result[0] : null;
}
- public static async getUserDefaultTag(tenantID: string, userID: string): Promise<Tag> {
- // Debug
- const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'getUserDefaultTag');
- // Get tag
- let tag = await UserStorage.getTags(tenantID, {
- userIDs: [userID],
- defaultTag: true,
- active: true
- }, Constants.DB_PARAMS_SINGLE_RECORD);
- if (tag.count < 1) {
- tag = await UserStorage.getTags(tenantID, {
- userIDs: [userID],
- active: true
- }, Constants.DB_PARAMS_SINGLE_RECORD);
- }
- // Debug
- Logging.traceEnd(MODULE_NAME, 'getUserDefaultTag', uniqueTimerID, { userID });
- return tag.count > 0 ? tag.result[0] : null;
- }
-
-
public static async getTags(tenantID: string,
params: {
issuer?: boolean; tagIDs?: string[]; userIDs?: string[]; dateFrom?: Date; dateTo?: Date;

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -65,7 +65,6 @@ export enum ServerAction {
CAR_DELETE = 'CarDelete',
CARS = 'Cars',
CAR = 'Car',
- DEFAULT_CAR = 'DefaultCar',
CAR_USERS = 'CarUsers',
SYNCHRONIZE_CAR_CATALOGS = 'SynchronizeCarCatalogs',
@@ -334,7 +333,7 @@ export enum ServerAction {
USER_IMAGE = 'UserImage',
TAGS = 'Tags',
TAG = 'Tag',
- DEFAUlT_TAG = 'DefaultTag',
+ USER_DEFAUlT_TAG_CAR = 'UserDefaultTagCar',
TAG_CREATE = 'TagCreate',
TAG_UPDATE = 'TagUpdate',
TAG_DELETE = 'TagDelete',

MODIFY
src/types/User.ts
src/types/User.ts
import Address from './Address';
import { BillingUserData } from './Billing';
+import { Car } from './Car';
import CreatedUpdatedProps from './CreatedUpdatedProps';
import Tag from './Tag';
import UserNotifications from './UserNotifications';
@@ -47,6 +48,11 @@ export interface UserSite {
siteOwner: boolean;
}
+export interface UserDefaultTagCar {
+ car?: Car;
+ tag?: Tag;
+}
+
export interface UserCar extends CreatedUpdatedProps {
id?: string;
user: User;

Get user default tag and car in the same endpoint