Keep retro compatibility in Service endpoints
Use Tag API to stop a transaction
MODIFY
src/assets/server/rest/v1/docs/e-mobility-oas.json
src/assets/server/rest/v1/docs/e-mobility-oas.json
"type": "string",
"example": ""
},
+ "userID": {
+ "type": "string",
+ "example": ""
+ },
"args": {
"type": "object",
"properties": {
"tagID": {
"type": "string",
- "description": "Required. The identifier that Charge Point must use to start a transaction.",
+ "description": "The identifier that Charge Point must use to start a transaction.",
"example": "123456"
},
+ "visualTagID": {
+ "type": "string",
+ "description": "The identifier that Charge Point must use to start a transaction.",
+ "example": "123456-123456"
+ },
"connectorId": {
"type": "integer",
"description": "Optional. Number of the connector on which to start the transaction. connectorId SHALL be > 0",
"minimum": 1,
"example": 1
}
- },
- "required": [
- "tagID"
- ]
+ }
}
},
"required": [

MODIFY
src/assets/server/rest/v1/schemas/chargingstation/chargingstation-action.json
src/assets/server/rest/v1/schemas/chargingstation/chargingstation-action.json
"carID": {
"$ref": "common.json#/definitions/id"
},
+ "userID": {
+ "$ref": "common.json#/definitions/id"
+ },
"args": {
"type": "object",
"properties": {
"stackLevel": {
"$ref": "chargingstation.json#/definitions/chargingProfile/properties/profile/properties/stackLevel"
},
- "visualTagID": {
+ "tagID": {
"type": "string",
"sanitize": "mongo"
},
- "userID": {
- "$ref": "common.json#/definitions/id"
+ "visualTagID": {
+ "type": "string",
+ "sanitize": "mongo"
},
"location": {
"type": "string",

MODIFY
src/server/rest/v1/service/ChargingStationService.ts
src/server/rest/v1/service/ChargingStationService.ts
@@ -18,6 +18,7 @@ import Constants from '../../../../utils/Constants';
import CpoOCPIClient from '../../../../client/ocpi/CpoOCPIClient';
import CpoOICPClient from '../../../../client/oicp/CpoOICPClient';
import { DataResult } from '../../../../types/DataResult';
+import { HttpChargingStationCommandRequest } from '../../../../types/requests/HttpChargingStationRequest';
import I18nManager from '../../../../utils/I18nManager';
import LockingHelper from '../../../../locking/LockingHelper';
import LockingManager from '../../../../locking/LockingManager';
@@ -36,10 +37,12 @@ import SiteAreaStorage from '../../../../storage/mongodb/SiteAreaStorage';
import SiteStorage from '../../../../storage/mongodb/SiteStorage';
import SmartChargingFactory from '../../../../integration/smart-charging/SmartChargingFactory';
import { StatusCodes } from 'http-status-codes';
+import Tag from '../../../../types/Tag';
import TagStorage from '../../../../storage/mongodb/TagStorage';
import Tenant from '../../../../types/Tenant';
import TenantComponents from '../../../../types/TenantComponents';
import TransactionStorage from '../../../../storage/mongodb/TransactionStorage';
+import User from '../../../../types/User';
import UserStorage from '../../../../storage/mongodb/UserStorage';
import UserToken from '../../../../types/UserToken';
import Utils from '../../../../utils/Utils';
@@ -1141,164 +1144,24 @@ export default class ChargingStationService {
const filteredRequest = ChargingStationValidator.getInstance().validateChargingStationActionReq(req.body);
UtilsService.assertIdIsProvided(action, filteredRequest.chargingStationID, MODULE_NAME, 'handleAction', req.user);
// Get the Charging station
- const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, filteredRequest.chargingStationID);
- UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${filteredRequest.chargingStationID}' does not exist`,
- MODULE_NAME, 'handleAction', req.user);
- let result = null;
+ const chargingStation = await UtilsService.checkAndGetChargingStationAuthorization(
+ req.tenant, req.user, filteredRequest.chargingStationID, action);
+ let result: any;
+ switch (command) {
// Remote Stop Transaction / Unlock Connector
- if (command === Command.REMOTE_STOP_TRANSACTION) {
- // Check Transaction ID
- if (!filteredRequest.args || !filteredRequest.args.transactionId) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'Transaction ID is mandatory',
- module: MODULE_NAME,
- method: 'handleAction',
- user: req.user,
- action: action,
- });
- }
- // Get Transaction
- const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.args.transactionId);
- UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.args.transactionId as string}' does not exist`,
- MODULE_NAME, 'handleAction', req.user);
- // Add connector ID
- filteredRequest.args.connectorId = transaction.connectorId;
- // Check Tag ID
- if (Utils.isEmptyArray(req.user.tagIDs)) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.USER_NO_BADGE_ERROR,
- message: 'The user does not have any badge',
- module: MODULE_NAME,
- method: 'handleAction',
- user: req.user,
- action: action,
- });
- }
- // Check if user is authorized
- await Authorizations.isAuthorizedToStopTransaction(req.tenant, chargingStation, transaction, req.user.tagIDs[0],
- ServerAction.STOP_TRANSACTION, Action.REMOTE_STOP_TRANSACTION);
- // Set the tag ID to handle the Stop Transaction afterwards
- transaction.remotestop = {
- timestamp: new Date(),
- tagID: req.user.tagIDs[0],
- userID: req.user.id
- };
- // Save Transaction
- await TransactionStorage.saveTransaction(req.user.tenantID, transaction);
- // Ok: Execute it
- result = await ChargingStationService.handleChargingStationCommand(
- req.tenant, req.user, chargingStation, action, command, filteredRequest.args);
+ case Command.REMOTE_STOP_TRANSACTION:
+ result = await ChargingStationService.executeChargingStationStopTransaction(action, chargingStation, command, filteredRequest, req, res, next);
+ break;
// Remote Start Transaction
- } else if (command === Command.REMOTE_START_TRANSACTION) {
- // Check Tag ID
- if (!filteredRequest.args || !filteredRequest.args.userID || !filteredRequest.args.visualTagID) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.USER_NO_BADGE_ERROR,
- message: 'The user does not have any badge',
- module: MODULE_NAME,
- method: 'handleAction',
- user: req.user,
- action: action,
- });
- }
- // Check and Get User
- const user = await UtilsService.checkAndGetUserAuthorization(
- req.tenant, req.user, filteredRequest.args.userID, Action.READ, action);
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- UtilsService.assertObjectExists(action, user, `User ID '${filteredRequest.args.userID}' does not exist`,
- MODULE_NAME, 'handleAction', req.user);
- if (!user.issuer) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.GENERAL_ERROR,
- message: `User not issued by the organization execute command '${Command.REMOTE_START_TRANSACTION}'`,
- module: MODULE_NAME, method: 'handleAction',
- user: req.user,
- action: action
- });
- }
- const tag = await TagStorage.getTagByVisualID(req.tenant.id, filteredRequest.args.visualTagID, { userIDs: [user.id] });
- if (!tag) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.USER_NO_BADGE_ERROR,
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- message: `The user does not own a badge with visual ID '${filteredRequest.args.visualTagID}'`,
- module: MODULE_NAME,
- method: 'handleAction',
- user: req.user,
- action: action,
- });
- }
- // Inactive Tag
- if (!tag.active) {
- throw new BackendError({
- source: chargingStation.id,
- action: action,
- message: `Tag ID '${tag.id}' is not active`,
- module: MODULE_NAME, method: 'handleAction',
- user: tag.user,
- detailedMessages: { tag }
- });
- }
- await Authorizations.isChargingStationValidInOrganization(action, req.tenant, chargingStation);
- // Ok: Execute it
- result = await ChargingStationService.handleChargingStationCommand(
- req.tenant, req.user, chargingStation, action, command, { tagID: tag.id, connectorId: filteredRequest.args.connectorId });
- // Save Car ID
- if (Utils.isComponentActiveFromToken(req.user, TenantComponents.CAR)) {
- if (result?.status === OCPPRemoteStartStopStatus.ACCEPTED) {
- if (filteredRequest.carID && filteredRequest.carID !== user.lastSelectedCarID) {
- // Save Car selection
- await UserStorage.saveUserLastSelectedCarID(req.user.tenantID, user.id, filteredRequest.carID);
- }
- }
- }
- } else if (command === Command.GET_COMPOSITE_SCHEDULE) {
- // Check auth
- if (!await Authorizations.canPerformActionOnChargingStation(req.user, command as unknown as Action, chargingStation)) {
- throw new AppAuthError({
- errorCode: HTTPAuthError.FORBIDDEN,
- user: req.user,
- action: command as unknown as Action,
- entity: Entity.CHARGING_STATION,
- module: MODULE_NAME, method: 'handleAction',
- value: chargingStation.id
- });
- }
- // Get the Vendor instance
- const chargingStationVendor = ChargingStationVendorFactory.getChargingStationVendorImpl(chargingStation);
- if (!chargingStationVendor) {
- throw new AppError({
- source: chargingStation.id,
- action: action,
- errorCode: HTTPError.FEATURE_NOT_SUPPORTED_ERROR,
- message: `No vendor implementation is available (${chargingStation.chargePointVendor}) for limiting the charge`,
- module: MODULE_NAME, method: 'handleAction',
- user: req.user
- });
- }
- // Get composite schedule
- if (filteredRequest.args.connectorId === 0) {
- result = [] as OCPPGetCompositeScheduleCommandResult[];
- for (const connector of chargingStation.connectors) {
- // Connector ID > 0
- const chargePoint = Utils.getChargePointFromID(chargingStation, connector.chargePointID);
- result.push(await chargingStationVendor.getCompositeSchedule(
- req.tenant, chargingStation, chargePoint, connector.connectorId, filteredRequest.args.duration));
- }
- } else {
- // Connector ID > 0
- const connector = Utils.getConnectorFromID(chargingStation, filteredRequest.args.connectorId);
- const chargePoint = Utils.getChargePointFromID(chargingStation, connector?.chargePointID);
- result = await chargingStationVendor.getCompositeSchedule(
- req.tenant, chargingStation, chargePoint, filteredRequest.args.connectorId, filteredRequest.args.duration);
- }
- } else {
+ case Command.REMOTE_START_TRANSACTION:
+ result = await ChargingStationService.executeChargingStationStartTransaction(action, chargingStation, command, filteredRequest, req, res, next);
+ break;
+ // Get the Charging Plans
+ case Command.GET_COMPOSITE_SCHEDULE:
+ result = await ChargingStationService.executeChargingStationGetCompositeSchedule(action, chargingStation, command, filteredRequest, req, res, next);
+ break;
+ // Other commands
+ default:
// Check auth
if (!await Authorizations.canPerformActionOnChargingStation(req.user, command as unknown as Action, chargingStation)) {
throw new AppAuthError({
@@ -1311,10 +1174,10 @@ export default class ChargingStationService {
});
}
// Execute it
- result = await ChargingStationService.handleChargingStationCommand(
+ result = await ChargingStationService.executeChargingStationCommand(
req.tenant, req.user, chargingStation, action, command, filteredRequest.args);
+ break;
}
- // Return
res.json(result);
next();
}
@@ -1591,7 +1454,7 @@ export default class ChargingStationService {
pdfDocument.text(qrCodeTitle, 65, bigSquareSide + mediumSquareSide + smallSquareSide + (marginHeight * 3) + 10, { align: 'center' });
}
- private static async handleChargingStationCommand(tenant: Tenant, user: UserToken, chargingStation: ChargingStation,
+ private static async executeChargingStationCommand(tenant: Tenant, user: UserToken, chargingStation: ChargingStation,
action: ServerAction, command: Command, params: any): Promise<any> {
let result: any;
// Get the OCPP Client
@@ -1826,4 +1689,174 @@ export default class ChargingStationService {
// Apply & Save charging plan
return OCPPUtils.setAndSaveChargingProfile(req.tenant, filteredRequest);
}
+
+ private static async executeChargingStationGetCompositeSchedule(action: ServerAction, chargingStation: ChargingStation, command: Command,
+ filteredRequest: HttpChargingStationCommandRequest, req: Request, res: Response, next: NextFunction): Promise<any> {
+ // Check auth
+ if (!await Authorizations.canPerformActionOnChargingStation(req.user, command as unknown as Action, chargingStation)) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: req.user,
+ action: command as unknown as Action,
+ entity: Entity.CHARGING_STATION,
+ module: MODULE_NAME, method: 'handleAction',
+ value: chargingStation.id
+ });
+ }
+ // Get the Vendor instance
+ const chargingStationVendor = ChargingStationVendorFactory.getChargingStationVendorImpl(chargingStation);
+ if (!chargingStationVendor) {
+ throw new AppError({
+ source: chargingStation.id,
+ action: action,
+ errorCode: HTTPError.FEATURE_NOT_SUPPORTED_ERROR,
+ message: `No vendor implementation is available (${chargingStation.chargePointVendor}) for limiting the charge`,
+ module: MODULE_NAME, method: 'handleAction',
+ user: req.user
+ });
+ }
+ // Get composite schedule
+ let result: any;
+ if (filteredRequest.args.connectorId === 0) {
+ result = [] as OCPPGetCompositeScheduleCommandResult[];
+ for (const connector of chargingStation.connectors) {
+ // Connector ID > 0
+ const chargePoint = Utils.getChargePointFromID(chargingStation, connector.chargePointID);
+ result.push(await chargingStationVendor.getCompositeSchedule(
+ req.tenant, chargingStation, chargePoint, connector.connectorId, filteredRequest.args.duration));
+ }
+ } else {
+ // Connector ID > 0
+ const connector = Utils.getConnectorFromID(chargingStation, filteredRequest.args.connectorId);
+ const chargePoint = Utils.getChargePointFromID(chargingStation, connector?.chargePointID);
+ result = await chargingStationVendor.getCompositeSchedule(
+ req.tenant, chargingStation, chargePoint, filteredRequest.args.connectorId, filteredRequest.args.duration);
+ }
+ return result;
+ }
+
+ private static async executeChargingStationStartTransaction(action: ServerAction, chargingStation: ChargingStation, command: Command,
+ filteredRequest: HttpChargingStationCommandRequest, req: Request, res: Response, next: NextFunction): Promise<any> {
+ // Check Tag ID
+ if (!filteredRequest.args || (!filteredRequest.args.visualTagID && !filteredRequest.args.tagID)) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.USER_NO_BADGE_ERROR,
+ message: 'The user does not have any badge',
+ module: MODULE_NAME,
+ method: 'handleAction',
+ user: req.user,
+ action: action,
+ });
+ }
+ let tag: Tag;
+ if (filteredRequest.args.tagID) {
+ tag = await UtilsService.checkAndGetTagAuthorization(
+ req.tenant, req.user, filteredRequest.args.tagID, Action.READ, ServerAction.CHARGING_STATION_REMOTE_START_TRANSACTION);
+ } else {
+ tag = await UtilsService.checkAndGetTagByVisualIDAuthorization(
+ req.tenant, req.user, filteredRequest.args.visualTagID, Action.READ, ServerAction.CHARGING_STATION_REMOTE_START_TRANSACTION);
+ }
+ // Inactive Tag
+ if (!tag.active) {
+ throw new BackendError({
+ source: chargingStation.id,
+ action: action,
+ message: `Tag ID '${tag.id}' is not active`,
+ module: MODULE_NAME, method: 'handleAction',
+ user: req.user,
+ actionOnUser: tag.user,
+ detailedMessages: { tag }
+ });
+ }
+ // Set the logged user
+ if (!filteredRequest.userID) {
+ filteredRequest.userID = req.user.id;
+ }
+ // Check and Get User
+ let user: User;
+ if (filteredRequest.userID === req.user.id) {
+ user = req.user.user;
+ } else {
+ user = await UtilsService.checkAndGetUserAuthorization(
+ req.tenant, req.user, filteredRequest.userID, Action.READ, action, null, { tagIDs: [tag.id] });
+ }
+ // Check Tag/User
+ if (tag.userID !== user.id) {
+ throw new BackendError({
+ source: chargingStation.id,
+ action: action,
+ message: `Tag ID '${tag.id}' is not linked to User ID '${user.id}'`,
+ module: MODULE_NAME, method: 'handleAction',
+ user: req.user,
+ actionOnUser: tag.user,
+ detailedMessages: { tag }
+ });
+ }
+ // Check Charging Station
+ await Authorizations.isChargingStationValidInOrganization(action, req.tenant, chargingStation);
+ // Execute it
+ const result = await ChargingStationService.executeChargingStationCommand(
+ req.tenant, req.user, chargingStation, action, command, { tagID: tag.id, connectorId: filteredRequest.args.connectorId });
+ // Save Car selection
+ if (Utils.isComponentActiveFromToken(req.user, TenantComponents.CAR)) {
+ if (result?.status === OCPPRemoteStartStopStatus.ACCEPTED) {
+ if (filteredRequest.carID && filteredRequest.carID !== user.lastSelectedCarID) {
+ await UserStorage.saveUserLastSelectedCarID(req.user.tenantID, user.id, filteredRequest.carID);
+ }
+ }
+ }
+ return result;
+ }
+
+ private static async executeChargingStationStopTransaction(action: ServerAction, chargingStation: ChargingStation, command: Command,
+ filteredRequest: HttpChargingStationCommandRequest, req: Request, res: Response, next: NextFunction): Promise<any> {
+ // Check Transaction ID
+ if (!filteredRequest.args || !filteredRequest.args.transactionId) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Transaction ID is mandatory',
+ module: MODULE_NAME,
+ method: 'handleAction',
+ user: req.user,
+ action: action,
+ });
+ }
+ // Get Transaction
+ const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.args.transactionId);
+ UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.args.transactionId as string}' does not exist`,
+ MODULE_NAME, 'handleAction', req.user);
+ // Add connector ID
+ filteredRequest.args.connectorId = transaction.connectorId;
+ // Get default Tag
+ const tags = await TagStorage.getTags(req.tenant.id, { userIDs: [req.user.id], active: true }, Constants.DB_PARAMS_SINGLE_RECORD, ['id']);
+ if (!Utils.isEmptyArray(tags)) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.USER_NO_BADGE_ERROR,
+ message: 'The user does not have any active badge',
+ module: MODULE_NAME,
+ method: 'handleAction',
+ user: req.user,
+ actionOnUser: transaction.userID,
+ action: action,
+ });
+ }
+ const tag = tags.result[0];
+ // Check if user is authorized
+ await Authorizations.isAuthorizedToStopTransaction(req.tenant, chargingStation, transaction, tag.id,
+ ServerAction.STOP_TRANSACTION, Action.REMOTE_STOP_TRANSACTION);
+ // Set the tag ID to handle the Stop Transaction afterwards
+ transaction.remotestop = {
+ timestamp: new Date(),
+ tagID: tag.id,
+ userID: req.user.id
+ };
+ // Save Transaction
+ await TransactionStorage.saveTransaction(req.user.tenantID, transaction);
+ // Ok: Execute it
+ return ChargingStationService.executeChargingStationCommand(
+ req.tenant, req.user, chargingStation, action, command, filteredRequest.args);
+ }
}

