Add lock in service -> Cannot import CSV if import is on going!
MODIFY
src/locking/LockingHelper.ts
src/locking/LockingHelper.ts
@@ -31,6 +31,30 @@ export default class LockingHelper {
return lock;
}
+ public static async createImportUsersLock(tenantID: string): Promise<Lock | null> {
+ const lock = LockingManager.createExclusiveLock(tenantID, LockEntity.USER, 'import-users');
+ if (!(await LockingManager.acquire(lock))) {
+ return null;
+ }
+ return lock;
+ }
+
+ public static async createImportTagsLock(tenantID: string): Promise<Lock | null> {
+ const lock = LockingManager.createExclusiveLock(tenantID, LockEntity.TAG, 'import-tags');
+ if (!(await LockingManager.acquire(lock))) {
+ return null;
+ }
+ return lock;
+ }
+
+ public static async createSyncCarCatalogsLock(tenantID: string): Promise<Lock | null> {
+ const lock = LockingManager.createExclusiveLock(tenantID, LockEntity.CAR_CATALOG, 'synchronize-car-catalogs');
+ if (!(await LockingManager.acquire(lock))) {
+ return null;
+ }
+ return lock;
+ }
+
public static async createBillingSyncInvoicesLock(tenantID: string): Promise<Lock | null> {
const lock = LockingManager.createExclusiveLock(tenantID, LockEntity.INVOICE, 'synchronize-billing-invoices');
if (!(await LockingManager.acquire(lock))) {

MODIFY
src/scheduler/SchedulerManager.ts
src/scheduler/SchedulerManager.ts
@@ -7,6 +7,8 @@ import CheckSessionNotStartedAfterAuthorizeTask from './tasks/CheckSessionNotSta
import CheckUserAccountInactivityTask from './tasks/CheckUserAccountInactivityTask';
import Configuration from '../utils/Configuration';
import Constants from '../utils/Constants';
+import ImportTagsTask from './tasks/ImportTagsTask';
+import ImportUsersTask from './tasks/ImportUsersTask';
import Logging from '../utils/Logging';
import LoggingDatabaseTableCleanupTask from './tasks/LoggingDatabaseTableCleanupTask';
import MigrateSensitiveDataTask from './tasks/MigrateSensitiveDataTask';
@@ -27,8 +29,6 @@ import SynchronizeBillingInvoicesTask from './tasks/SynchronizeBillingInvoicesTa
import SynchronizeBillingUsersTask from './tasks/SynchronizeBillingUsersTask';
import SynchronizeCarsTask from './tasks/SynchronizeCarsTask';
import SynchronizeRefundTransactionsTask from './tasks/SynchronizeRefundTransactionsTask';
-import SynchronizeTagsImportTask from './tasks/SynchronizeTagsImportTask';
-import SynchronizeUsersImportTask from './tasks/SynchronizeUsersImportTask';
import Utils from '../utils/Utils';
import cron from 'node-cron';
@@ -37,11 +37,11 @@ const MODULE_NAME = 'SchedulerManager';
export default class SchedulerManager {
private static schedulerConfig = Configuration.getSchedulerConfig();
- public static init(): void {
+ public static async init(): Promise<void> {
// Active?
if (SchedulerManager.schedulerConfig.active) {
// Log
- Logging.logInfo({
+ await Logging.logInfo({
tenantID: Constants.DEFAULT_TENANT,
action: ServerAction.SCHEDULER,
module: MODULE_NAME, method: 'init',
@@ -51,7 +51,7 @@ export default class SchedulerManager {
for (const task of SchedulerManager.schedulerConfig.tasks) {
// Active?
if (!task.active) {
- Logging.logWarning({
+ await Logging.logWarning({
tenantID: Constants.DEFAULT_TENANT,
action: ServerAction.SCHEDULER,
module: MODULE_NAME, method: 'init',
@@ -133,17 +133,17 @@ export default class SchedulerManager {
case 'CheckChargingStationTemplateTask':
schedulerTask = new CheckChargingStationTemplateTask();
break;
- case 'SynchronizeUsersImportTask':
- schedulerTask = new SynchronizeUsersImportTask();
+ case 'ImportUsersTask':
+ schedulerTask = new ImportUsersTask();
break;
- case 'SynchronizeTagsImportTask':
- schedulerTask = new SynchronizeTagsImportTask();
+ case 'ImportTagsTask':
+ schedulerTask = new ImportTagsTask();
break;
case 'MigrateSensitiveDataTask':
schedulerTask = new MigrateSensitiveDataTask();
break;
default:
- Logging.logError({
+ await Logging.logError({
tenantID: Constants.DEFAULT_TENANT,
action: ServerAction.SCHEDULER,
module: MODULE_NAME, method: 'init',
@@ -160,7 +160,7 @@ export default class SchedulerManager {
for (let i = 0; i < numberOfInstance; i++) {
cron.schedule(task.periodicity, async (): Promise<void> => await schedulerTask.run(task.name, task.config));
}
- Logging.logInfo({
+ await Logging.logInfo({
tenantID: Constants.DEFAULT_TENANT,
action: ServerAction.SCHEDULER,
module: MODULE_NAME, method: 'init',
@@ -170,7 +170,7 @@ export default class SchedulerManager {
}
} else {
// Log
- Logging.logWarning({
+ await Logging.logWarning({
tenantID: Constants.DEFAULT_TENANT,
action: ServerAction.SCHEDULER,
module: MODULE_NAME, method: 'init',

RENAME
src/scheduler/tasks/SynchronizeTagsImportTask.ts
src/scheduler/tasks/ImportTagsTask.ts
@@ -4,7 +4,7 @@ import Constants from '../../utils/Constants';
import { DataResult } from '../../types/DataResult';
import DbParams from '../../types/database/DbParams';
import { ImportStatus } from '../../types/GlobalType';
-import { LockEntity } from '../../types/Locking';
+import LockingHelper from '../../locking/LockingHelper';
import LockingManager from '../../locking/LockingManager';
import Logging from '../../utils/Logging';
import SchedulerTask from '../SchedulerTask';
@@ -16,10 +16,10 @@ import Utils from '../../utils/Utils';
const MODULE_NAME = 'SynchronizeUsersImportTask';
-export default class SynchronizeTagsImportTask extends SchedulerTask {
+export default class ImportTagsTask extends SchedulerTask {
async processTenant(tenant: Tenant, config: TaskConfig): Promise<void> {
- const synchronizeTagsImport = LockingManager.createExclusiveLock(tenant.id, LockEntity.TAGS, 'synchronize-tags-import');
- if (await LockingManager.acquire(synchronizeTagsImport)) {
+ const importTagsLock = await LockingHelper.createImportTagsLock(tenant.id);
+ if (importTagsLock) {
try {
const dbParams: DbParams = { limit: Constants.IMPORT_PAGE_SIZE, skip: 0 };
let importedTags: DataResult<ImportedTag>;
@@ -87,7 +87,7 @@ export default class SynchronizeTagsImportTask extends SchedulerTask {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.SYNCHRONIZE_TAGS, error);
} finally {
// Release the lock
- await LockingManager.release(synchronizeTagsImport);
+ await LockingManager.release(importTagsLock);
}
}
}

RENAME
src/scheduler/tasks/SynchronizeUsersImportTask.ts
src/scheduler/tasks/ImportUsersTask.ts
@@ -4,7 +4,7 @@ import Constants from '../../utils/Constants';
import { DataResult } from '../../types/DataResult';
import DbParams from '../../types/database/DbParams';
import { ImportStatus } from '../../types/GlobalType';
-import { LockEntity } from '../../types/Locking';
+import LockingHelper from '../../locking/LockingHelper';
import LockingManager from '../../locking/LockingManager';
import Logging from '../../utils/Logging';
import SchedulerTask from '../SchedulerTask';
@@ -16,10 +16,10 @@ import Utils from '../../utils/Utils';
const MODULE_NAME = 'SynchronizeUsersImportTask';
-export default class SynchronizeUsersImportTask extends SchedulerTask {
+export default class ImportUsersTask extends SchedulerTask {
async processTenant(tenant: Tenant, config: TaskConfig): Promise<void> {
- const synchronizeUsersImport = LockingManager.createExclusiveLock(tenant.id, LockEntity.USER, 'synchronize-users-import');
- if (await LockingManager.acquire(synchronizeUsersImport)) {
+ const importUsersLock = await LockingHelper.createImportUsersLock(tenant.id);
+ if (importUsersLock) {
try {
const dbParams: DbParams = { limit: Constants.IMPORT_PAGE_SIZE, skip: 0 };
let importedUsers: DataResult<ImportedUser>;
@@ -96,7 +96,7 @@ export default class SynchronizeUsersImportTask extends SchedulerTask {
await Logging.logActionExceptionMessage(tenant.id, ServerAction.SYNCHRONIZE_USERS, error);
} finally {
// Release the lock
- await LockingManager.release(synchronizeUsersImport);
+ await LockingManager.release(importUsersLock);
}
}
}

MODIFY
src/scheduler/tasks/SynchronizeCarsTask.ts
src/scheduler/tasks/SynchronizeCarsTask.ts
import CarFactory from '../../integration/car/CarFactory';
import Constants from '../../utils/Constants';
-import { LockEntity } from '../../types/Locking';
+import LockingHelper from '../../locking/LockingHelper';
import LockingManager from '../../locking/LockingManager';
import Logging from '../../utils/Logging';
import NotificationHandler from '../../notification/NotificationHandler';
@@ -12,8 +12,8 @@ import Utils from '../../utils/Utils';
export default class SynchronizeCarsTask extends SchedulerTask {
async run(name: string, config: TaskConfig): Promise<void> {
// Get the lock
- const carLock = LockingManager.createExclusiveLock(Constants.DEFAULT_TENANT, LockEntity.CAR, 'synchronize-cars');
- if (await LockingManager.acquire(carLock)) {
+ const syncCarCatalogLock = await LockingHelper.createSyncCarCatalogsLock(Constants.DEFAULT_TENANT);
+ if (syncCarCatalogLock) {
try {
const carDatabaseImpl = await CarFactory.getCarImpl();
if (carDatabaseImpl) {
@@ -27,10 +27,10 @@ export default class SynchronizeCarsTask extends SchedulerTask {
}
} catch (error) {
// Log error
- Logging.logActionExceptionMessage(Constants.DEFAULT_TENANT, ServerAction.SYNCHRONIZE_CAR_CATALOGS, error);
+ await Logging.logActionExceptionMessage(Constants.DEFAULT_TENANT, ServerAction.SYNCHRONIZE_CAR_CATALOGS, error);
} finally {
// Release the lock
- await LockingManager.release(carLock);
+ await LockingManager.release(syncCarCatalogLock);
}
}
}

MODIFY
src/server/rest/v1/service/CarService.ts
src/server/rest/v1/service/CarService.ts
@@ -11,6 +11,8 @@ import CarFactory from '../../../../integration/car/CarFactory';
import CarSecurity from './security/CarSecurity';
import CarStorage from '../../../../storage/mongodb/CarStorage';
import Constants from '../../../../utils/Constants';
+import LockingHelper from '../../../../locking/LockingHelper';
+import LockingManager from '../../../../locking/LockingManager';
import Logging from '../../../../utils/Logging';
import { ServerAction } from '../../../../types/Server';
import TenantComponents from '../../../../types/TenantComponents';
@@ -163,15 +165,33 @@ export default class CarService {
}
const carDatabaseImpl = await CarFactory.getCarImpl();
if (!carDatabaseImpl) {
- throw new BackendError({
+ throw new AppError({
source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.MISSING_SETTINGS,
message: 'Car service is not configured',
module: MODULE_NAME, method: 'handleSynchronizeCarCatalogs'
});
}
+ // Get the lock
+ const carLock = await LockingHelper.createSyncCarCatalogsLock(Constants.DEFAULT_TENANT);
+ if (!carLock) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ action: action,
+ errorCode: HTTPError.CANNOT_ACQUIRE_LOCK,
+ module: MODULE_NAME, method: 'handleSynchronizeCarCatalogs',
+ message: 'Error in synchronizing the Car Catalogs: cannot acquire the lock',
+ user: req.user
+ });
+ }
+ try {
const result = await carDatabaseImpl.synchronizeCarCatalogs();
res.json({ ...result, ...Constants.REST_RESPONSE_SUCCESS });
next();
+ } finally {
+ // Release the lock
+ await LockingManager.release(carLock);
+ }
}
public static async handleGetCarMakers(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {

MODIFY
src/server/rest/v1/service/TagService.ts
src/server/rest/v1/service/TagService.ts
@@ -13,6 +13,8 @@ import Busboy from 'busboy';
import Constants from '../../../../utils/Constants';
import EmspOCPIClient from '../../../../client/ocpi/EmspOCPIClient';
import JSONStream from 'JSONStream';
+import LockingHelper from '../../../../locking/LockingHelper';
+import LockingManager from '../../../../locking/LockingManager';
import Logging from '../../../../utils/Logging';
import OCPIClientFactory from '../../../../client/ocpi/OCPIClientFactory';
import { OCPIRole } from '../../../../types/ocpi/OCPIRole';
@@ -473,6 +475,18 @@ export default class TagService {
module: MODULE_NAME, method: 'handleImportTags'
});
}
+ // Acquire the lock
+ const importTagsLock = await LockingHelper.createImportTagsLock(req.tenant.id);
+ if (!importTagsLock) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ action: action,
+ errorCode: HTTPError.CANNOT_ACQUIRE_LOCK,
+ module: MODULE_NAME, method: 'handleImportTags',
+ message: 'Error in importing the Tags: cannot acquire the lock',
+ user: req.user
+ });
+ }
// Default values for Tag import
const importedBy = req.user.id;
const importedOn = new Date();
@@ -607,6 +621,9 @@ export default class TagService {
if (tagsToBeImported.length > 0) {
await TagService.insertTags(req.user.tenantID, req.user, action, tagsToBeImported, result);
}
+ // Release the lock
+ await LockingManager.release(importTagsLock);
+ // Respond
res.json({ ...result, ...Constants.REST_RESPONSE_SUCCESS });
next();
});

MODIFY
src/server/rest/v1/service/UserService.ts
src/server/rest/v1/service/UserService.ts
@@ -21,6 +21,8 @@ import { DataResult } from '../../../../types/DataResult';
import EmspOCPIClient from '../../../../client/ocpi/EmspOCPIClient';
import I18nManager from '../../../../utils/I18nManager';
import JSONStream from 'JSONStream';
+import LockingHelper from '../../../../locking/LockingHelper';
+import LockingManager from '../../../../locking/LockingManager';
import Logging from '../../../../utils/Logging';
import NotificationHandler from '../../../../notification/NotificationHandler';
import OCPIClientFactory from '../../../../client/ocpi/OCPIClientFactory';
@@ -872,6 +874,18 @@ export default class UserService {
module: MODULE_NAME, method: 'handleImportUser'
});
}
+ // Acquire the lock
+ const importUsersLock = await LockingHelper.createImportUsersLock(req.tenant.id);
+ if (!importUsersLock) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ action: action,
+ errorCode: HTTPError.CANNOT_ACQUIRE_LOCK,
+ module: MODULE_NAME, method: 'handleImportUsers',
+ message: 'Error in importing the Users: cannot acquire the lock',
+ user: req.user
+ });
+ }
// Default values for User import
const importedBy = req.user.id;
const importedOn = new Date();
@@ -1005,6 +1019,8 @@ export default class UserService {
if (usersToBeImported.length > 0) {
await UserService.insertUsers(req.user.tenantID, req.user, action, usersToBeImported, result);
}
+ // Release the lock
+ await LockingManager.release(importUsersLock);
res.json({ ...result, ...Constants.REST_RESPONSE_SUCCESS });
next();
});

MODIFY
src/start.ts
src/start.ts
@@ -129,7 +129,7 @@ export default class Bootstrap {
// -------------------------------------------------------------------------
// Init the Scheduler
// -------------------------------------------------------------------------
- SchedulerManager.init();
+ await SchedulerManager.init();
// Locks remain in storage if server crashes
// Delete acquired database locks with same hostname
await LockingManager.cleanupLocks(Configuration.isCloudFoundry() || Utils.isDevelopmentEnv());

MODIFY
src/types/HTTPError.ts
src/types/HTTPError.ts
@@ -3,6 +3,7 @@ import { StatusCodes } from 'http-status-codes';
export enum HTTPError {
GENERAL_ERROR = StatusCodes.INTERNAL_SERVER_ERROR,
NOT_IMPLEMENTED_ERROR = StatusCodes.NOT_IMPLEMENTED,
+ MISSING_SETTINGS = 505,
OBJECT_DOES_NOT_EXIST_ERROR = 550,
FEATURE_NOT_SUPPORTED_ERROR = 585,

MODIFY
src/types/Locking.ts
src/types/Locking.ts
@@ -23,9 +23,10 @@ export enum LockEntity {
PERFORMANCE = 'performance',
TRANSACTION = 'transaction',
CAR = 'car',
+ CAR_CATALOG = 'car-catalog',
INVOICE = 'invoice',
ASSET = 'asset',
OCPI_ENDPOINT = 'ocpi-endpoint',
- TAGS = 'tags',
+ TAG = 'tag',
OICP_ENDPOINT = 'oicp-endpoint',
}

Add lock in service -> Cannot import CSV if import is on going!