Add notification of collection tags + update user's hash when tag is updated
MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIMapping.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIMapping.ts
@@ -181,10 +181,7 @@ export default class OCPIMapping {
// Result
const tokens: OCPIToken[] = [];
// Get all tokens
- const tags = await UserStorage.getTags(tenant.id, { issuer: true, dateFrom, dateTo }, {
- limit,
- skip
- });
+ const tags = await UserStorage.getTags(tenant.id, { issuer: true, dateFrom, dateTo }, { limit, skip });
// Convert Sites to Locations
for (const tag of tags.result) {
const user = await UserStorage.getUser(tenant.id, tag.userID);

MODIFY
src/server/rest/CentralRestServer.ts
src/server/rest/CentralRestServer.ts
@@ -195,6 +195,26 @@ export default class CentralRestServer {
});
}
+ public notifyTag(tenantID: string, action: Action, data: NotificationData): void {
+ // On Tag change rebuild userHashID
+ if (data && data.id) {
+ SessionHashService.rebuildUserHashIDFromTagID(tenantID, data.id).catch(() => { });
+ }
+ // Add in buffer
+ this.addSingleChangeNotificationInBuffer({
+ 'tenantID': tenantID,
+ 'entity': Entity.TAG,
+ 'action': action,
+ 'data': data
+ });
+ // Add in buffer
+ this.addChangeNotificationInBuffer({
+ 'tenantID': tenantID,
+ 'entity': Entity.TAGS,
+ 'action': action
+ });
+ }
+
public notifyTenant(tenantID: string, action: Action, data: NotificationData): void {
// On Tenant change rebuild tenantHashID
if (data && data.id) {

MODIFY
src/server/rest/service/SessionHashService.ts
src/server/rest/service/SessionHashService.ts
@@ -74,7 +74,7 @@ export default class SessionHashService {
// Rebuild and store User Hash ID
static async rebuildUserHashID(tenantID: string, userID: string): Promise<void> {
// Build User hash
- const user = await UserStorage.getUser(tenantID, userID);
+ const user = await UserStorage.getUser(tenantID, userID, { withTag: true });
if (user) {
global.userHashMapIDs.set(`${tenantID}#${userID}`, SessionHashService.buildUserHashID(user));
} else {
@@ -82,6 +82,14 @@ export default class SessionHashService {
}
}
+ static async rebuildUserHashIDFromTagID(tenantID: string, tagID: string): Promise<void> {
+ // Build User hash
+ const tag = await UserStorage.getTag(tenantID, tagID);
+ if (tag?.userID) {
+ await this.rebuildUserHashID(tenantID, tag.userID);
+ }
+ }
+
// Rebuild and store Tenant Hash ID
static async rebuildTenantHashID(tenantID: string): Promise<void> {
// Build Tenant hash

MODIFY
src/server/rest/service/UserService.ts
src/server/rest/service/UserService.ts
@@ -16,6 +16,7 @@ import NotificationHandler from '../../../notification/NotificationHandler';
import OCPIClientFactory from '../../../client/ocpi/OCPIClientFactory';
import { OCPIRole } from '../../../types/ocpi/OCPIRole';
import { ServerAction } from '../../../types/Server';
+import SessionHashService from './SessionHashService';
import SettingStorage from '../../../storage/mongodb/SettingStorage';
import SiteStorage from '../../../storage/mongodb/SiteStorage';
import Tag from '../../../types/Tag';
@@ -1026,7 +1027,8 @@ export default class UserService {
const tags = await UserStorage.getTags(req.user.tenantID,
{
search: filteredRequest.Search,
- userIDs: filteredRequest.UserID ? filteredRequest.UserID.split('|') : null
+ userIDs: filteredRequest.UserID ? filteredRequest.UserID.split('|') : null,
+ withUser: true,
},
{ limit: filteredRequest.Limit, skip: filteredRequest.Skip, sort: filteredRequest.Sort, onlyRecordCount: filteredRequest.OnlyRecordCount },
[ 'id', 'userID', 'active', 'ocpiToken', 'description', 'issuer', 'user.id', 'user.name', 'user.firstName', 'user.email',
@@ -1200,21 +1202,12 @@ export default class UserService {
await Utils.checkIfUserTagIsValid(filteredRequest, req);
// Get Tag
const tag = await UserStorage.getTag(req.user.tenantID, filteredRequest.id);
- UtilsService.assertObjectExists(action, tag, `Tag '${filteredRequest.id}' does not exist`,
+ UtilsService.assertObjectExists(action, tag, `Tag ID '${filteredRequest.id}' does not exist`,
+ MODULE_NAME, 'handleUpdateTag', req.user);
+ // Get User
+ const user = await UserStorage.getUser(req.user.tenantID, filteredRequest.userID);
+ UtilsService.assertObjectExists(action, tag, `User ID '${filteredRequest.userID}' does not exist`,
MODULE_NAME, 'handleUpdateTag', req.user);
- // Check User
- const user = await UserStorage.getUser(req.user.tenantID, tag.userID);
- if (user && (user.id !== filteredRequest.userID)) {
- // Tag already used!
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.USER_TAG_ID_ALREADY_USED_ERROR,
- message: `The Tag ID '${filteredRequest.id}' is already used by User '${Utils.buildUserFullName(user)}'`,
- module: MODULE_NAME,
- method: 'handleUpdateTag',
- user: req.user.id
- });
- }
// Only current organizations tags can be updated
if (!tag.issuer) {
throw new AppError({
@@ -1226,6 +1219,11 @@ export default class UserService {
action: action
});
}
+ // Check User reassignment
+ if (tag.userID !== filteredRequest.userID) {
+ // Recompute the former User's Hash (trigger unlog)
+ await SessionHashService.rebuildUserHashID(req.user.tenantID, tag.userID);
+ }
// Update
tag.description = filteredRequest.description;
tag.active = filteredRequest.active;

MODIFY
src/storage/mongodb/MongoDBStorageNotification.ts
src/storage/mongodb/MongoDBStorageNotification.ts
@@ -118,6 +118,9 @@ export default class MongoDBStorageNotification {
private handleCollectionChange(tenantID: string, collection: string, documentID: string, action: Action, changeEvent) {
switch (collection) {
+ case 'tags':
+ this.centralRestServer.notifyTag(tenantID, action, { id: documentID });
+ break;
case 'users':
case 'userimages':
this.centralRestServer.notifyUser(tenantID, action, { id: documentID });

MODIFY
src/storage/mongodb/UserStorage.ts
src/storage/mongodb/UserStorage.ts
@@ -125,11 +125,16 @@ export default class UserStorage {
return user.count === 1 ? user.result[0] : null;
}
- public static async getUser(tenantID: string, id: string = Constants.UNKNOWN_OBJECT_ID): Promise<User> {
+ public static async getUser(tenantID: string, id: string = Constants.UNKNOWN_OBJECT_ID,
+ params: { withTag?: boolean } = {}): Promise<User> {
// Debug
const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'getUser');
// Get user
- const user = await UserStorage.getUsers(tenantID, { userIDs: [id] }, Constants.DB_PARAMS_SINGLE_RECORD);
+ const user = await UserStorage.getUsers(tenantID,
+ {
+ userIDs: [id],
+ withTag: params.withTag
+ }, Constants.DB_PARAMS_SINGLE_RECORD);
// Debug
Logging.traceEnd(MODULE_NAME, 'getUser', uniqueTimerID, { id });
return user.count === 1 ? user.result[0] : null;
@@ -712,18 +717,23 @@ export default class UserStorage {
};
}
- public static async getTag(tenantID: string, id: string): Promise<Tag> {
+ public static async getTag(tenantID: string, id: string,
+ params: { withUser?: boolean } = {}): Promise<Tag> {
// Debug
const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'getTag');
// Get tag
- const tag = await UserStorage.getTags(tenantID, { tagIDs: [id] }, Constants.DB_PARAMS_SINGLE_RECORD);
+ const tag = await UserStorage.getTags(tenantID, {
+ tagIDs: [id],
+ withUser: params.withUser,
+ }, Constants.DB_PARAMS_SINGLE_RECORD);
// Debug
Logging.traceEnd(MODULE_NAME, 'getTag', uniqueTimerID, { id });
return tag.count === 1 ? tag.result[0] : null;
}
public static async getTags(tenantID: string,
- params: { issuer?: boolean; tagIDs?: string[]; userIDs?: string[]; dateFrom?: Date; dateTo?: Date, search?: string },
+ params: { issuer?: boolean; tagIDs?: string[]; userIDs?: string[]; dateFrom?: Date; dateTo?: Date,
+ withUser?: boolean, search?: string },
dbParams: DbParams, projectFields?: string[]): Promise<DataResult<Tag>> {
const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'getTags');
// Check Tenant
@@ -803,10 +813,12 @@ export default class UserStorage {
objectIDFields: ['createdBy', 'lastChangedBy']
});
// Users
+ if (params.withUser) {
DatabaseUtils.pushUserLookupInAggregation({
tenantID, aggregation: aggregation, asField: 'user', localField: 'userID',
foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false
});
+ }
// Handle the ID
DatabaseUtils.pushRenameDatabaseID(aggregation);
// Convert Object ID to string

Add notification of collection tags + update user's hash when tag is updated