MODIFY
src/server/rest/v1/service/TransactionService.ts
src/server/rest/v1/service/TransactionService.ts
@@ -548,7 +548,7 @@ export default class TransactionService {
projectFields = [
...projectFields,
'userID', 'user.id', 'user.name', 'user.firstName', 'user.email',
- 'stop.userID', 'stop.user.id', 'stop.user.name', 'stop.user.firstName', 'stop.user.email', 'stop.tagID', 'tag.visualID'
+ 'stop.userID', 'stop.user.id', 'stop.user.name', 'stop.user.firstName', 'stop.user.email', 'stop.tagID', 'stop.tag.visualID'
];
}
// Get Transaction
@@ -693,7 +693,7 @@ export default class TransactionService {
const transactions = await TransactionService.getTransactions(req, action, { withTag: true }, [
'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'status', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
- 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'tagID', 'site.name', 'siteArea.name', 'company.name', 'tag.visualID'
+ 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'tagID', 'tag.visualID', 'site.name', 'siteArea.name', 'company.name'
]);
res.json(transactions);
next();
@@ -706,7 +706,7 @@ export default class TransactionService {
'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop', 'site.name', 'siteArea.name', 'company.name',
- 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', 'tag.visualID'
+ 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'tag.visualID', 'stop.tagID', 'stop.tag.visualID'
]);
res.json(transactions);
next();

