Enhance the Tag Import Service
MODIFY
src/client/oicp/CpoOICPClient.ts
src/client/oicp/CpoOICPClient.ts
@@ -326,7 +326,7 @@ export default class CpoOICPClient extends OICPClient {
// Save
const executionDurationSecs = Utils.createDecimal(new Date().getTime()).minus(startTime).div(1000).toNumber();
await OICPEndpointStorage.saveOicpEndpoint(this.tenant.id, this.oicpEndpoint);
- Logging.logOicpResult(this.tenant.id, ServerAction.OICP_PUSH_EVSE_DATA,
+ await Logging.logOicpResult(this.tenant.id, ServerAction.OICP_PUSH_EVSE_DATA,
MODULE_NAME, 'sendEVSEs', result,
`{{inSuccess}} EVSE(s) were successfully patched in ${executionDurationSecs}s`,
`{{inError}} EVSE(s) failed to be patched in ${executionDurationSecs}s`,
@@ -445,7 +445,7 @@ export default class CpoOICPClient extends OICPClient {
// Save
const executionDurationSecs = (new Date().getTime() - startTime) / 1000;
await OICPEndpointStorage.saveOicpEndpoint(this.tenant.id, this.oicpEndpoint);
- Logging.logOicpResult(this.tenant.id, ServerAction.OICP_PUSH_EVSE_STATUSES,
+ await Logging.logOicpResult(this.tenant.id, ServerAction.OICP_PUSH_EVSE_STATUSES,
MODULE_NAME, 'sendEVSEStatuses', result,
`{{inSuccess}} EVSE Status(es) were successfully patched in ${executionDurationSecs}s`,
`{{inError}} EVSE Status(es) failed to be patched in ${executionDurationSecs}s`,

MODIFY
src/integration/car/CarIntegration.ts
src/integration/car/CarIntegration.ts
@@ -35,7 +35,7 @@ export default abstract class CarIntegration {
externalCar.id = await CarStorage.saveCarCatalog(externalCar, true);
actionsDone.inSuccess++;
// Log
- Logging.logDebug({
+ await Logging.logDebug({
tenantID: Constants.DEFAULT_TENANT,
source: Constants.CENTRAL_SERVER,
action: ServerAction.SYNCHRONIZE_CAR_CATALOGS,
@@ -66,7 +66,7 @@ export default abstract class CarIntegration {
}
actionsDone.inSuccess++;
// Log
- Logging.logDebug({
+ await Logging.logDebug({
tenantID: Constants.DEFAULT_TENANT,
source: Constants.CENTRAL_SERVER,
action: ServerAction.SYNCHRONIZE_CAR_CATALOGS,
@@ -76,7 +76,7 @@ export default abstract class CarIntegration {
}
} catch (error) {
actionsDone.inError++;
- Logging.logError({
+ await Logging.logError({
tenantID: Constants.DEFAULT_TENANT,
source: Constants.CENTRAL_SERVER,
action: ServerAction.SYNCHRONIZE_CAR_CATALOGS,
@@ -87,7 +87,7 @@ export default abstract class CarIntegration {
}
}
// Log
- Logging.logActionsResponse(Constants.DEFAULT_TENANT, ServerAction.SYNCHRONIZE_CAR_CATALOGS,
+ await Logging.logActionsResponse(Constants.DEFAULT_TENANT, ServerAction.SYNCHRONIZE_CAR_CATALOGS,
MODULE_NAME, 'synchronizeCarCatalogs', actionsDone,
'{{inSuccess}} car(s) were successfully synchronized',
'{{inError}} car(s) failed to be synchronized',

MODIFY
src/integration/smart-charging/SmartChargingIntegration.ts
src/integration/smart-charging/SmartChargingIntegration.ts
@@ -31,7 +31,7 @@ export default abstract class SmartChargingIntegration<T extends SmartChargingSe
// Call the charging plans
const chargingProfiles: ChargingProfile[] = await this.buildChargingProfiles(siteArea, this.excludedChargingStations);
if (!chargingProfiles) {
- Logging.logInfo({
+ await Logging.logInfo({
tenantID: this.tenantID,
action: ServerAction.CHARGING_PROFILE_UPDATE,
module: MODULE_NAME, method: 'computeAndApplyChargingProfiles',
@@ -53,7 +53,7 @@ export default abstract class SmartChargingIntegration<T extends SmartChargingSe
}
actionsResponse.inError++;
// Log failed
- Logging.logError({
+ await Logging.logError({
tenantID: this.tenantID,
source: chargingProfile.chargingStationID,
action: ServerAction.CHARGING_PROFILE_UPDATE,
@@ -64,7 +64,7 @@ export default abstract class SmartChargingIntegration<T extends SmartChargingSe
}
}
// Log
- Logging.logActionsResponse(this.tenantID, ServerAction.CHECK_AND_APPLY_SMART_CHARGING,
+ await Logging.logActionsResponse(this.tenantID, ServerAction.CHECK_AND_APPLY_SMART_CHARGING,
MODULE_NAME, 'computeAndApplyChargingProfiles', actionsResponse,
'{{inSuccess}} charging plan(s) were successfully pushed',
'{{inError}} charging plan(s) failed to be pushed',
@@ -122,7 +122,7 @@ export default abstract class SmartChargingIntegration<T extends SmartChargingSe
return true;
} catch (error) {
// Log failed
- Logging.logError({
+ await Logging.logError({
tenantID: this.tenantID,
source: chargingProfile.chargingStationID,
action: ServerAction.CHARGING_PROFILE_UPDATE,

MODIFY
src/scheduler/tasks/CheckChargingStationTemplateTask.ts
src/scheduler/tasks/CheckChargingStationTemplateTask.ts
@@ -34,7 +34,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask {
await this.applyTemplateToChargingStations(tenant);
} catch (error) {
// Log error
- Logging.logActionExceptionMessage(tenant.id, ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE, error);
+ await Logging.logActionExceptionMessage(tenant.id, ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE, error);
} finally {
// Release the lock
await LockingManager.release(offlineChargingStationLock);
@@ -46,7 +46,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask {
let updated = 0;
// Bypass perf tenant
if (tenant.subdomain === 'testperf') {
- Logging.logWarning({
+ await Logging.logWarning({
tenantID: tenant.id,
action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
module: MODULE_NAME, method: 'applyTemplateToChargingStations',
@@ -114,7 +114,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask {
if (chargingStationTemplateUpdated.ocppStandardUpdated || chargingStationTemplateUpdated.ocppVendorUpdated) {
sectionsUpdated.push('OCPP');
}
- Logging.logInfo({
+ await Logging.logInfo({
tenantID: tenant.id,
source: chargingStation.id,
action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
@@ -127,7 +127,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask {
updated++;
// Retrieve OCPP parameters and update them if needed
if (chargingStationTemplateUpdated.ocppStandardUpdated || chargingStationTemplateUpdated.ocppVendorUpdated) {
- Logging.logDebug({
+ await Logging.logDebug({
tenantID: tenant.id,
action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
source: chargingStation.id,
@@ -139,7 +139,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask {
Constants.DELAY_REQUEST_CONFIGURATION_EXECUTION_MILLIS, OCPPUtils.requestAndSaveChargingStationOcppParameters(tenant.id, chargingStation),
`Time out error (${Constants.DELAY_REQUEST_CONFIGURATION_EXECUTION_MILLIS}ms) in requesting OCPP Parameters`);
if (result.status !== OCPPConfigurationStatus.ACCEPTED) {
- Logging.logError({
+ await Logging.logError({
tenantID: tenant.id,
action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
source: chargingStation.id,
@@ -153,7 +153,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask {
Constants.DELAY_REQUEST_CONFIGURATION_EXECUTION_MILLIS, OCPPUtils.updateChargingStationTemplateOcppParameters(tenant.id, chargingStation),
`Time out error (${Constants.DELAY_REQUEST_CONFIGURATION_EXECUTION_MILLIS}ms) in updating OCPP Parameters`);
// Log
- Logging.logActionsResponse(
+ await Logging.logActionsResponse(
tenant.id,
ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
MODULE_NAME, 'applyTemplateToChargingStations', updatedOcppParameters,
@@ -165,7 +165,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask {
}
}
} catch (error) {
- Logging.logError({
+ await Logging.logError({
tenantID: tenant.id,
action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
source: chargingStation.id,
@@ -176,7 +176,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask {
}
}
if (updated > 0) {
- Logging.logDebug({
+ await Logging.logDebug({
tenantID: tenant.id,
action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE,
module: MODULE_NAME, method: 'applyTemplateToChargingStations',
@@ -189,7 +189,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask {
// Update current Chargers
ChargingStationStorage.updateChargingStationTemplatesFromFile().catch(
(error) => {
- Logging.logActionExceptionMessage(Constants.DEFAULT_TENANT, ServerAction.UPDATE_CHARGING_STATION_TEMPLATES, error);
+ void Logging.logActionExceptionMessage(Constants.DEFAULT_TENANT, ServerAction.UPDATE_CHARGING_STATION_TEMPLATES, error);
});
}
}

MODIFY
src/server/rest/v1/service/TagService.ts
src/server/rest/v1/service/TagService.ts
@@ -473,24 +473,39 @@ export default class TagService {
module: MODULE_NAME, method: 'handleImportTags'
});
}
+ // Default values for Tag import
+ const importedBy = req.user.id;
+ const importedOn = new Date();
+ const result: ActionsResponse = {
+ inSuccess: 0,
+ inError: 0
+ };
const busboy = new Busboy({ headers: req.headers });
req.pipe(busboy);
- busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
+ busboy.on('file', (fieldname: string, file: any, filename: string, encoding: string, mimetype: string) => {
if (mimetype === 'text/csv') {
const converter = csvToJson({
trim: true,
delimiter: ['\t'],
quote: 'off'
});
- void converter.subscribe(async (tag) => {
- await TagService.importTag(action, req, tag);
+ void converter.subscribe(async (tag: ImportedTag) => {
+ // Set default value
+ tag.importedBy = importedBy;
+ tag.importedOn = importedOn;
+ // Import
+ if (await TagService.importTag(action, req, tag)) {
+ result.inSuccess++;
+ } else {
+ result.inError++;
+ }
}, (error) => {
void Logging.logError({
tenantID: req.user.tenantID,
module: MODULE_NAME, method: 'handleImportTags',
action: action,
user: req.user.id,
- message: 'Invalid csv file',
+ message: `Invalid Csv file '${filename}'`,
detailedMessages: { error: error.message, stack: error.stack }
});
res.writeHead(HTTPError.INVALID_FILE_FORMAT);
@@ -499,8 +514,17 @@ export default class TagService {
void file.pipe(converter);
} else if (mimetype === 'application/json') {
const parser = JSONStream.parse('tags.*');
- parser.on('data', (tag) => {
- void TagService.importTag(action, req, tag);
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
+ parser.on('data', async (tag: ImportedTag) => {
+ // Set default value
+ tag.importedBy = importedBy;
+ tag.importedOn = importedOn;
+ // Import
+ if (await TagService.importTag(action, req, tag)) {
+ result.inSuccess++;
+ } else {
+ result.inError++;
+ }
});
parser.on('error', function(error) {
void Logging.logError({
@@ -508,7 +532,7 @@ export default class TagService {
module: MODULE_NAME, method: 'handleImportTags',
action: action,
user: req.user.id,
- message: 'Invalid json file',
+ message: `Invalid Json file '${filename}'`,
detailedMessages: { error: error.message, stack: error.stack }
});
res.writeHead(HTTPError.INVALID_FILE_FORMAT);
@@ -521,19 +545,28 @@ export default class TagService {
module: MODULE_NAME, method: 'handleImportTags',
action: action,
user: req.user.id,
- message: 'Invalid file format'
+ message: `Invalid file format '${mimetype}'`
});
res.writeHead(HTTPError.INVALID_FILE_FORMAT);
res.end();
}
});
- busboy.on('finish', function() {
+ busboy.on('finish', () => {
+ // Log
+ void Logging.logActionsResponse(
+ req.user.tenantID, action,
+ MODULE_NAME, 'deleteTransactions', result,
+ '{{inSuccess}} Tag(s) were successfully uploaded and ready for asynchronous import',
+ '{{inError}} Tag(s) failed to be uploaded',
+ '{{inSuccess}} Tag(s) were successfully uploaded and ready for asynchronous import and {{inError}} failed to be uploaded',
+ 'No Tag have been uploaded', req.user
+ );
void Logging.logInfo({
tenantID: req.user.tenantID,
action: action,
module: MODULE_NAME, method: 'handleImportTags',
user: req.user,
- message: 'File successfully uploaded',
+ message: 'File has been successfully uploaded in database and ready for asynchronous import',
});
res.end();
next();
@@ -633,28 +666,31 @@ export default class TagService {
'{{inSuccess}} tag(s) were successfully deleted',
'{{inError}} tag(s) failed to be deleted',
'{{inSuccess}} tag(s) were successfully deleted and {{inError}} failed to be deleted',
- 'No tags have been deleted'
+ 'No tags have been deleted', loggedUser
);
return result;
}
- private static async importTag(action: ServerAction, req: Request, tag: any): Promise<void> {
+ private static async importTag(action: ServerAction, req: Request, importedTag: ImportedTag): Promise<boolean> {
try {
- const newUploadedTag: ImportedTag = {
- id: tag.id.toUpperCase(),
- description: tag.description ? tag.description : `Badge ID '${tag.id}'`,
+ const newImportedTag: ImportedTag = {
+ id: importedTag.id.toUpperCase(),
+ description: importedTag.description ? importedTag.description : `Badge ID '${importedTag.id}'`,
};
- TagValidator.getInstance().validateTagCreation(newUploadedTag);
- newUploadedTag.importedBy = req.user.id;
- await TagStorage.saveImportedTag(req.user.tenantID, newUploadedTag);
+ // Validate Tad data
+ TagValidator.getInstance().validateTagCreation(newImportedTag);
+ // Save it for import
+ await TagStorage.saveImportedTag(req.user.tenantID, newImportedTag);
+ return true;
} catch (error) {
await Logging.logError({
tenantID: req.user.tenantID,
module: MODULE_NAME, method: 'importTag',
action: action,
- message: 'tag cannot be imported',
- detailedMessages: { error: error.message, stack: error.stack }
+ message: 'Tag cannot be imported',
+ detailedMessages: { tag: importedTag, error: error.message, stack: error.stack }
});
+ return false;
}
}
}

MODIFY
src/server/rest/v1/service/TransactionService.ts
src/server/rest/v1/service/TransactionService.ts
@@ -990,12 +990,13 @@ export default class TransactionService {
'{{inSuccess}} transaction(s) were successfully deleted',
'{{inError}} transaction(s) failed to be deleted',
'{{inSuccess}} transaction(s) were successfully deleted and {{inError}} failed to be deleted',
- 'No transactions have been deleted'
+ 'No transactions have been deleted', loggedUser
);
return result;
}
- private static async getTransactions(req: Request, action: ServerAction, params: { completedTransactions?: boolean, withTag?: boolean } = {}, projectFields): Promise<DataResult<Transaction>> {
+ private static async getTransactions(req: Request, action: ServerAction,
+ params: { completedTransactions?: boolean, withTag?: boolean } = {}, projectFields): Promise<DataResult<Transaction>> {
// Check Transactions
if (!Authorizations.canListTransactions(req.user)) {
throw new AppAuthError({

MODIFY
src/storage/mongodb/TagStorage.ts
src/storage/mongodb/TagStorage.ts
@@ -44,7 +44,7 @@ export default class TagStorage {
const tagMDB = {
_id: importedTagToSave.id,
description: importedTagToSave.description,
- importedOn: importedTagToSave.importedOn ? importedTagToSave.importedOn : new Date(),
+ importedOn: Utils.convertToDate(importedTagToSave.importedOn),
importedBy: Utils.convertToObjectID(importedTagToSave.importedBy),
errorCode: importedTagToSave.errorCode,
errorDescription: importedTagToSave.errorDescription

MODIFY
src/utils/Logging.ts
src/utils/Logging.ts
@@ -172,7 +172,7 @@ export default class Logging {
public static async logActionsResponse(
tenantID: string, action: ServerAction, module: string, method: string, actionsResponse: ActionsResponse,
messageSuccess: string, messageError: string, messageSuccessAndError: string,
- messageNoSuccessNoError: string): Promise<void> {
+ messageNoSuccessNoError: string, user?: UserToken): Promise<void> {
// Replace
messageSuccess = messageSuccess.replace('{{inSuccess}}', actionsResponse.inSuccess.toString());
messageError = messageError.replace('{{inError}}', actionsResponse.inError.toString());
@@ -182,6 +182,7 @@ export default class Logging {
if (actionsResponse.inSuccess > 0 && actionsResponse.inError > 0) {
await Logging.logError({
tenantID: tenantID,
+ user,
source: Constants.CENTRAL_SERVER,
action, module, method,
message: messageSuccessAndError
@@ -190,6 +191,7 @@ export default class Logging {
} else if (actionsResponse.inSuccess > 0) {
await Logging.logInfo({
tenantID: tenantID,
+ user,
source: Constants.CENTRAL_SERVER,
action, module, method,
message: messageSuccess
@@ -198,6 +200,7 @@ export default class Logging {
} else if (actionsResponse.inError > 0) {
await Logging.logError({
tenantID: tenantID,
+ user,
source: Constants.CENTRAL_SERVER,
action, module, method,
message: messageError
@@ -206,6 +209,7 @@ export default class Logging {
} else {
await Logging.logInfo({
tenantID: tenantID,
+ user,
source: Constants.CENTRAL_SERVER,
action, module, method,
message: messageNoSuccessNoError
@@ -266,9 +270,9 @@ export default class Logging {
}
}
- public static logOicpResult(tenantID: string, action: ServerAction, module: string, method: string, oicpResult: OICPResult,
- messageSuccess: string, messageError: string, messageSuccessAndError: string, messageNoSuccessNoError: string): void {
- Logging.logOcpiResult(tenantID, action, module, method, oicpResult,
+ public static async logOicpResult(tenantID: string, action: ServerAction, module: string, method: string, oicpResult: OICPResult,
+ messageSuccess: string, messageError: string, messageSuccessAndError: string, messageNoSuccessNoError: string): Promise<void> {
+ await Logging.logOcpiResult(tenantID, action, module, method, oicpResult,
messageSuccess, messageError, messageSuccessAndError, messageNoSuccessNoError);
}

Enhance the Tag Import Service