MODIFY
src/server/rest/v1/service/UtilsService.ts
src/server/rest/v1/service/UtilsService.ts
@@ -653,56 +653,14 @@ export default class UtilsService {
public static async checkAndGetTagAuthorization(tenant: Tenant, userToken:UserToken, tagID: string, authAction: Action,
action: ServerAction, entityData?: EntityDataType, additionalFilters: Record<string, any> = {}, applyProjectFields = false, checkIssuer = true): Promise<Tag> {
- // Check mandatory fields
- UtilsService.assertIdIsProvided(action, tagID, MODULE_NAME, 'checkAndGetTagAuthorization', userToken);
- // Get dynamic auth
- const authorizationFilter = await AuthorizationService.checkAndGetTagAuthorizations(
- tenant, userToken, { ID: tagID }, authAction, entityData);
- if (!authorizationFilter.authorized) {
- throw new AppAuthError({
- errorCode: HTTPAuthError.FORBIDDEN,
- user: userToken,
- action: authAction, entity: Entity.TAG,
- module: MODULE_NAME, method: 'checkAndGetTagAuthorization',
- value: tagID
- });
+ return UtilsService.checkAndGetTagByXXXAuthorization(tenant, userToken, tagID, TagStorage.getTag.bind(this),
+ authAction, action, entityData, additionalFilters, applyProjectFields, checkIssuer);
}
- // Get the Tag & check it exists
- const tag = await TagStorage.getTag(userToken.tenantID, tagID,
- {
- ...additionalFilters,
- ...authorizationFilter.filters
- },
- applyProjectFields ? authorizationFilter.projectFields : null
- );
- UtilsService.assertObjectExists(action, tag, `Tag ID '${tagID}' does not exist`,
- MODULE_NAME, 'handleGetTag', userToken);
- // External Tag
- if (checkIssuer && !tag.issuer) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.GENERAL_ERROR,
- message: `Tag ID '${tag.id}' not issued by the organization`,
- module: MODULE_NAME, method: 'checkAndGetTagAuthorization',
- user: userToken,
- action: action,
- detailedMessages: { tag }
- });
- }
- // Add actions
- await AuthorizationService.addTagAuthorizations(tenant, userToken, tag, authorizationFilter);
- // Check
- const authorized = AuthorizationService.canPerformAction(tag, authAction);
- if (!authorized) {
- throw new AppAuthError({
- errorCode: HTTPAuthError.FORBIDDEN,
- user: userToken,
- action: authAction, entity: Entity.TAG,
- module: MODULE_NAME, method: 'checkAndGetTagAuthorization',
- value: tagID
- });
- }
- return tag;
+
+ public static async checkAndGetTagByVisualIDAuthorization(tenant: Tenant, userToken:UserToken, tagID: string, authAction: Action,
+ action: ServerAction, entityData?: EntityDataType, additionalFilters: Record<string, any> = {}, applyProjectFields = false, checkIssuer = true): Promise<Tag> {
+ return UtilsService.checkAndGetTagByXXXAuthorization(tenant, userToken, tagID, TagStorage.getTagByVisualID.bind(this),
+ authAction, action, entityData, additionalFilters, applyProjectFields, checkIssuer);
}
public static sendEmptyDataResult(res: Response, next: NextFunction): void {
@@ -1825,4 +1783,59 @@ export default class UtilsService {
}
return properties;
}
+
+ private static async checkAndGetTagByXXXAuthorization(tenant: Tenant, userToken:UserToken, id: string,
+ getTagByXXX: (tenantID: string, id: string, params: any, projectedFileds: string[]) => Promise<Tag>, authAction: Action,
+ action: ServerAction, entityData?: EntityDataType, additionalFilters: Record<string, any> = {}, applyProjectFields = false, checkIssuer = true): Promise<Tag> {
+ // Check mandatory fields
+ UtilsService.assertIdIsProvided(action, id, MODULE_NAME, 'checkAndGetTagByXXXAuthorization', userToken);
+ // Get dynamic auth
+ const authorizationFilter = await AuthorizationService.checkAndGetTagAuthorizations(
+ tenant, userToken, { ID: id }, authAction, entityData);
+ if (!authorizationFilter.authorized) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: userToken,
+ action: authAction, entity: Entity.TAG,
+ module: MODULE_NAME, method: 'checkAndGetTagByXXXAuthorization',
+ value: id
+ });
+ }
+ // Get the Tag & check it exists
+ const tag = await getTagByXXX(userToken.tenantID, id,
+ {
+ ...additionalFilters,
+ ...authorizationFilter.filters
+ },
+ applyProjectFields ? authorizationFilter.projectFields : null
+ );
+ UtilsService.assertObjectExists(action, tag, `Tag ID '${id}' does not exist`,
+ MODULE_NAME, 'handleGetTag', userToken);
+ // External Tag
+ if (checkIssuer && !tag.issuer) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: `Tag ID '${tag.id}' not issued by the organization`,
+ module: MODULE_NAME, method: 'checkAndGetTagByXXXAuthorization',
+ user: userToken,
+ action: action,
+ detailedMessages: { tag }
+ });
+ }
+ // Add actions
+ await AuthorizationService.addTagAuthorizations(tenant, userToken, tag, authorizationFilter);
+ // Check
+ const authorized = AuthorizationService.canPerformAction(tag, authAction);
+ if (!authorized) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: userToken,
+ action: authAction, entity: Entity.TAG,
+ module: MODULE_NAME, method: 'checkAndGetTagByXXXAuthorization',
+ value: id
+ });
+ }
+ return tag;
+ }
}

MODIFY
src/storage/mongodb/TagStorage.ts
src/storage/mongodb/TagStorage.ts
@@ -261,12 +261,13 @@ export default class TagStorage {
}
public static async getTag(tenantID: string, id: string,
- params: { userIDs?: string[], withUser?: boolean; withNbrTransactions?: boolean } = {}, projectFields?: string[]): Promise<Tag> {
+ params: { userIDs?: string[], withUser?: boolean; withNbrTransactions?: boolean; active?: boolean; } = {}, projectFields?: string[]): Promise<Tag> {
const tagMDB = await TagStorage.getTags(tenantID, {
tagIDs: [id],
withUser: params.withUser,
withNbrTransactions: params.withNbrTransactions,
- userIDs: params.userIDs
+ userIDs: params.userIDs,
+ active: params.active,
}, Constants.DB_PARAMS_SINGLE_RECORD, projectFields);
return tagMDB.count === 1 ? tagMDB.result[0] : null;
}
@@ -306,7 +307,7 @@ export default class TagStorage {
public static async getTags(tenantID: string,
params: {
issuer?: boolean; tagIDs?: string[]; visualIDs?: string[]; userIDs?: string[]; siteIDs?: string[]; dateFrom?: Date; dateTo?: Date;
- withUser?: boolean; withUsersOnly?: boolean; withNbrTransactions?: boolean; search?: string, defaultTag?: boolean, active?: boolean
+ withUser?: boolean; withUsersOnly?: boolean; withNbrTransactions?: boolean; search?: string, defaultTag?: boolean, active?: boolean;
},
dbParams: DbParams, projectFields?: string[]): Promise<DataResult<Tag>> {
const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getTags');
@@ -342,10 +343,11 @@ export default class TagStorage {
// Users
if (!Utils.isEmptyArray(params.userIDs)) {
filters.userID = { $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) };
+ }
+ // Default Tag
if (params.defaultTag) {
filters.default = true;
}
- }
// Sites
if (!Utils.isEmptyArray(params.siteIDs)) {
DatabaseUtils.pushSiteUserLookupInAggregation({

MODIFY
src/storage/mongodb/UserStorage.ts
src/storage/mongodb/UserStorage.ts
@@ -108,11 +108,12 @@ export default class UserStorage {
}
public static async getUser(tenantID: string, id: string = Constants.UNKNOWN_OBJECT_ID,
- params: { withImage?: boolean; siteIDs?: string[]; } = {}, projectFields?: string[]): Promise<User> {
+ params: { withImage?: boolean; siteIDs?: string[]; tagIDs?: string[]; } = {}, projectFields?: string[]): Promise<User> {
const userMDB = await UserStorage.getUsers(tenantID, {
userIDs: [id],
withImage: params.withImage,
siteIDs: params.siteIDs,
+ tagIDs: params.tagIDs
}, Constants.DB_PARAMS_SINGLE_RECORD, projectFields);
return userMDB.count === 1 ? userMDB.result[0] : null;
}
@@ -539,7 +540,7 @@ export default class UserStorage {
notificationsActive?: boolean; siteIDs?: string[]; excludeSiteID?: string; search?: string;
userIDs?: string[]; email?: string; issuer?: boolean; passwordResetHash?: string; roles?: string[];
statuses?: string[]; withImage?: boolean; billingUserID?: string; notSynchronizedBillingData?: boolean;
- withTestBillingData?: boolean; notifications?: any; noLoginSince?: Date; visualTagIDs?: string[];
+ withTestBillingData?: boolean; notifications?: any; noLoginSince?: Date; visualTagIDs?: string[]; tagIDs?: string[];
},
dbParams: DbParams, projectFields?: string[]): Promise<DataResult<User>> {
// Debug
@@ -628,14 +629,21 @@ export default class UserStorage {
$match: filters
});
// Add Tags
- if (!Utils.isEmptyArray(params.visualTagIDs)) {
+ if (!Utils.isEmptyArray(params.visualTagIDs) || !Utils.isEmptyArray(params.tagIDs)) {
DatabaseUtils.pushTagLookupInAggregation({
tenantID, aggregation, localField: '_id', foreignField: 'userID', asField: 'tag'
});
+ if (!Utils.isEmptyArray(params.tagIDs)) {
+ aggregation.push({
+ $match: { 'tag.id': { $in: params.tagIDs } }
+ });
+ }
+ if (!Utils.isEmptyArray(params.visualTagIDs)) {
aggregation.push({
$match: { 'tag.visualID': { $in: params.visualTagIDs } }
});
}
+ }
// Add Site
if (params.siteIDs || params.excludeSiteID) {
DatabaseUtils.pushSiteUserLookupInAggregation({

MODIFY
src/types/requests/HttpChargingStationRequest.ts
src/types/requests/HttpChargingStationRequest.ts
@@ -107,6 +107,7 @@ export interface HttpChargingStationSetMaxIntensitySocketRequest extends HttpCha
export interface HttpChargingStationCommandRequest {
chargingStationID: string;
carID?: string;
+ userID?: string;
args?: any;
}

Keep retro compatibility in Service endpoints
Use Tag API to stop a transaction