diff --git a/package-lock.json b/package-lock.json index 16c222a8f7..e60af356fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ev-server", - "version": "2.4.71", + "version": "2.4.72", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -16700,24 +16700,6 @@ } } }, - "safe-url-assembler": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/safe-url-assembler/-/safe-url-assembler-1.3.5.tgz", - "integrity": "sha1-vINxZ2wR7KcikQ/XRzIzsIvXwXs=", - "dev": true, - "requires": { - "extend": "~3.0.0", - "qs": "~6.3.0" - }, - "dependencies": { - "qs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", - "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", - "dev": true - } - } - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", diff --git a/package.json b/package.json index 45e78ee2f2..d4e0065b8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ev-server", - "version": "2.4.71", + "version": "2.4.72", "engines": { "node": "14.x.x", "npm": "6.x.x" @@ -243,7 +243,6 @@ "querystring": "^0.2.1", "rimraf": "^3.0.2", "rosie": "^2.1.0", - "safe-url-assembler": "^1.3.5", "ts-loader": "^8.3.0", "ts-node": "^10.0.0", "ts-node-dev": "^1.1.6", diff --git a/postman/EVSE.postman_collection.json b/postman/EVSE.postman_collection.json index 4a36e77541..06971d0ef1 100644 --- a/postman/EVSE.postman_collection.json +++ b/postman/EVSE.postman_collection.json @@ -297,7 +297,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"email\": \"demo.basic@sap.com\",\n \"hash\": \"795fc992-d9bd-4695-8fa6-78cee1ef3464\",\n \"captcha\": \"aaa\",\n \"passwords\": {\n \"password\": \"Azerty123?\",\n \"repeatPassword\": \"Azerty123?\"\n },\n \"tenant\": \"{{tenant}}\"\n}" + "raw": "{\n \"email\": \"demo.basic@sap.com\",\n \"hash\": \"795fc992-d9bd-4695-8fa6-78cee1ef3464\",\n \"captcha\": \"aaa\",\n \"password\": \"Azerty123?\",\n \"tenant\": \"{{tenant}}\"\n}" }, "url": { "raw": "{{base_url}}/v1/auth/password/reset", @@ -3554,7 +3554,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"name\": \"CreateName\",\n \"firstName\": \"CreateFirstname\",\n \"email\": \"firstname.lastname@sap.com\",\n \"passwords\": {\n \"password\": \"Azerty123!\",\n \"repeatPassword\": \"Azerty123!\"\n },\n \"acceptEula\": true,\n \"tenant\": \"slf\",\n \"role\": \"B\",\n \"status\": \"A\"\n}\n", + "raw": "{\n \"name\": \"CreateName\",\n \"firstName\": \"CreateFirstname\",\n \"email\": \"firstname.lastname@sap.com\",\n \"password\": \"Azerty123!\",\n \"acceptEula\": true,\n \"tenant\": \"slf\",\n \"role\": \"B\",\n \"status\": \"A\"\n}\n", "options": { "raw": { "language": "json" @@ -3598,7 +3598,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"name\": \"FABIANO\",\r\n \"firstName\": \"Sergio\",\r\n \"email\": \"sdf3.sdf.sdfssss@free.fr\",\r\n \"phone\": \"+33 492286307\",\r\n \"mobile\": \"\",\r\n \"iNumber\": \"I030367\",\r\n \"tagIDs\": [\r\n \"66BDC46F\"\r\n ],\r\n \"costCenter\": \"175030602\",\r\n \"status\": \"A\",\r\n \"role\": \"A\",\r\n \"passwords\": {\r\n \"password\": \"\",\r\n \"repeatPassword\": \"\"\r\n }\r\n}\r\n" + "raw": "{\r\n \"name\": \"FABIANO\",\r\n \"firstName\": \"Sergio\",\r\n \"email\": \"sdf3.sdf.sdfssss@free.fr\",\r\n \"phone\": \"+33 492286307\",\r\n \"mobile\": \"\",\r\n \"iNumber\": \"I030367\",\r\n \"tagIDs\": [\r\n \"66BDC46F\"\r\n ],\r\n \"costCenter\": \"175030602\",\r\n \"status\": \"A\",\r\n \"role\": \"A\",\r\n \"password\": \"\"\r\n" }, "url": { "raw": "{{base_url}}/v1/api/users/{{userID}}", @@ -3656,7 +3656,424 @@ "response": [] }, { - "name": "FE-UserImage", + "name": "FE-Tags", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var responseJson = JSON.parse(responseBody);", + "postman.setEnvironmentVariable(\"tag\", responseJson[\"result\"][0][\"id\"]);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/api/tags", + "host": [ + "{{base_url}}" + ], + "path": [ + "v1", + "api", + "tags" + ] + } + }, + "response": [] + }, + { + "name": "FE-Tag", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/api/tags/{{tag}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "v1", + "api", + "tags", + "{{tag}}" + ] + } + }, + "response": [] + }, + { + "name": "FE-TagExport", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/v1/api/tags/action/export?Active", + "host": [ + "{{base_url}}" + ], + "path": [ + "v1", + "api", + "tags", + "action", + "export" + ], + "query": [ + { + "key": "Active", + "value": null + }, + { + "key": "Issuer", + "value": null, + "disabled": true + }, + { + "key": "UserID", + "value": null, + "description": "Pipe separated user ids", + "disabled": true + }, + { + "key": "Search", + "value": null, + "disabled": true + }, + { + "key": "SortFields", + "value": null, + "disabled": true + }, + { + "key": "Skip", + "value": null, + "disabled": true + }, + { + "key": "OnlyRecordCount", + "value": null, + "disabled": true + }, + { + "key": "ProjectFields", + "value": null, + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "FE-TagCreate", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var responseJson = JSON.parse(responseBody);", + "postman.setEnvironmentVariable(\"tag\", responseJson[\"id\"]);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"ABCD987654\",\n \"visualID\": \"######\",\n \"description\": \"Postman tag\",\n \"active\": true,\n \"issuer\": true,\n \"default\": false,\n \"userID\": \"{{userID}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/v1/api/tags/", + "host": [ + "{{base_url}}" + ], + "path": [ + "v1", + "api", + "tags", + "" + ] + } + }, + "response": [] + }, + { + "name": "FE-TagImport", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "file", + "file": {}, + "options": { + "raw": { + "language": "text" + } + } + }, + "url": { + "raw": "{{base_url}}/v1/api/tags/action/import", + "host": [ + "{{base_url}}" + ], + "path": [ + "v1", + "api", + "tags", + "action", + "import" + ] + } + }, + "response": [] + }, + { + "name": "FE-TagUpdate", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"ABCDE1234679\",\n \"visualID\": \"New visual ID\",\n \"description\": \"Postman tag\",\n \"active\": true,\n \"issuer\": true,\n \"default\": false,\n \"userID\": \"{{userID}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/v1/api/tags/{{tag}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "v1", + "api", + "tags", + "{{tag}}" + ] + } + }, + "response": [] + }, + { + "name": "FE-TagDelete", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/v1/api/tags/{{tag}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "v1", + "api", + "tags", + "{{tag}}" + ] + } + }, + "response": [] + }, + { + "name": "FE-TagsDelete", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"tagsIDs\": [\n \"{{tagId1}}\",\n \"{{tagId2}}\"\n ]\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/v1/api/tags", + "host": [ + "{{base_url}}" + ], + "path": [ + "v1", + "api", + "tags" + ] + } + }, + "response": [] + }, + { + "name": "FE-UserImage Copy", "request": { "auth": { "type": "bearer", @@ -3955,7 +4372,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"name\": \"DEMO\",\r\n \"firstName\": \"Demo\",\r\n \"email\": \"demo.basic@sap.com\",\r\n \"phone\": \"+33 492286307\",\r\n \"mobile\": \"\",\r\n \"iNumber\": \"I030367\",\r\n \"tagIDs\": [\r\n \"66BDC46F\"\r\n ],\r\n \"costCenter\": \"175030602\",\r\n \"status\": \"A\",\r\n \"role\": \"A\",\r\n \"passwords\": {\r\n \"password\": \"\",\r\n \"repeatPassword\": \"\"\r\n }\r\n}\r\n" + "raw": "{\r\n \"name\": \"DEMO\",\r\n \"firstName\": \"Demo\",\r\n \"email\": \"demo.basic@sap.com\",\r\n \"phone\": \"+33 492286307\",\r\n \"mobile\": \"\",\r\n \"iNumber\": \"I030367\",\r\n \"tagIDs\": [\r\n \"66BDC46F\"\r\n ],\r\n \"costCenter\": \"175030602\",\r\n \"status\": \"A\",\r\n \"role\": \"A\",\r\n \"password\": \"\"\r\n}\r\n" }, "url": { "raw": "{{base_url}}/v1/api/users/{{userID}}", diff --git a/src/assets/charging-station-templates b/src/assets/charging-station-templates index 66b8c0f4ed..5a32fd9297 160000 --- a/src/assets/charging-station-templates +++ b/src/assets/charging-station-templates @@ -1 +1 @@ -Subproject commit 66b8c0f4ed59924c34657616a82ff2d9d30e726e +Subproject commit 5a32fd9297e9ab6d12db0d06b20e31c8c8d741d5 diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 49e1b9d361..a2eaef92d9 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -2,9 +2,7 @@ "billing": { "generatedUser": "Erstellter Benutzer für '{{email}}'", "chargingAtSiteArea": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {startTime} - Energy Consumption: {{totalConsumption}} kWh at {{siteAreaName}}", - "chargingAtChargeBox": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {{startTime}} - Energy Consumption: {{totalConsumption}} kWh at charging station {{chargeBoxID}}", - "chargingStopSiteArea": "Ladung von {{totalConsumption}} kWh, Ort: {{siteAreaName}} (beendet um {{stopTime}})", - "chargingStopChargeBox": "Ladung von {{totalConsumption}} kWh an Ladestation {{chargeBoxID}} (beendet um {{stopTime}})" + "chargingAtChargeBox": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {{startTime}} - Energy Consumption: {{totalConsumption}} kWh at charging station {{chargeBoxID}}" }, "chargers": { "chargeBoxSN": "Seriennummer Ladestation", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index f6325bef8a..4bd92853f5 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -2,9 +2,7 @@ "billing": { "generatedUser": "Generated user for '{{email}}'", "chargingAtSiteArea": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {startTime} - Energy Consumption: {{totalConsumption}} kWh at {{siteAreaName}}", - "chargingAtChargeBox": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {{startTime}} - Energy Consumption: {{totalConsumption}} kWh at charging station {{chargeBoxID}}", - "chargingStopSiteArea": "Charging {{totalConsumption}} kWh at {{siteAreaName}} (finished at {{stopTime}})", - "chargingStopChargeBox": "Charging {{totalConsumption}} kWh at charging station {{chargeBoxID}} (finished at {{stopTime}})" + "chargingAtChargeBox": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {{startTime}} - Energy Consumption: {{totalConsumption}} kWh at charging station {{chargeBoxID}}" }, "chargers": { "chargeBoxSN": "Charge Box S/N", diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index 2426d02a8a..b69393a1bb 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -2,9 +2,7 @@ "billing": { "generatedUser": "Usuario generado por '{{email}}'", "chargingAtSiteArea": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {startTime} - Energy Consumption: {{totalConsumption}} kWh at {{siteAreaName}}", - "chargingAtChargeBox": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {{startTime}} - Energy Consumption: {{totalConsumption}} kWh at charging station {{chargeBoxID}}", - "chargingStopSiteArea": "Cargando {{totalConsumption}} kWh en {{siteAreaName}} (finalizado a {{stopTime}})", - "chargingStopChargeBox": "Cargando {{totalConsumption}} kWh en la estación de carga {{chargeBoxID}} (finalizado a {{stopTime}})" + "chargingAtChargeBox": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {{startTime}} - Energy Consumption: {{totalConsumption}} kWh at charging station {{chargeBoxID}}" }, "chargers": { "chargeBoxSN": "Charge Box S/N", diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 3e17732301..862267a03c 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -2,9 +2,7 @@ "billing": { "generatedUser": "Utilisateur généré pour '{{email}}'", "chargingAtSiteArea": "Session : {{sessionID}} - Date : {{startDate}} à {startTime} - Energie Consommée : {{totalConsumption}} kWh sur le zone {{siteAreaName}}", - "chargingAtChargeBox": "Session : {{sessionID}} - Date : {{startDate}} à {{startTime}} - Energie Consommée : {{totalConsumption}} kWh à la borne {{chargeBoxID}}", - "chargingStopSiteArea": "Consommation de {{totalConsumption}} kWh sur la zone {{siteAreaName}} (terminé à {{stopTime}})", - "chargingStopChargeBox": "Consommation de {{totalConsumption}} kWh à la borne {{chargeBoxID}} (terminé à {{stopTime}})" + "chargingAtChargeBox": "Session : {{sessionID}} - Date : {{startDate}} à {{startTime}} - Energie Consommée : {{totalConsumption}} kWh à la borne {{chargeBoxID}}" }, "chargers": { "chargeBoxSN": "Numéro de série de la borne", diff --git a/src/assets/i18n/it.json b/src/assets/i18n/it.json index c6aa73e6bc..05c2e84328 100644 --- a/src/assets/i18n/it.json +++ b/src/assets/i18n/it.json @@ -2,9 +2,7 @@ "billing": { "generatedUser": "Utenza generata per '{{email}}'", "chargingAtSiteArea": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {startTime} - Energy Consumption: {{totalConsumption}} kWh at {{siteAreaName}}", - "chargingAtChargeBox": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {{startTime}} - Energy Consumption: {{totalConsumption}} kWh at charging station {{chargeBoxID}}", - "chargingStopSiteArea": "Caricando {{totalConsumption}} kWh a {{siteAreaName}} (finito a {{stopTime}})", - "chargingStopChargeBox": "Caricando {{totalConsumption}} kWh alla stazione di ricarica {{chargeBoxID}} (finito a {{stopTime}})" + "chargingAtChargeBox": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {{startTime}} - Energy Consumption: {{totalConsumption}} kWh at charging station {{chargeBoxID}}" }, "chargers": { "chargeBoxSN": "Charge Box S/N", diff --git a/src/assets/i18n/pt.json b/src/assets/i18n/pt.json index 8e724ee95d..69a1aa9950 100644 --- a/src/assets/i18n/pt.json +++ b/src/assets/i18n/pt.json @@ -2,9 +2,7 @@ "billing": { "generatedUser": "Utilizador criado para '{{email}}'", "chargingAtSiteArea": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {startTime} - Energy Consumption: {{totalConsumption}} kWh at {{siteAreaName}}", - "chargingAtChargeBox": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {{startTime}} - Energy Consumption: {{totalConsumption}} kWh at charging station {{chargeBoxID}}", - "chargingStopSiteArea": "Carregamento {{totalConsumption}} kWh na {{siteAreaName}} (terminado as {{stopTime}})", - "chargingStopChargeBox": "Carregamento {{totalConsumption}} kWh em curso na estação {{chargeBoxID}} (termiando as {{stopTime}})" + "chargingAtChargeBox": "Charging session: {{sessionID}} - Start Date: {{startDate}} at {{startTime}} - Energy Consumption: {{totalConsumption}} kWh at charging station {{chargeBoxID}}" }, "chargers": { "chargeBoxSN": "Charge Box S/N", diff --git a/src/assets/server/rest/v1/docs/e-mobility-oas.json b/src/assets/server/rest/v1/docs/e-mobility-oas.json index 56ae1295ab..61aaa660ee 100644 --- a/src/assets/server/rest/v1/docs/e-mobility-oas.json +++ b/src/assets/server/rest/v1/docs/e-mobility-oas.json @@ -321,7 +321,7 @@ "email", "name", "firstName", - "passwords", + "password", "acceptEula", "tenant", "captcha" @@ -339,18 +339,9 @@ "type": "string", "example": "User" }, - "passwords": { - "type": "object", - "properties": { - "password": { - "type": "string", - "example": "DeM*Us$r1" - }, - "repeatPassword": { - "type": "string", - "example": "DeM*Us$r1" - } - } + "password": { + "type": "string", + "example": "DeM*Us$r1" }, "acceptEula": { "type": "boolean", @@ -428,16 +419,8 @@ "type": "string", "example": "demo.demo@sap.com" }, - "passwords": { - "type": "object", - "properties": { - "password": { - "type": "string" - }, - "repeatPassword": { - "type": "string" - } - } + "password": { + "type": "string" }, "captcha": { "type": "string", @@ -464,10 +447,7 @@ "email": "demo.demo@sap.com", "captcha": "03AGdBq26Ca8rdqn0-odDV18kJCLnVytzGPXVPyW811IY4pqyQuak-o59MZlYfUIVS-ToLBiL4o7fBfPPvmkAhQLKoMXtqitKxRTG016wmGbFLvZWnOSkJKreB1Kx6n3dB7uYd302UVyVpJJ0BGFcmXWnWZmGnR3ZJC8IE1r7oVohcYVLttnKXtuhJwg5IdHe0HleYzOjxCBw9FnCJhVoSmaPn6FeVjWC9d8vSDwBq-18dA_cm6CQb7BhZm6LXWhMdW1_e_5DcSs1koj_vn3FGB7UQESsPp81WBfr6msCNiddNc1D4MRfF4wXeimJ1wplqoGOmMmI2Y_i-120grLrjkfzBNgDPQPWvfpUjeyW1f_T-5SaObYpUFEEZ4YW6Xt2revAWMmZ6i9_QOWWUcukaZU3jr_MzR1YIMscKrnBP3Zhl45zLXOY-0Bi28z6XxsMqqSHE8lY_C8B-7M-X3PNc_293qtrEnE2rhG9PU-RdFJG4dqBcwU6YfZJwwyXDWG7eyNG8iJoBDgCWrbib2jLKTmuwWCowApAoeA", "hash": "dcff1ec7-cecb-4c2f-99da-47aeb1f83509", - "passwords": { - "password": "Azerty123?", - "repeatPassword": "Azerty123?" - }, + "password": "Azerty123?", "tenant": "slf" } } @@ -4589,10 +4569,10 @@ "$ref": "#/components/responses/BackendError" }, "583": { - "description": "Invalid file format" + "$ref": "#/components/responses/InvalidFileFormat" }, "584": { - "description": "Invalid CSV header format" + "$ref": "#/components/responses/InvalidCSVHeaderFormat" } } } @@ -4701,12 +4681,10 @@ "$ref": "#/components/parameters/UserID" }, { - "in": "query", - "name": "Active", - "description": "Active attribute", - "schema": { - "type": "boolean" - } + "$ref": "#/components/parameters/TagActive" + }, + { + "$ref": "#/components/parameters/TagWithUser" }, { "$ref": "#/components/parameters/Skip" @@ -4818,6 +4796,60 @@ "description": "Tag already exists" } } + }, + "delete": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Delete several Tags", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tagsIDs": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "tags": [ + "Tags" + ], + "responses": { + "200": { + "description": "Tags deletion result", + "content": { + "application/json": { + "example": { + "inSuccess": 42, + "inError": 0, + "status": "Success" + } + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + }, + "550": { + "$ref": "#/components/responses/ResourceDoesNotExists" + } + } } }, "/api/tags/{id}": { @@ -4997,9 +5029,123 @@ }, "550": { "$ref": "#/components/responses/ResourceDoesNotExists" + } + } + } + }, + "/api/tags/action/import": { + "post": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Import a Tag file", + "requestBody": { + "content": { + "text/csv": { + "schema": { + "type": "string", + "format": "binary", + "example": "id,visualID,description,firstName,name,email\n\"###\",\"###\",\"###\",\"###\",\"###\",\"###\"" + } + } + } + }, + "tags": [ + "Tags" + ], + "responses": { + "200": { + "description": "Tag importation results", + "content": { + "application/json": { + "example": { + "inSuccess": 42, + "inError": 0, + "status": "Success" + } + } + } }, - "575": { - "$ref": "#/components/responses/TagHasTransactionsError" + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + }, + "583": { + "$ref": "#/components/responses/InvalidFileFormat" + }, + "584": { + "$ref": "#/components/responses/InvalidCSVHeaderFormat" + } + } + } + }, + "/api/tags/action/export": { + "get": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Export Tags", + "parameters": [ + { + "$ref": "#/components/parameters/Search" + }, + { + "$ref": "#/components/parameters/UserIDs" + }, + { + "$ref": "#/components/parameters/Issuer" + }, + { + "$ref": "#/components/parameters/TagActive" + }, + { + "$ref": "#/components/parameters/TagWithUser" + }, + { + "$ref": "#/components/parameters/Skip" + }, + { + "$ref": "#/components/parameters/Limit" + }, + { + "$ref": "#/components/parameters/ProjectFields" + } + ], + "tags": [ + "Tags" + ], + "responses": { + "200": { + "description": "Return exported tags", + "content": { + "text/csv": { + "example": "id,visualID,description,firstName,name,email\n\"###\",\"###\",\"###\",\"###\",\"###\",\"###\"" + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + }, + "583": { + "$ref": "#/components/responses/InvalidFileFormat" + }, + "584": { + "$ref": "#/components/responses/InvalidCSVHeaderFormat" } } } @@ -5037,6 +5183,12 @@ }, "TagHasTransactionsError": { "description": "Cannot change user or delete a tag having transactions" + }, + "InvalidFileFormat": { + "description": "Invalid file format" + }, + "InvalidCSVHeaderFormat": { + "description": "Invalid CSV header format" } }, "schemas": { @@ -5710,6 +5862,10 @@ "type": "string", "example": "T12345678" }, + "visualID": { + "type": "string", + "example": "###" + }, "userID": { "type": "string", "example": "###" @@ -5769,14 +5925,9 @@ "address": { "$ref": "#/components/schemas/Address" }, - "passwords": { - "type": "object", - "properties": { - "password": { - "type": "string", - "example": "DeM*Us$r1" - } - } + "password": { + "type": "string", + "example": "DeM*Us$r1" }, "phone": { "type": "string", @@ -6392,6 +6543,22 @@ "schema": { "type": "string" } + }, + "TagActive": { + "in": "query", + "name": "Active", + "description": "Active attribute", + "schema": { + "type": "boolean" + } + }, + "TagWithUser": { + "in": "query", + "name": "WithUser", + "description": "Retrieve also the user", + "schema": { + "type": "boolean" + } } } } diff --git a/src/assets/server/rest/v1/schemas/auth/auth-reset-password.json b/src/assets/server/rest/v1/schemas/auth/auth-reset-password.json index 356bf164aa..18d69c2239 100644 --- a/src/assets/server/rest/v1/schemas/auth/auth-reset-password.json +++ b/src/assets/server/rest/v1/schemas/auth/auth-reset-password.json @@ -15,17 +15,8 @@ "type": "string", "sanitize": "mongo" }, - "passwords": { - "type": "object", - "properties": { - "password": { - "$ref": "user.json#/definitions/password" - }, - "repeatPassword": { - "$ref": "user.json#/definitions/password" - } - }, - "required": ["password", "repeatPassword"] + "password": { + "$ref": "user.json#/definitions/password" } }, "required": [ @@ -37,6 +28,6 @@ { "required": ["hash"] } ], "dependencies": { - "hash": { "required": ["passwords"] } + "hash": { "required": ["password"] } } } diff --git a/src/assets/server/rest/v1/schemas/auth/auth-signin.json b/src/assets/server/rest/v1/schemas/auth/auth-signin.json index cc1e0aeca0..fa2a1b106a 100644 --- a/src/assets/server/rest/v1/schemas/auth/auth-signin.json +++ b/src/assets/server/rest/v1/schemas/auth/auth-signin.json @@ -6,7 +6,8 @@ "$ref": "user.json#/definitions/email" }, "password": { - "$ref": "user.json#/definitions/password" + "type": "string", + "sanitize": "mongo" }, "acceptEula": { "$ref": "common.json#/definitions/acceptEula" diff --git a/src/assets/server/rest/v1/schemas/auth/auth-signon.json b/src/assets/server/rest/v1/schemas/auth/auth-signon.json index bf6c867957..5cc5963898 100644 --- a/src/assets/server/rest/v1/schemas/auth/auth-signon.json +++ b/src/assets/server/rest/v1/schemas/auth/auth-signon.json @@ -11,16 +11,8 @@ "firstName": { "$ref": "user.json#/definitions/firstName" }, - "passwords": { - "type": "object", - "properties": { - "password": { - "$ref": "user.json#/definitions/password" - }, - "repeatPassword": { - "$ref": "user.json#/definitions/password" - } - } + "password": { + "$ref": "user.json#/definitions/password" }, "acceptEula": { "$ref": "common.json#/definitions/acceptEula" @@ -41,7 +33,7 @@ "email", "name", "firstName", - "passwords", + "password", "acceptEula", "captcha" ] diff --git a/src/assets/server/rest/v1/schemas/tag/imported-tag-create-req.json b/src/assets/server/rest/v1/schemas/tag/imported-tag-create-req.json index 82c06d84ad..46de46266c 100644 --- a/src/assets/server/rest/v1/schemas/tag/imported-tag-create-req.json +++ b/src/assets/server/rest/v1/schemas/tag/imported-tag-create-req.json @@ -1,19 +1,16 @@ { - "title": "Tag creation", + "title": "Tag importation", "description": "", "type": "object", "properties": { "id": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "pattern": "^[a-zA-Z0-9]*$" + "$ref": "tag.json#/definitions/id" }, "description": { - "type": "string" + "$ref": "tag.json#/definitions/description" }, "visualID": { - "type": "string" + "$ref": "tag.json#/definitions/visualID" } }, "required": [ diff --git a/src/assets/server/rest/v1/schemas/tag/tag-create.json b/src/assets/server/rest/v1/schemas/tag/tag-create.json new file mode 100644 index 0000000000..3fc1682e53 --- /dev/null +++ b/src/assets/server/rest/v1/schemas/tag/tag-create.json @@ -0,0 +1,33 @@ +{ + "title": "Tag creation", + "description": "", + "type": "object", + "properties": { + "id": { + "$ref": "tag.json#/definitions/id" + }, + "visualID": { + "$ref": "tag.json#/definitions/visualID" + }, + "description": { + "$ref": "tag.json#/definitions/description" + }, + "active": { + "$ref": "tag.json#/definitions/active" + }, + "issuer": { + "$ref": "tag.json#/definitions/issuer" + }, + "default": { + "$ref": "tag.json#/definitions/default" + }, + "userID": { + "$ref": "tag.json#/definitions/userID" + } + }, + "required": [ + "id", + "visualID", + "userID" + ] +} diff --git a/src/assets/server/rest/v1/schemas/tag/tag-get.json b/src/assets/server/rest/v1/schemas/tag/tag-get.json new file mode 100644 index 0000000000..decbc54571 --- /dev/null +++ b/src/assets/server/rest/v1/schemas/tag/tag-get.json @@ -0,0 +1,16 @@ +{ + "title": "Get a Tag", + "description": "", + "type": "object", + "properties": { + "ID": { + "$ref": "tag.json#/definitions/id" + }, + "ProjectFields": { + "$ref": "common.json#/definitions/projectFields" + } + }, + "required": [ + "ID" + ] +} diff --git a/src/assets/server/rest/v1/schemas/tag/tag-update.json b/src/assets/server/rest/v1/schemas/tag/tag-update.json new file mode 100644 index 0000000000..35e830c14d --- /dev/null +++ b/src/assets/server/rest/v1/schemas/tag/tag-update.json @@ -0,0 +1,33 @@ +{ + "title": "Update a Tag", + "description": "", + "type": "object", + "properties": { + "id": { + "$ref": "tag.json#/definitions/id" + }, + "visualID": { + "$ref": "tag.json#/definitions/visualID" + }, + "description": { + "$ref": "tag.json#/definitions/description" + }, + "active": { + "$ref": "tag.json#/definitions/active" + }, + "issuer": { + "$ref": "tag.json#/definitions/issuer" + }, + "default": { + "$ref": "tag.json#/definitions/default" + }, + "userID": { + "$ref": "tag.json#/definitions/userID" + } + }, + "required": [ + "id", + "visualID", + "userID" + ] +} diff --git a/src/assets/server/rest/v1/schemas/tag/tag.json b/src/assets/server/rest/v1/schemas/tag/tag.json index aafb6d1fa5..f3d3d3450e 100644 --- a/src/assets/server/rest/v1/schemas/tag/tag.json +++ b/src/assets/server/rest/v1/schemas/tag/tag.json @@ -10,6 +10,29 @@ "type": "string", "pattern": "^(([\\w\\d]*)(\\||[\\w\\d])*)?$", "sanitize": "mongo" + }, + "visualID": { + "type": "string", + "sanitize": "mongo" + }, + "description": { + "type": "string", + "sanitize": "mongo" + }, + "active": { + "type": "boolean", + "sanitize": "mongo" + }, + "issuer": { + "type": "boolean", + "sanitize": "mongo" + }, + "default": { + "type": "boolean", + "sanitize": "mongo" + }, + "userID": { + "$ref": "user.json#/definitions/id" } } } diff --git a/src/assets/server/rest/v1/schemas/tag/tags-delete.json b/src/assets/server/rest/v1/schemas/tag/tags-delete.json new file mode 100644 index 0000000000..57eabfc41a --- /dev/null +++ b/src/assets/server/rest/v1/schemas/tag/tags-delete.json @@ -0,0 +1,16 @@ +{ + "title": "Delete several Tags", + "description": "", + "type": "object", + "properties": { + "tagsIDs": { + "type": "array", + "items": { + "$ref": "tag.json#/definitions/id" + } + } + }, + "required": [ + "tagsIDs" + ] +} diff --git a/src/assets/server/rest/v1/schemas/tag/tags-get.json b/src/assets/server/rest/v1/schemas/tag/tags-get.json new file mode 100644 index 0000000000..16f7303000 --- /dev/null +++ b/src/assets/server/rest/v1/schemas/tag/tags-get.json @@ -0,0 +1,32 @@ +{ + "title": "Get Tags", + "description": "", + "type": "object", + "properties": { + "Active": { + "$ref": "tag.json#/definitions/active" + }, + "Issuer": { + "$ref": "tag.json#/definitions/issuer" + }, + "UserID": { + "$ref": "user.json#/definitions/ids" + }, + "Search": { + "$ref": "common.json#/definitions/search" + }, + "SortFields": { + "$ref": "common.json#/definitions/sortFields" + }, + "Skip": { + "$ref": "common.json#/definitions/skip" + }, + "OnlyRecordCount": { + "$ref": "common.json#/definitions/onlyRecordCount" + }, + "ProjectFields": { + "$ref": "common.json#/definitions/projectFields" + } + }, + "required": [] +} diff --git a/src/assets/server/rest/v1/schemas/tenant/tenant-create.json b/src/assets/server/rest/v1/schemas/tenant/tenant-create.json index 21cb6a0d5e..bc6b74fb2e 100644 --- a/src/assets/server/rest/v1/schemas/tenant/tenant-create.json +++ b/src/assets/server/rest/v1/schemas/tenant/tenant-create.json @@ -2,6 +2,9 @@ "title": "Create one Tenant", "type": "object", "properties": { + "id": { + "$ref": "common.json#/definitions/id" + }, "name": { "$ref": "tenant.json#/definitions/name" }, diff --git a/src/assets/server/rest/v1/schemas/transaction/transactions-get.json b/src/assets/server/rest/v1/schemas/transaction/transactions-get.json index 250c0167f0..fc590b9c12 100644 --- a/src/assets/server/rest/v1/schemas/transaction/transactions-get.json +++ b/src/assets/server/rest/v1/schemas/transaction/transactions-get.json @@ -55,7 +55,7 @@ "Statistics": { "type": "string", "sanitize": "mongo", - "enum": ["refund", "history"] + "enum": ["refund", "history", "ongoing"] }, "ReportIDs": { "$ref": "common.json#/definitions/ids" diff --git a/src/assets/server/rest/v1/schemas/user/user-create.json b/src/assets/server/rest/v1/schemas/user/user-create.json index c4c938fc6f..21d943cd86 100644 --- a/src/assets/server/rest/v1/schemas/user/user-create.json +++ b/src/assets/server/rest/v1/schemas/user/user-create.json @@ -29,8 +29,8 @@ "address": { "$ref": "user.json#/definitions/address" }, - "passwords": { - "$ref": "user.json#/definitions/passwords" + "password": { + "$ref": "user.json#/definitions/password" }, "phone": { "$ref": "user.json#/definitions/phone" @@ -57,6 +57,6 @@ "required": [ "name", "email", - "passwords" + "password" ] } diff --git a/src/assets/server/rest/v1/schemas/user/user-update.json b/src/assets/server/rest/v1/schemas/user/user-update.json index 84f053777d..38b08d63e8 100644 --- a/src/assets/server/rest/v1/schemas/user/user-update.json +++ b/src/assets/server/rest/v1/schemas/user/user-update.json @@ -32,8 +32,8 @@ "address": { "$ref": "user.json#/definitions/address" }, - "passwords": { - "$ref": "user.json#/definitions/passwords" + "password": { + "$ref": "user.json#/definitions/password" }, "phone": { "$ref": "user.json#/definitions/phone" diff --git a/src/assets/server/rest/v1/schemas/user/user.json b/src/assets/server/rest/v1/schemas/user/user.json index cf83d43f19..96e7952585 100644 --- a/src/assets/server/rest/v1/schemas/user/user.json +++ b/src/assets/server/rest/v1/schemas/user/user.json @@ -4,6 +4,9 @@ "id": { "$ref": "common.json#/definitions/id" }, + "ids": { + "$ref": "common.json#/definitions/ids" + }, "name": { "type": "string", "minLength": 1, @@ -49,11 +52,6 @@ "address": { "$ref": "common.json#/definitions/address" }, - "passwords": { - "password": { - "$ref": "#/definitions/password" - } - }, "phone": { "type": "string", "sanitize": "mongo" diff --git a/src/assets/server/rest/v1/schemas/user/users-get.json b/src/assets/server/rest/v1/schemas/user/users-get.json index e1183a19d6..f20d852eeb 100644 --- a/src/assets/server/rest/v1/schemas/user/users-get.json +++ b/src/assets/server/rest/v1/schemas/user/users-get.json @@ -14,7 +14,7 @@ "$ref": "common.json#/definitions/ids" }, "UserID": { - "$ref": "common.json#/definitions/ids" + "$ref": "user.json#/definitions/ids" }, "Roles": { "type": "string", @@ -33,7 +33,7 @@ "$ref": "tag.json#/definitions/ids" }, "ExcludeUserIDs": { - "$ref": "common.json#/definitions/ids" + "$ref": "user.json#/definitions/ids" }, "IncludeCarUserIDs": { "$ref": "common.json#/definitions/ids" diff --git a/src/authorization/Authorizations.ts b/src/authorization/Authorizations.ts index ce069ef0af..152a3fa6f1 100644 --- a/src/authorization/Authorizations.ts +++ b/src/authorization/Authorizations.ts @@ -1,10 +1,10 @@ import { Action, AuthorizationContext, AuthorizationResult, Entity } from '../types/Authorization'; -import ChargingStation, { Connector } from '../types/ChargingStation'; import User, { UserRole, UserStatus } from '../types/User'; import AuthorizationConfiguration from '../types/configuration/AuthorizationConfiguration'; import AuthorizationsDefinition from './AuthorizationsDefinition'; import BackendError from '../exception/BackendError'; +import ChargingStation from '../types/ChargingStation'; import ChargingStationStorage from '../storage/mongodb/ChargingStationStorage'; import Configuration from '../utils/Configuration'; import Constants from '../utils/Constants'; @@ -19,7 +19,6 @@ import { OICPAuthorizationStatus } from '../types/oicp/OICPAuthentication'; import OICPClientFactory from '../client/oicp/OICPClientFactory'; import { OICPDefaultTagId } from '../types/oicp/OICPIdentification'; import { OICPRole } from '../types/oicp/OICPRole'; -import { ObjectID } from 'mongodb'; import { PricingSettingsType } from '../types/Setting'; import { ServerAction } from '../types/Server'; import SessionHashService from '../server/rest/v1/service/SessionHashService'; @@ -959,7 +958,6 @@ export default class Authorizations { action: ServerAction, tenant: Tenant, tagID: string, chargingStation: ChargingStation) { const tag: Tag = { id: tagID, - visualID: new ObjectID().toString(), description: `Badged on '${chargingStation.id}'`, issuer: true, active: false, @@ -970,7 +968,7 @@ export default class Authorizations { await TagStorage.saveTag(tenant.id, tag); // Notify (Async) NotificationHandler.sendUnknownUserBadged( - tenant.id, + tenant, Utils.generateUUID(), chargingStation, { @@ -1071,7 +1069,7 @@ export default class Authorizations { } // Site ----------------------------------------------------- chargingStation.siteArea.site = chargingStation.siteArea.site ?? - (chargingStation.siteArea.siteID ? await SiteStorage.getSite(tenant.id, chargingStation.siteArea.siteID) : null); + (chargingStation.siteArea.siteID ? await SiteStorage.getSite(tenant, chargingStation.siteArea.siteID) : null); if (!chargingStation.siteArea.site) { // Reject Site Not Found throw new BackendError({ diff --git a/src/authorization/dynamic-data-source/AssignedSitesCompaniesDynamicAuthorizationDataSource.ts b/src/authorization/dynamic-data-source/AssignedSitesCompaniesDynamicAuthorizationDataSource.ts index 1fc9f86664..8c4562d2a5 100644 --- a/src/authorization/dynamic-data-source/AssignedSitesCompaniesDynamicAuthorizationDataSource.ts +++ b/src/authorization/dynamic-data-source/AssignedSitesCompaniesDynamicAuthorizationDataSource.ts @@ -22,7 +22,7 @@ export default class AssignedSitesCompaniesDynamicAuthorizationDataSource private async getAssignedSitesCompanyIDs(): Promise { // Get the Company IDs of the assigned Sites - const sites = await SiteStorage.getSites(this.tenant.id, + const sites = await SiteStorage.getSites(this.tenant, { userID: this.userToken.id, issuer: true, diff --git a/src/authorization/dynamic-data-source/AssignedSitesDynamicAuthorizationDataSource.ts b/src/authorization/dynamic-data-source/AssignedSitesDynamicAuthorizationDataSource.ts index cbbf3db9dd..ea526b6e7b 100644 --- a/src/authorization/dynamic-data-source/AssignedSitesDynamicAuthorizationDataSource.ts +++ b/src/authorization/dynamic-data-source/AssignedSitesDynamicAuthorizationDataSource.ts @@ -22,7 +22,7 @@ export default class AssignedSitesDynamicAuthorizationDataSource private async getAssignedSiteIDs(): Promise { // Get the Site IDs of the assigned Sites - const sites = await SiteStorage.getSites(this.tenant.id, + const sites = await SiteStorage.getSites(this.tenant, { userID: this.userToken.id, issuer: true, diff --git a/src/client/ocpi/CpoOCPIClient.ts b/src/client/ocpi/CpoOCPIClient.ts index 75e21c747f..024fc73c8d 100644 --- a/src/client/ocpi/CpoOCPIClient.ts +++ b/src/client/ocpi/CpoOCPIClient.ts @@ -227,7 +227,7 @@ export default class CpoOCPIClient extends OCPIClient { public async startSession(ocpiToken: OCPIToken, chargingStation: ChargingStation, transaction: Transaction, authorizationId: string): Promise { // Get tokens endpoint url const sessionsUrl = `${this.getEndpointUrl('sessions', ServerAction.OCPI_PUSH_SESSIONS)}/${this.getLocalCountryCode(ServerAction.OCPI_PUSH_SESSIONS)}/${this.getLocalPartyID(ServerAction.OCPI_PUSH_SESSIONS)}/${transaction.id.toString()}`; - const site = await SiteStorage.getSite(this.tenant.id, chargingStation.siteID); + const site = await SiteStorage.getSite(this.tenant, chargingStation.siteID); const ocpiLocation: OCPILocation = this.convertChargingStationToOCPILocation(this.tenant, site, chargingStation, transaction.connectorId, this.getLocalCountryCode(ServerAction.OCPI_PUSH_SESSIONS), this.getLocalPartyID(ServerAction.OCPI_PUSH_SESSIONS)); // Build payload @@ -706,7 +706,7 @@ export default class CpoOCPIClient extends OCPIClient { // Send notification to admins // eslint-disable-next-line @typescript-eslint/no-floating-promises NotificationHandler.sendOCPIPatchChargingStationsStatusesError( - this.tenant.id, + this.tenant, { location: location.name, evseDashboardURL: Utils.buildEvseURL(this.tenant.subdomain), diff --git a/src/client/ocpi/EmspOCPIClient.ts b/src/client/ocpi/EmspOCPIClient.ts index a65193e9a2..d0a36cc05f 100644 --- a/src/client/ocpi/EmspOCPIClient.ts +++ b/src/client/ocpi/EmspOCPIClient.ts @@ -152,7 +152,7 @@ export default class EmspOCPIClient extends OCPIClient { locationsUrl = `${locationsUrl}?limit=5`; } const company = await this.checkAndGetCompany(); - const sites = await SiteStorage.getSites(this.tenant.id, + const sites = await SiteStorage.getSites(this.tenant, { companyIDs: [ company.id ] }, Constants.DB_PARAMS_MAX_LIMIT); let nextResult = true; do { @@ -339,7 +339,7 @@ export default class EmspOCPIClient extends OCPIClient { Utils.convertToFloat(location.coordinates.latitude) ]; } - site.id = await SiteStorage.saveSite(this.tenant.id, site, false); + site.id = await SiteStorage.saveSite(this.tenant, site, false); // Push the Site then it can be retrieve in the next round existingSites.push(site); } diff --git a/src/client/oicp/CpoOICPClient.ts b/src/client/oicp/CpoOICPClient.ts index 95715d7154..a7d9a2d877 100644 --- a/src/client/oicp/CpoOICPClient.ts +++ b/src/client/oicp/CpoOICPClient.ts @@ -60,7 +60,7 @@ export default class CpoOICPClient extends OICPClient { // Get Site Area const siteArea = chargingStation.siteArea; // Get Site - const site = await SiteStorage.getSite(this.tenant.id, chargingStation.siteID); + const site = await SiteStorage.getSite(this.tenant, chargingStation.siteID); // Get Evse const oicpEvse = OICPUtils.getEvseByConnectorId( site, siteArea, chargingStation, transaction.connectorId, options); @@ -201,7 +201,7 @@ export default class CpoOICPClient extends OICPClient { let currentSiteSkip = 0; do { // Get the public Sites - sites = (await SiteStorage.getSites(this.tenant.id, + sites = (await SiteStorage.getSites(this.tenant, { public: true }, { skip: currentSiteSkip, limit: Constants.DB_RECORD_COUNT_DEFAULT })).result; if (!Utils.isEmptyArray(sites)) { for (const site of sites) { @@ -281,7 +281,7 @@ export default class CpoOICPClient extends OICPClient { if (result.failure > 0) { // eslint-disable-next-line @typescript-eslint/no-floating-promises NotificationHandler.sendOICPPatchChargingStationsError( - this.tenant.id, + this.tenant, { evseDashboardURL: Utils.buildEvseURL(this.tenant.subdomain) } @@ -349,7 +349,7 @@ export default class CpoOICPClient extends OICPClient { let currentSiteSkip = 0; do { // Get the public Sites - sites = (await SiteStorage.getSites(this.tenant.id, + sites = (await SiteStorage.getSites(this.tenant, { public: true }, { skip: currentSiteSkip, limit: Constants.DB_RECORD_COUNT_DEFAULT })).result; if (!Utils.isEmptyArray(sites)) { for (const site of sites) { @@ -408,7 +408,7 @@ export default class CpoOICPClient extends OICPClient { // Send notification to admins // eslint-disable-next-line @typescript-eslint/no-floating-promises NotificationHandler.sendOICPPatchChargingStationsStatusesError( - this.tenant.id, + this.tenant, { evseDashboardURL: Utils.buildEvseURL(this.tenant.subdomain) } diff --git a/src/client/websocket/WSClient.ts b/src/client/websocket/WSClient.ts index 37108a491a..8a5e8dfef5 100644 --- a/src/client/websocket/WSClient.ts +++ b/src/client/websocket/WSClient.ts @@ -147,11 +147,11 @@ export default class WSClient { tenantID: this.logTenantID, module: MODULE_NAME, method: 'reconnect', action: ServerAction.WS_CLIENT_INFO, - message: `Reconnection try #${this.autoReconnectRetryCount} to '${this.url}' with timeout ${this.autoReconnectTimeout}ms` + message: `Reconnection try #${this.autoReconnectRetryCount} to '${this.url}' with timeout ${this.autoReconnectTimeout} ms` }); } else { // eslint-disable-next-line no-console - (Utils.isDevelopmentEnv() || Utils.isTestEnv()) && console.log(`WSClient reconnection try #${this.autoReconnectRetryCount} to '${this.url}' with timeout ${this.autoReconnectTimeout}ms`); + (Utils.isDevelopmentEnv() || Utils.isTestEnv()) && console.log(`WSClient reconnection try #${this.autoReconnectRetryCount} to '${this.url}' with timeout ${this.autoReconnectTimeout} ms`); } this.onreconnect(error); this.open(); diff --git a/src/integration/billing/BillingIntegration.ts b/src/integration/billing/BillingIntegration.ts index 08d27baafe..8cbb7b92e4 100644 --- a/src/integration/billing/BillingIntegration.ts +++ b/src/integration/billing/BillingIntegration.ts @@ -250,7 +250,7 @@ export default abstract class BillingIntegration { const invoiceAmount = new Intl.NumberFormat(Utils.convertLocaleForCurrency(billingInvoice.user.locale), { style: 'currency', currency: billingInvoice.currency.toUpperCase() }).format(decimInvoiceAmount.toNumber()); // Send async notification await NotificationHandler.sendBillingNewInvoiceNotification( - this.tenant.id, + this.tenant, billingInvoice.id, billingInvoice.user, { diff --git a/src/integration/billing/stripe/StripeBillingIntegration.ts b/src/integration/billing/stripe/StripeBillingIntegration.ts index 0b7eca8508..ac0ab41702 100644 --- a/src/integration/billing/stripe/StripeBillingIntegration.ts +++ b/src/integration/billing/stripe/StripeBillingIntegration.ts @@ -1180,18 +1180,12 @@ export default class StripeBillingIntegration extends BillingIntegration { const chargeBox = transaction.chargeBox; const i18nManager = I18nManager.getInstanceForLocale(transaction.user.locale); const sessionID = String(transaction?.id); - const startDate = i18nManager.formatDateTime(transaction.timestamp, 'LL'); - const startTime = i18nManager.formatDateTime(transaction.timestamp, 'LT'); - const stopTime = i18nManager.formatDateTime(transaction.stop.timestamp, 'LT'); + const startDate = i18nManager.formatDateTime(transaction.timestamp, 'LL', transaction.timezone); + const startTime = i18nManager.formatDateTime(transaction.timestamp, 'LT', transaction.timezone); + const stopTime = i18nManager.formatDateTime(transaction.stop.timestamp, 'LT', transaction.timezone); const formattedConsumptionkWh = this.formatConsumptionToKWh(transaction); const timeSpent = this.convertTimeSpentToString(transaction); - // TODO: Determine the description pattern to use according to the billing settings - let descriptionPattern; - if (FeatureToggles.isFeatureActive(Feature.BILLING_ITEM_WITH_START_DATE)) { - descriptionPattern = (chargeBox?.siteArea?.name) ? 'billing.chargingAtSiteArea' : 'billing.chargingAtChargeBox'; - } else { - descriptionPattern = (chargeBox?.siteArea?.name) ? 'billing.chargingStopSiteArea' : 'billing.chargingStopChargeBox'; - } + const descriptionPattern = (chargeBox?.siteArea?.name) ? 'billing.chargingAtSiteArea' : 'billing.chargingAtChargeBox'; // Get the translated line item description const description = i18nManager.translate(descriptionPattern, { sessionID, diff --git a/src/integration/smart-charging/SmartChargingIntegration.ts b/src/integration/smart-charging/SmartChargingIntegration.ts index 44420f9c22..50c7f80e52 100644 --- a/src/integration/smart-charging/SmartChargingIntegration.ts +++ b/src/integration/smart-charging/SmartChargingIntegration.ts @@ -54,7 +54,7 @@ export default abstract class SmartChargingIntegration { + private async handleRefusedChargingProfile(tenant: Tenant, chargingProfile: ChargingProfile, siteAreaName: string): Promise { // Retry setting the cp 2 more times for (let i = 0; i < 2; i++) { try { @@ -142,11 +142,11 @@ export default abstract class SmartChargingIntegration { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Add the active property to tags - const result = await global.database.getCollection(tenant.id, 'tags').updateMany( - {}, - { $set: { 'active': true } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - module: MODULE_NAME, method: 'migrateTenant', - action: ServerAction.MIGRATION, - message: `${result.modifiedCount} Tag(s) have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - // Remove deleted property from tags - await global.database.getCollection(tenant.id, 'tags').updateMany( - { }, - { $unset: { 'deleted': '' } } - ); - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AddActivePropertyToTagsTask'; - } -} diff --git a/src/migration/tasks/AddConsumptionAmpsToConsumptionsTask.ts b/src/migration/tasks/AddConsumptionAmpsToConsumptionsTask.ts deleted file mode 100644 index 45320b94e7..0000000000 --- a/src/migration/tasks/AddConsumptionAmpsToConsumptionsTask.ts +++ /dev/null @@ -1,58 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import { Voltage } from '../../types/ChargingStation'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddInstantAmpsToConsumptionsTask'; - -export default class AddConsumptionAmpsToConsumptionsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let modifiedCount = 0; - const result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { - 'consumptionAmps': { $exists: false }, - }, - [ - { - '$set': { - 'consumptionAmps': { '$divide': ['$consumptionWh', Voltage.VOLTAGE_230] }, - } - } - ] - ); - modifiedCount += result.modifiedCount; - // Log in the default tenant - if (modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${modifiedCount} Consumptions have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AddConsumptionAmpsToConsumptionsTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AddCreatedPropertiesToTagTask.ts b/src/migration/tasks/AddCreatedPropertiesToTagTask.ts deleted file mode 100644 index 496e7dbe39..0000000000 --- a/src/migration/tasks/AddCreatedPropertiesToTagTask.ts +++ /dev/null @@ -1,73 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import UserStorage from '../../storage/mongodb/UserStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddCreatedPropertiesToTagTask'; - -export default class AddCreatedPropertiesToTagTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - const users = await UserStorage.getUsers(tenant.id, { - issuer: true - }, Constants.DB_PARAMS_MAX_LIMIT); - const tagCollection = global.database.getCollection(tenant.id, 'tags'); - let counter = 0; - for (const user of users.result) { - if (user.createdOn) { - // Get the User's Tags with no Created On date - const tags = await tagCollection.find({ - userID: Utils.convertToObjectID(user.id), - createdOn: null - }).toArray(); - if (!Utils.isEmptyArray(tags)) { - for (const tag of tags) { - await tagCollection.updateOne( - { _id: tag._id }, - { - $set: { - createdOn: user.createdOn, - createdBy: user.createdBy ? Utils.convertToObjectID(user.createdBy.id) : null - } - }, - { upsert: false } - ); - counter++; - } - } - } - } - // Log in the default tenant - if (counter > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${counter} Tags's created properties have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AddCreatedPropertiesToTagTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AddDefaultPropertyToTagsTask.ts b/src/migration/tasks/AddDefaultPropertyToTagsTask.ts deleted file mode 100644 index d66fda442d..0000000000 --- a/src/migration/tasks/AddDefaultPropertyToTagsTask.ts +++ /dev/null @@ -1,51 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddDefaultPropertyToTagsTask'; - -export default class AddDefaultPropertyToTagsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Add the default property to tags - const result = await global.database.getCollection(tenant.id, 'tags').updateMany( - { - default: null, - issuer: true, - }, - { $set: { default: false } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - module: MODULE_NAME, method: 'migrateTenant', - action: ServerAction.MIGRATION, - message: `${result.modifiedCount} Tag's default properties have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AddDefaultPropertyToTagsTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AddDescriptionToTagsTask.ts b/src/migration/tasks/AddDescriptionToTagsTask.ts deleted file mode 100644 index 1e220710b6..0000000000 --- a/src/migration/tasks/AddDescriptionToTagsTask.ts +++ /dev/null @@ -1,63 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddDescriptionToTagsTask'; - -export default class AddDescriptionToTagsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Add the active property to tags - let updated = 0; - // Get Tags with no Description - const tagsMDB = await global.database.getCollection(tenant.id, 'tags') - .find({ - $or: [ - { description: null }, - { description: '' } - ], - issuer: true, - }).toArray(); - if (!Utils.isEmptyArray(tagsMDB)) { - for (const tagMDB of tagsMDB) { - await global.database.getCollection(tenant.id, 'tags').findOneAndUpdate( - { _id: tagMDB._id }, - { $set: { description: `Badge ID '${tagMDB._id}'` } } - ); - updated++; - } - } - // Log in the default tenant - if (updated > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - module: MODULE_NAME, method: 'migrateTenant', - action: ServerAction.MIGRATION, - message: `${updated} Tag(s) description have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.1'; - } - - getName(): string { - return 'AddDescriptionToTagsTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AddInactivityStatusInTransactionsTask.ts b/src/migration/tasks/AddInactivityStatusInTransactionsTask.ts deleted file mode 100644 index 5364029bf7..0000000000 --- a/src/migration/tasks/AddInactivityStatusInTransactionsTask.ts +++ /dev/null @@ -1,77 +0,0 @@ -import Constants from '../../utils/Constants'; -import DatabaseUtils from '../../storage/mongodb/DatabaseUtils'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Transaction from '../../types/Transaction'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddInactivityStatusInTransactionsTask'; - -export default class AddInactivityStatusInTransactionsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let modifiedCount = 0; - // Get all transactions - const aggregation = []; - aggregation.push({ - $match: { - 'stop': { $exists: true }, - 'stop.inactivityStatus': { $exists: false } - } - }); - // Add Charge Box - DatabaseUtils.pushChargingStationLookupInAggregation({ - tenantID: tenant.id, aggregation: aggregation, localField: 'chargeBoxID', - foreignField: '_id', asField: 'chargeBox', oneToOneCardinality: true, oneToOneCardinalityNotNull: false - }); - DatabaseUtils.pushConvertObjectIDToString(aggregation, 'chargeBox.siteAreaID'); - // Call - const transactionsMDB: Transaction[] = await global.database.getCollection(tenant.id, 'transactions') - .aggregate(aggregation).toArray(); - // Set the Inactivity Status - for (const transactionMDB of transactionsMDB) { - // Init extra inactivity - transactionMDB.stop.inactivityStatus = Utils.getInactivityStatusLevel( - transactionMDB.chargeBox, transactionMDB.connectorId, - transactionMDB.stop.totalInactivitySecs + transactionMDB.stop.extraInactivitySecs); - // Update - await global.database.getCollection(tenant.id, 'transactions').findOneAndUpdate( - { '_id': transactionMDB['_id'] }, - { $set: { 'stop.inactivityStatus': transactionMDB.stop.inactivityStatus } }, - { upsert: true, returnDocument: 'after' } - ); - modifiedCount++; - } - // Log in the default tenant - if (modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${modifiedCount} Transactions' inactivity status have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AddInactivityStatusInTransactions'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AddIssuerFieldTask.ts b/src/migration/tasks/AddIssuerFieldTask.ts deleted file mode 100644 index 0af4b905ff..0000000000 --- a/src/migration/tasks/AddIssuerFieldTask.ts +++ /dev/null @@ -1,50 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddIssuerFieldTask'; - -export default class AddIssuerFieldTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - await this.migrateTenant(Constants.DEFAULT_TENANT, Constants.DEFAULT_TENANT, 'users'); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant.id, tenant.name, 'chargingstations'); - await this.migrateTenant(tenant.id, tenant.name, 'companies'); - await this.migrateTenant(tenant.id, tenant.name, 'sites'); - await this.migrateTenant(tenant.id, tenant.name, 'siteareas'); - await this.migrateTenant(tenant.id, tenant.name, 'users'); - await this.migrateTenant(tenant.id, tenant.name, 'transactions'); - } - } - - async migrateTenant(tenantId: string, tenantName: string, collectionName: string): Promise { - // Add the property to the collection - const result = await global.database.getCollection(tenantId, collectionName).updateMany( - { - 'issuer': { $exists: false } - }, - { $set: { 'issuer': true } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.modifiedCount} Object(s) has been updated in the collection '${collectionName}' of Tenant '${tenantName}'` - }); - } - } - - getVersion(): string { - return '1.3'; - } - - getName(): string { - return 'AddIssuerFieldTask'; - } -} diff --git a/src/migration/tasks/AddLastChangePropertiesToBadgeTask.ts b/src/migration/tasks/AddLastChangePropertiesToBadgeTask.ts deleted file mode 100644 index d67b81dddf..0000000000 --- a/src/migration/tasks/AddLastChangePropertiesToBadgeTask.ts +++ /dev/null @@ -1,71 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import UserStorage from '../../storage/mongodb/UserStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddLastChangePropertiesToBadgeTask'; - -export default class AddLastChangePropertiesToBadgeTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - const users = await UserStorage.getUsers(tenant.id, { - issuer: true, - }, Constants.DB_PARAMS_MAX_LIMIT); - const tagCollection = global.database.getCollection(tenant.id, 'tags'); - let counter = 0; - for (const user of users.result) { - const tags = await tagCollection.find({ 'userID': Utils.convertToObjectID(user.id), lastChangedOn: null }).toArray(); - if (!Utils.isEmptyArray(tags)) { - for (const tag of tags) { - const lastChangedOn = user.lastChangedOn ? user.lastChangedOn : user.createdOn; - const lastChangedBy = user.lastChangedBy ? user.lastChangedBy : user.createdBy; - await tagCollection.updateOne( - { - '_id': tag._id - }, - { - $set: { - 'lastChangedOn': lastChangedOn ? lastChangedOn : new Date(), - 'lastChangedBy': lastChangedBy ? Utils.convertToObjectID(lastChangedBy.id) : null - } - }, - { upsert: false } - ); - counter++; - } - } - } - // Log in the default tenant - if (counter > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${counter} Tags's last changed properties have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.1'; - } - - getName(): string { - return 'AddLastChangePropertiesToBadgeTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AddLastChangedOnToCarCatalogTask.ts b/src/migration/tasks/AddLastChangedOnToCarCatalogTask.ts deleted file mode 100644 index 6cdfacd6c2..0000000000 --- a/src/migration/tasks/AddLastChangedOnToCarCatalogTask.ts +++ /dev/null @@ -1,48 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddLastChangedOnToCarCatalogTask'; - -export default class AddLastChangedOnToCarCatalogTask extends MigrationTask { - async migrate(): Promise { - try { - const result = await global.database.getCollection(Constants.DEFAULT_TENANT, 'carcatalogs').updateMany({ - 'lastChangedOn': { $exists: false } - }, - [ - { '$set': { 'lastChangedOn': '$createdOn' } } - ]); - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrate', - message: `${result.modifiedCount} Car(s) catalog(s) have been updated in the default tenant` - }); - } - } catch (error) { - await Logging.logError({ - tenantID: Constants.DEFAULT_TENANT, - module: MODULE_NAME, method: 'migrate', - action: ServerAction.MIGRATION, - message: `Error while updating the Cars catalog: ${error.message}`, - detailedMessages: { error: error.message, stack: error.stack } - }); - } - } - - getVersion(): string { - return '1.0'; - } - - isAsynchronous(): boolean { - return true; - } - - getName(): string { - return 'AddLastChangedOnToCarCatalogTask'; - } -} diff --git a/src/migration/tasks/AddNotificationsFlagsToUsersTask.ts b/src/migration/tasks/AddNotificationsFlagsToUsersTask.ts deleted file mode 100644 index c6fc060acf..0000000000 --- a/src/migration/tasks/AddNotificationsFlagsToUsersTask.ts +++ /dev/null @@ -1,99 +0,0 @@ -import User, { UserRole } from '../../types/User'; - -import Constants from '../../utils/Constants'; -import MigrationTask from '../MigrationTask'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import UserNotifications from '../../types/UserNotifications'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -export default class AddNotificationsFlagsToUsersTask extends MigrationTask { - async migrate(): Promise { - // Migrate tenants - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - // Migrate super tenant - await this.migrateSuperTenant(); - } - - async migrateSuperTenant(): Promise { - // Read all users - const users: User[] = await global.database.getCollection(Constants.DEFAULT_TENANT, 'users').aggregate().toArray(); - // Process each user - for (const user of users) { - // Exists? - if (user.notifications && Utils.objectHasProperty(user.notifications, 'sendCarCatalogSynchronizationFailed')) { - continue; - } - // No: Set it - user.notificationsActive = true; - user.notifications = { - sendCarCatalogSynchronizationFailed: user.notifications.sendCarCatalogSynchronizationFailed ? user.notifications.sendCarCatalogSynchronizationFailed : false, - } as UserNotifications; - // Update - await global.database.getCollection(Constants.DEFAULT_TENANT, 'users').findOneAndUpdate( - { '_id': user['_id'] }, - { $set: user }, - { upsert: true, returnDocument: 'after' } - ); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Read all users - const users: User[] = await global.database.getCollection(tenant.id, 'users').aggregate().toArray(); - // Process each user - for (const user of users) { - // Keep current setting - user.notifications = { - sendSessionStarted: user.notifications?.sendSessionStarted ? user.notifications.sendSessionStarted : false, - sendOptimalChargeReached: user.notifications?.sendOptimalChargeReached ? user.notifications.sendOptimalChargeReached : false, - sendEndOfCharge: user.notifications?.sendEndOfCharge ? user.notifications.sendEndOfCharge : false, - sendEndOfSession: user.notifications?.sendEndOfSession ? user.notifications.sendEndOfSession : false, - sendUserAccountStatusChanged: user.notifications?.sendUserAccountStatusChanged ? user.notifications.sendUserAccountStatusChanged : false, - sendSessionNotStarted: user.notifications?.sendSessionNotStarted ? user.notifications.sendSessionNotStarted : false, - sendUserAccountInactivity: user.notifications?.sendUserAccountInactivity ? user.notifications.sendUserAccountInactivity : false, - sendPreparingSessionNotStarted: user.notifications?.sendPreparingSessionNotStarted ? user.notifications.sendPreparingSessionNotStarted : false, - sendNewRegisteredUser: user.notifications?.sendNewRegisteredUser ? user.notifications.sendNewRegisteredUser : false, - sendUnknownUserBadged: user.notifications?.sendUnknownUserBadged ? user.notifications.sendUnknownUserBadged : false, - sendChargingStationStatusError: user.notifications?.sendChargingStationStatusError ? user.notifications.sendChargingStationStatusError : false, - sendChargingStationRegistered: user.notifications?.sendChargingStationRegistered ? user.notifications.sendChargingStationRegistered : false, - sendOcpiPatchStatusError: user.notifications?.sendOcpiPatchStatusError ? user.notifications.sendOcpiPatchStatusError : false, - sendOicpPatchStatusError: user.notifications?.sendOicpPatchStatusError ? user.notifications.sendOicpPatchStatusError : false, - sendSmtpError: user.notifications?.sendSmtpError ? user.notifications.sendSmtpError : false, - sendOfflineChargingStations: user.notifications?.sendOfflineChargingStations ? user.notifications.sendOfflineChargingStations : false, - sendBillingSynchronizationFailed: user.notifications?.sendBillingSynchronizationFailed ? user.notifications.sendBillingSynchronizationFailed : false, - sendBillingPeriodicOperationFailed: user.notifications?.sendBillingPeriodicOperationFailed ? user.notifications.sendBillingPeriodicOperationFailed : false, - sendCarCatalogSynchronizationFailed: user.notifications?.sendCarCatalogSynchronizationFailed ? user.notifications.sendCarCatalogSynchronizationFailed : false, - sendComputeAndApplyChargingProfilesFailed: user.notifications?.sendComputeAndApplyChargingProfilesFailed ? - user.notifications.sendComputeAndApplyChargingProfilesFailed : false, - sendEndUserErrorNotification: user.notifications?.sendEndUserErrorNotification ? user.notifications.sendEndUserErrorNotification : false, - sendBillingNewInvoice: user.notifications?.sendBillingNewInvoice ? user.notifications.sendBillingNewInvoice : false, - sendAccountVerificationNotification: user.notifications?.sendAccountVerificationNotification ? user.notifications.sendAccountVerificationNotification : false, - sendAdminAccountVerificationNotification: user.notifications?.sendAdminAccountVerificationNotification ? user.notifications.sendAdminAccountVerificationNotification : false, - }; - // Add new prop - if (user.role === UserRole.ADMIN) { - user.notifications.sendEndUserErrorNotification = true; - user.notifications.sendAdminAccountVerificationNotification = true; - } - // Update - await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( - { '_id': user['_id'] }, - { $set: user }, - { upsert: true, returnDocument: 'after' } - ); - } - } - - getVersion(): string { - return '1.6'; - } - - getName(): string { - return 'AddNotificationsFlagsToUsersTask'; - } -} diff --git a/src/migration/tasks/AddSensitiveDataInSettingsTask.ts b/src/migration/tasks/AddSensitiveDataInSettingsTask.ts deleted file mode 100644 index ee42afa1cc..0000000000 --- a/src/migration/tasks/AddSensitiveDataInSettingsTask.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { PricingSettingsType, RefundSettingsType } from '../../types/Setting'; - -import Constants from '../../utils/Constants'; -import Cypher from '../../utils/Cypher'; -import MigrationTask from '../MigrationTask'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import global from '../../types/GlobalType'; - -export default class AddSensitiveDataInSettingsTask extends MigrationTask { - public async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - public async migrateTenant(tenant: Tenant): Promise { - // Read all Settings - const settings: any = await global.database.getCollection(tenant.id, 'settings') - .aggregate([{ - $match: { - 'sensitiveData': { $exists: false } - } - }]) - .toArray(); - // Process each setting - for (const setting of settings) { - // Add sensitiveData property if not present - setting.sensitiveData = []; - // Concur - if (setting.content.type === RefundSettingsType.CONCUR) { - setting.sensitiveData = ['content.concur.clientSecret']; - // Encrypt - if (setting.content.concur.clientSecret) { - setting.content.concur.clientSecret = await Cypher.encrypt(tenant.id, setting.content.concur.clientSecret); - } else { - setting.content.concur.clientSecret = ''; - } - // Convergent Charging - } else if (setting.content.type === PricingSettingsType.CONVERGENT_CHARGING) { - setting.sensitiveData = ['content.convergentCharging.password']; - if (setting.content.convergentCharging.password) { - setting.content.convergentCharging.password = await Cypher.encrypt(tenant.id, setting.content.convergentCharging.password); - } else { - setting.content.convergentCharging.password = ''; - } - } - // Update - await global.database.getCollection(tenant.id, 'settings').findOneAndUpdate( - { '_id': setting._id }, - { $set: setting }, - { upsert: true, returnDocument: 'after' } - ); - } - } - - public getVersion(): string { - return '1.0'; - } - - public getName(): string { - return 'AddSensitiveDataInSettings'; - } -} diff --git a/src/migration/tasks/AddSiteAreaIDToAssetConsumptionTask.ts b/src/migration/tasks/AddSiteAreaIDToAssetConsumptionTask.ts deleted file mode 100644 index 549f58933e..0000000000 --- a/src/migration/tasks/AddSiteAreaIDToAssetConsumptionTask.ts +++ /dev/null @@ -1,61 +0,0 @@ -import AssetStorage from '../../storage/mongodb/AssetStorage'; -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddSiteAreaIDToAssetConsumptionTask'; - -export default class AddSiteAreaIDToAssetConsumptionTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let modifiedCount = 0; - // Get Assets - const assets = await AssetStorage.getAssets(tenant.id, { withSiteArea: true }, Constants.DB_PARAMS_MAX_LIMIT); - for (const asset of assets.result) { - const result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { - assetID: Utils.convertToObjectID(asset.id), - }, - { - $set: { - siteAreaID: Utils.convertToObjectID(asset.siteAreaID), - siteID: Utils.convertToObjectID(asset.siteArea.siteID), - } - } - ); - modifiedCount += result.modifiedCount; - } - // Log in the default tenant - if (modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${modifiedCount} Consumptions have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AddSiteAreaIDToAssetConsumptionTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AddSiteAreaLimitToConsumptionsTask.ts b/src/migration/tasks/AddSiteAreaLimitToConsumptionsTask.ts deleted file mode 100644 index 625e1820a1..0000000000 --- a/src/migration/tasks/AddSiteAreaLimitToConsumptionsTask.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { SiteAreaLimitSource, Voltage } from '../../types/ChargingStation'; - -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import SiteAreaStorage from '../../storage/mongodb/SiteAreaStorage'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddSiteAreaLimitToConsumptionsTask'; - -export default class AddSiteAreaLimitToConsumptionsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let modifiedCount = 0; - // Get Charging Stations - const siteAreas = await SiteAreaStorage.getSiteAreas(tenant.id, { withChargingStations: true }, Constants.DB_PARAMS_MAX_LIMIT); - for (const siteArea of siteAreas.result) { - let limitSiteAreaWatts = 0; - let limitChargingStationsWatts = 0; - if (siteArea.maximumPower) { - limitSiteAreaWatts = siteArea.maximumPower; - } - // Compute charging station power - for (const chargingStation of siteArea.chargingStations) { - const limitAmps = Utils.getChargingStationAmperage(chargingStation); - limitChargingStationsWatts += Utils.convertAmpToWatt(chargingStation, null, 0, limitAmps); - } - // Update Consumption - limitSiteAreaWatts = limitSiteAreaWatts ? limitSiteAreaWatts : limitChargingStationsWatts; - const result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { - siteAreaID: Utils.convertToObjectID(siteArea.id), - }, - { - $set: { - limitSiteAreaAmps: limitSiteAreaWatts / Voltage.VOLTAGE_230, - limitSiteAreaWatts: limitSiteAreaWatts, - limitSiteAreaSource: SiteAreaLimitSource.CHARGING_STATIONS - } - } - ); - modifiedCount += result.modifiedCount; - // Update Site Area - await global.database.getCollection(tenant.id, 'siteareas').updateOne( - { - '_id': Utils.convertToObjectID(siteArea.id) - }, - { - $set: { - 'voltage': Voltage.VOLTAGE_230, - 'numberOfPhases': 3, - 'maximumPower': limitSiteAreaWatts, - } - }, - { upsert: false } - ); - } - // Log in the default tenant - if (modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${modifiedCount} Consumptions have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.3'; - } - - getName(): string { - return 'AddSiteAreaLimitToConsumptions'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AddSiteIDToAssetTask.ts b/src/migration/tasks/AddSiteIDToAssetTask.ts deleted file mode 100644 index c2c24c699a..0000000000 --- a/src/migration/tasks/AddSiteIDToAssetTask.ts +++ /dev/null @@ -1,75 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import SiteAreaStorage from '../../storage/mongodb/SiteAreaStorage'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddSiteIDToAssetTask'; - -export default class AddSiteIDToAssetTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let modifiedCount = 0; - // Get Assets - const siteAreas = await SiteAreaStorage.getSiteAreas(tenant.id, {}, Constants.DB_PARAMS_MAX_LIMIT); - for (const siteArea of siteAreas.result) { - const result = await global.database.getCollection(tenant.id, 'assets').updateMany( - { - siteAreaID: Utils.convertToObjectID(siteArea.id), - }, - { - $set: { - siteID: Utils.convertToObjectID(siteArea.siteID), - } - } - ); - modifiedCount += result.modifiedCount; - } - // Delete siteIDs for assets without site area - const result = await global.database.getCollection(tenant.id, 'assets').updateMany( - { - siteAreaID: { - $exists: true, - $eq: null - } - }, - { - $set: { - siteID: null, - } - } - ); - modifiedCount += result.modifiedCount; - // Log in the default tenant - if (modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${modifiedCount} Assets have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AddSiteIDToAssetTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AddSiteIDToChargingStationTask.ts b/src/migration/tasks/AddSiteIDToChargingStationTask.ts deleted file mode 100644 index a45bb1c6d9..0000000000 --- a/src/migration/tasks/AddSiteIDToChargingStationTask.ts +++ /dev/null @@ -1,76 +0,0 @@ -import ChargingStationStorage from '../../storage/mongodb/ChargingStationStorage'; -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import SiteAreaStorage from '../../storage/mongodb/SiteAreaStorage'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddSiteIDToChargingStationTask'; - -export default class AddSiteIDToChargingStationTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let modifiedCount = 0; - // Get Site Areas - const siteAreas = await SiteAreaStorage.getSiteAreas(tenant.id, {}, Constants.DB_PARAMS_MAX_LIMIT); - for (const siteArea of siteAreas.result) { - const result = await global.database.getCollection(tenant.id, 'chargingstations').updateMany( - { - siteAreaID: Utils.convertToObjectID(siteArea.id), - }, - { - $set: { - siteID: Utils.convertToObjectID(siteArea.siteID), - } - } - ); - modifiedCount += result.modifiedCount; - } - // Delete siteIDs for charging stations without site area - const result = await global.database.getCollection(tenant.id, 'chargingstations').updateMany( - { - siteAreaID: { - $exists: true, - $eq: null - } - }, - { - $set: { - siteID: null, - } - } - ); - modifiedCount += result.modifiedCount; - // Log in the default tenant - if (modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${modifiedCount} Charging stations have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.1'; - } - - getName(): string { - return 'AddSiteIDToChargingStationTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AddTagTypeTask.ts b/src/migration/tasks/AddTagTypeTask.ts deleted file mode 100644 index b310d70afe..0000000000 --- a/src/migration/tasks/AddTagTypeTask.ts +++ /dev/null @@ -1,53 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tag from '../../types/Tag'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddTagTypeTask'; - -export default class AddTagTypeTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Add the property to the collection - const result = await global.database.getCollection(tenant.id, 'tags').updateMany( - { - 'internal': { $exists: false } - }, - { $set: { 'internal': false } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.modifiedCount} Tag(s) have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - // Remove tagIDs from User - await global.database.getCollection(tenant.id, 'users').updateMany( - { }, - { $unset: { 'tagIDs': '', 'mobileLastChanged': '', 'lastLogin': '', 'image': '' } }, - { upsert: false } - ); - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AddTagTypeTask'; - } -} diff --git a/src/migration/tasks/AddTransactionRefundStatusTask.ts b/src/migration/tasks/AddTransactionRefundStatusTask.ts deleted file mode 100644 index 7e47b5d79c..0000000000 --- a/src/migration/tasks/AddTransactionRefundStatusTask.ts +++ /dev/null @@ -1,48 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { RefundStatus } from '../../types/Refund'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddTransactionRefundStatusTask'; - -export default class AddTransactionRefundStatusTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Add the status property to the refunded transactions - const result = await global.database.getCollection(tenant.id, 'transactions').updateMany( - { - 'refundData': { $exists: true }, - 'refundData.status': { $exists: false } - }, - { $set: { 'refundData.status': RefundStatus.SUBMITTED } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.modifiedCount} Refunded Transaction(s) has been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AddTransactionRefundStatusTask'; - } -} diff --git a/src/migration/tasks/AddUserInTransactionsTask.ts b/src/migration/tasks/AddUserInTransactionsTask.ts deleted file mode 100644 index f822d1bcd2..0000000000 --- a/src/migration/tasks/AddUserInTransactionsTask.ts +++ /dev/null @@ -1,75 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import UserStorage from '../../storage/mongodb/UserStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'RecomputeAllTransactionsConsumptionsTask'; - -export default class AddUserInTransactionsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let success = 0; - // Get transactions - const transactionsMDB = await global.database.getCollection(tenant.id, 'transactions') - .aggregate([ - { $match: { userID: null } } - ]).toArray(); - if (!Utils.isEmptyArray(transactionsMDB)) { - await Logging.logInfo({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${transactionsMDB.length} Transaction(s) are going to be assigned an user in Tenant ${Utils.buildTenantName(tenant)}...`, - }); - for (const transactionMDB of transactionsMDB) { - // Get the user with tag - const user = await UserStorage.getUserByTagId(tenant.id, transactionMDB.tagID); - // Assign and Save the transaction - if (user && user.name !== 'Unknown') { - transactionMDB.userID = Utils.convertToObjectID(user.id); - await global.database.getCollection(tenant.id, 'transactions').updateOne( - { - '_id': transactionMDB._id - }, - { - $set: { - 'userID': Utils.convertToObjectID(user.id), - } - }, - { upsert: false } - ); - success++; - } - } - await Logging.logInfo({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${success} Transaction(s) have been assigned an user in Tenant ${Utils.buildTenantName(tenant)}...`, - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AddUserInTransactionsTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/AlignTagsWithUsersIssuerTask.ts b/src/migration/tasks/AlignTagsWithUsersIssuerTask.ts deleted file mode 100644 index 6d46661cd0..0000000000 --- a/src/migration/tasks/AlignTagsWithUsersIssuerTask.ts +++ /dev/null @@ -1,63 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import { UpdateWriteOpResult } from 'mongodb'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AlignTagsWithUsersIssuerTask'; - -export default class AlignTagsWithUsersIssuerTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let updated = 0; - let result: UpdateWriteOpResult; - // Get users - const usersMDB = await global.database.getCollection(tenant.id, 'users').find({ - issuer: true, - }).toArray(); - if (!Utils.isEmptyArray(usersMDB)) { - // Update tags - for (const userMDB of usersMDB) { - result = await global.database.getCollection(tenant.id, 'tags').updateMany( - { - userID: userMDB._id - }, - { - $set: { - issuer: userMDB.issuer, - } - } - ); - updated += result.modifiedCount; - } - await Logging.logInfo({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${updated} Tag's issuer properties have been updated in Tenant ${Utils.buildTenantName(tenant)}`, - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'AlignTagsWithUsersIssuerTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/ChangeAssetIssuerFieldTask.ts b/src/migration/tasks/ChangeAssetIssuerFieldTask.ts deleted file mode 100644 index fae9b875a1..0000000000 --- a/src/migration/tasks/ChangeAssetIssuerFieldTask.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'ChangeAssetIssuerFieldTask'; - -export default class ChangeAssetIssuerFieldTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant.id, tenant.name); - } - } - - async migrateTenant(tenantId: string, tenantName: string): Promise { - // Set to true for all assets - const result = await global.database.getCollection(tenantId, 'assets').updateMany( - {}, - { $set: { 'issuer': true } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.modifiedCount} Assets of Tenant '${tenantName}' has been updated to issuer true.` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'ChangeAssetIssuerFieldTask'; - } -} diff --git a/src/migration/tasks/ChangeCryptoKeyTask.ts b/src/migration/tasks/ChangeCryptoKeyTask.ts deleted file mode 100644 index 789cd5f1e0..0000000000 --- a/src/migration/tasks/ChangeCryptoKeyTask.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { CryptoSettings, CryptoSettingsType, TechnicalSettings } from '../../types/Setting'; - -import Configuration from '../../utils/Configuration'; -import Constants from '../../utils/Constants'; -import Cypher from '../../utils/Cypher'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import SettingStorage from '../../storage/mongodb/SettingStorage'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; - -const MODULE_NAME = 'ChangeCryptoKeyTask'; - -export default class ChangeCryptoKeyTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Skip default tenant - if (tenant.id === Constants.DEFAULT_TENANT) { - return; - } - // Get crypto key settings from config file & db - const historicalCryptoSettings = Configuration.getCryptoConfig(); - const currentCryptoSettings = await SettingStorage.getCryptoSettings(tenant.id); - if (!currentCryptoSettings || currentCryptoSettings?.crypto?.key === historicalCryptoSettings.key) { - // If they match, generate a new key with the default algorithm - const keySettingToSave: CryptoSettings = { - id: currentCryptoSettings?.id, - identifier: TechnicalSettings.CRYPTO, - type: CryptoSettingsType.CRYPTO, - crypto: { - formerKey: currentCryptoSettings?.crypto?.key, - formerKeyProperties: currentCryptoSettings?.crypto?.keyProperties, - key: Utils.generateRandomKey(Utils.getDefaultKeyProperties()), - keyProperties: Utils.getDefaultKeyProperties(), - migrationToBeDone: true - } - }; - await SettingStorage.saveCryptoSettings(tenant.id, keySettingToSave); - // Migrate sensitive data to the new key - await Cypher.handleCryptoSettingsChange(tenant.id); - // Log in the default tenant - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, - method: 'migrateTenant', - message: `Crypto settings have been updated in Tenant '${tenant.name}'` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'ChangeCryptoKeyTask'; - } -} diff --git a/src/migration/tasks/CleanUpCarUsersWithDeletedUsersTask.ts b/src/migration/tasks/CleanUpCarUsersWithDeletedUsersTask.ts deleted file mode 100644 index ef11efe34a..0000000000 --- a/src/migration/tasks/CleanUpCarUsersWithDeletedUsersTask.ts +++ /dev/null @@ -1,77 +0,0 @@ -import CarStorage from '../../storage/mongodb/CarStorage'; -import { CarType } from '../../types/Car'; -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import UserStorage from '../../storage/mongodb/UserStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'CleanUpCarUsersWithDeletedUsersTask'; - -export default class CleanUpCarUsersWithDeletedUsersTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let carUsersCounter = 0, carsCounter = 0; - // Get users cars - const carUsersMDB = await global.database.getCollection(tenant.id, 'carusers').find().toArray(); - if (!Utils.isEmptyArray(carUsersMDB)) { - for (const carUserMDB of carUsersMDB) { - // Get the User - const user = await UserStorage.getUser(tenant.id, carUserMDB.userID.toString()); - if (!user) { - // Owner? - if (carUserMDB.owner) { - // Get the Private Cars of the Deleted User - const privateCar = await CarStorage.getCar(tenant, carUserMDB.carID.toString(), { type: CarType.PRIVATE }); - if (privateCar) { - // Delete All Private Cars assignment - carUsersCounter += await CarStorage.deleteCarUsersByCarID(tenant, privateCar.id); - // Delete Private Car - await CarStorage.deleteCar(tenant, privateCar.id); - carsCounter++; - } else { - // Delete Car assignment - await CarStorage.deleteCarUser(tenant, carUserMDB._id); - carUsersCounter++; - } - } else { - // Delete Car assignment - await CarStorage.deleteCarUser(tenant, carUserMDB._id); - carUsersCounter++; - } - } - } - } - // Log in the default tenant - if (carUsersCounter > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${carUsersCounter} CarUser(s) and ${carsCounter} Car(s) have been deleted in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - isAsynchronous(): boolean { - return true; - } - - getName(): string { - return 'CleanUpCarUsersWithDeletedUsersTask'; - } -} diff --git a/src/migration/tasks/CleanUpLogicallyDeletedUsersTask.ts b/src/migration/tasks/CleanUpLogicallyDeletedUsersTask.ts deleted file mode 100644 index f6c60b826d..0000000000 --- a/src/migration/tasks/CleanUpLogicallyDeletedUsersTask.ts +++ /dev/null @@ -1,79 +0,0 @@ -import CarStorage from '../../storage/mongodb/CarStorage'; -import { CarType } from '../../types/Car'; -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import UserStorage from '../../storage/mongodb/UserStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'CleanUpLogicallyDeletedUsersTask'; - -export default class CleanUpLogicallyDeletedUsersTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Get logically deleted users - const deletedUsersMDB = await global.database.getCollection(tenant.id, 'users').find({ - deleted : true - }).toArray(); - let counter = 0; - for (const deletedUserMDB of deletedUsersMDB) { - const userID = deletedUserMDB._id.toString(); - const carUsers = await CarStorage.getCarUsers(tenant, { userIDs : [userID] }, Constants.DB_PARAMS_MAX_LIMIT); - if (!Utils.isEmptyArray(carUsers.result)) { - for (const carUser of carUsers.result) { - // Owner? - if (carUser.owner) { - // Get the Private Cars of the Deleted User - const privateCar = await CarStorage.getCar(tenant, carUser.carID, { type: CarType.PRIVATE }); - if (privateCar) { - // Delete All Private Cars assignment - await CarStorage.deleteCarUsersByCarID(tenant, privateCar.id); - // Delete Private Car - await CarStorage.deleteCar(tenant, privateCar.id); - } else { - // Delete Car assignment - await CarStorage.deleteCarUser(tenant, carUser.id); - } - } else { - // Delete Car assignment - await CarStorage.deleteCarUser(tenant, carUser.id); - } - } - } - // Delete the User physically - await UserStorage.deleteUser(tenant.id, userID); - counter++; - } - // Log in the default tenant - if (counter > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${counter} User(s) have been deleted in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - isAsynchronous(): boolean { - return true; - } - - getName(): string { - return 'CleanUpLogicallyDeletedUsersTask'; - } -} diff --git a/src/migration/tasks/CleanupMeterValuesTask.ts b/src/migration/tasks/CleanupMeterValuesTask.ts deleted file mode 100644 index 35cb186c60..0000000000 --- a/src/migration/tasks/CleanupMeterValuesTask.ts +++ /dev/null @@ -1,77 +0,0 @@ -import Constants from '../../utils/Constants'; -import DatabaseUtils from '../../storage/mongodb/DatabaseUtils'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; -import moment from 'moment'; - -const MODULE_NAME = 'CleanupMeterValuesTask'; - -export default class CleanupMeterValuesTask extends MigrationTask { - public totalCount: any; - public done: any; - public startTime: any; - - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - this.totalCount = 0; - this.done = 0; - this.startTime = moment(); - // Create Aggregation - const aggregation = []; - // Add Charging Station - aggregation.push({ - $lookup: { - from: DatabaseUtils.getCollectionName(tenant.id, 'transactions'), - localField: 'transactionId', - foreignField: '_id', - as: 'transactions' - } - }); - aggregation.push({ - '$match': { 'transactions': { '$eq': [] } } - }); - // Read all transactions - const meterValuesMDB = await global.database.getCollection(tenant.id, 'metervalues') - .aggregate(aggregation).toArray(); - // Delete - for (const meterValueMDB of meterValuesMDB) { - // Delete - await global.database.getCollection(tenant.id, 'metervalues') - .findOneAndDelete({ '_id': meterValueMDB._id }); - } - // Log - if (!Utils.isEmptyArray(meterValuesMDB)) { - await Logging.logWarning({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrate', - message: `Tenant ${Utils.buildTenantName(tenant)} (${tenant.id}): ${meterValuesMDB.length} orphan Meter Values have been deleted` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'CleanupMeterValuesTask'; - } - - isAsynchronous(): boolean { - return true; - } -} - diff --git a/src/migration/tasks/CleanupOrphanBadgeTask.ts b/src/migration/tasks/CleanupOrphanBadgeTask.ts deleted file mode 100644 index a56696b08e..0000000000 --- a/src/migration/tasks/CleanupOrphanBadgeTask.ts +++ /dev/null @@ -1,52 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import UserStorage from '../../storage/mongodb/UserStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'CleanupOrphanBadgeTask'; - -export default class CleanupOrphanBadgeTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Delete the property from the collection - const tagCollection = global.database.getCollection(tenant.id, 'tags'); - const tags = await tagCollection.find().toArray(); - - let counter = 0; - for (const tag of tags) { - const user = await UserStorage.getUserByTagId(tenant.id, tag._id); - if (!user) { - await tagCollection.deleteOne({ _id: tag._id }); - counter++; - } - } - // Log in the default tenant - if (counter > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${counter} Tags(s) have been deleted in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'CleanupOrphanBadgeTask'; - } -} diff --git a/src/migration/tasks/CleanupSiteAreasTask.ts b/src/migration/tasks/CleanupSiteAreasTask.ts deleted file mode 100644 index 2d82001931..0000000000 --- a/src/migration/tasks/CleanupSiteAreasTask.ts +++ /dev/null @@ -1,50 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'CleanupSiteAreasTask'; - -export default class CleanupSiteAreasTask extends MigrationTask { - - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Add Charging Station - const result = await global.database.getCollection(tenant.id, 'siteareas').deleteMany({ - 'name': null - }); - - // Log in the default tenant - if (result.deletedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.deletedCount} Site area(s) have been deleted in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'CleanupSiteAreasTask'; - } - - isAsynchronous(): boolean { - return true; - } -} - diff --git a/src/migration/tasks/DeleteChargingStationPropertiesTask.ts b/src/migration/tasks/DeleteChargingStationPropertiesTask.ts deleted file mode 100644 index f83f639bc7..0000000000 --- a/src/migration/tasks/DeleteChargingStationPropertiesTask.ts +++ /dev/null @@ -1,45 +0,0 @@ -import ChargingStation from '../../types/ChargingStation'; -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'DeleteChargingStationPropertiesTask'; - -export default class DeleteChargingStationPropertiesTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Delete the properties from the collection - const result = await global.database.getCollection(tenant.id, 'chargingstations').updateMany( - {}, - { $unset: { currentServerLocalIPAddress: '', currentServerLocalIPAddressPort: '' } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.modifiedCount} Charging Stations' properties have been deleted in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.1'; - } - - getName(): string { - return 'DeleteChargingStationPropertiesTask'; - } -} diff --git a/src/migration/tasks/FixedConsumptionRoundedPriceTask.ts b/src/migration/tasks/FixedConsumptionRoundedPriceTask.ts deleted file mode 100644 index a74c80968e..0000000000 --- a/src/migration/tasks/FixedConsumptionRoundedPriceTask.ts +++ /dev/null @@ -1,57 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'FixedConsumptionRoundedPriceTask'; - -export default class FixedConsumptionRoundedPriceTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let modifiedCount = 0; - const result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { }, - [ - { - $set: { - roundedAmount: { - $round: [ '$amount', 2 ] - } - } - } - ] - ); - modifiedCount += result.modifiedCount; - // Log in the default tenant - if (modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${modifiedCount} Consumptions have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'FixedConsumptionRoundedPriceTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/ImportLocalCarCatalogTask.ts b/src/migration/tasks/ImportLocalCarCatalogTask.ts deleted file mode 100644 index 276ed3b339..0000000000 --- a/src/migration/tasks/ImportLocalCarCatalogTask.ts +++ /dev/null @@ -1,250 +0,0 @@ -import { CarCatalog, CarCatalogChargeAlternativeTable, CarCatalogChargeOptionTable, CarCatalogConverter } from '../../types/Car'; - -import CarStorage from '../../storage/mongodb/CarStorage'; -import Constants from '../../utils/Constants'; -import Jimp from 'jimp'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Utils from '../../utils/Utils'; -import { Voltage } from '../../types/ChargingStation'; -import fs from 'fs'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'ImportLocalCarCatalogTask'; - -export default class ImportLocalCarCatalogTask extends MigrationTask { - async migrate(): Promise { - let created = 0; - try { - const cars = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/cars/cars-definition.json`, 'utf8')); - if (!Utils.isEmptyArray(cars)) { - for (const car of cars) { - const chargeStandardTables: CarCatalogConverter[] = []; - const chargeAlternativeTables: CarCatalogChargeAlternativeTable[] = []; - const chargeOptionTables: CarCatalogChargeOptionTable[] = []; - for (const chargeStandard of Object.keys(car.Charge_Standard_Table)) { - const chargeStandardTable: CarCatalogConverter = { - type: chargeStandard, - evsePhaseVolt: car.Charge_Standard_Table[chargeStandard].EVSE_PhaseVolt, - evsePhaseAmp: car.Charge_Standard_Table[chargeStandard].EVSE_PhaseAmp, - evsePhase: car.Charge_Standard_Table[chargeStandard].EVSE_Phase, - evsePhaseVoltCalculated: car.Charge_Standard_Table[chargeStandard].EVSE_Phase === 3 ? Voltage.VOLTAGE_400 : car.Charge_Standard_Table[chargeStandard].EVSE_PhaseVolt, - chargePhaseVolt: car.Charge_Standard_Table[chargeStandard].Charge_PhaseVolt, - chargePhaseAmp: car.Charge_Standard_Table[chargeStandard].Charge_PhaseAmp, - chargePhase: car.Charge_Standard_Table[chargeStandard].Charge_Phase, - chargePower: car.Charge_Standard_Table[chargeStandard].Charge_Power, - chargeTime: car.Charge_Standard_Table[chargeStandard].Charge_Time, - chargeSpeed: car.Charge_Standard_Table[chargeStandard].Charge_Speed, - }; - chargeStandardTables.push(chargeStandardTable); - } - if (car.Charge_Alternative_Table) { - for (const chargeAlternative of Object.keys(car.Charge_Alternative_Table)) { - const chargeAlternativeTable: CarCatalogChargeAlternativeTable = { - type: chargeAlternative, - evsePhaseVolt: car.Charge_Standard_Table[chargeAlternative].EVSE_PhaseVolt, - evsePhaseAmp: car.Charge_Standard_Table[chargeAlternative].EVSE_PhaseAmp, - evsePhase: car.Charge_Standard_Table[chargeAlternative].EVSE_Phase, - chargePhaseVolt: car.Charge_Standard_Table[chargeAlternative].Charge_PhaseVolt, - chargePhaseAmp: car.Charge_Standard_Table[chargeAlternative].Charge_PhaseAmp, - chargePhase: car.Charge_Standard_Table[chargeAlternative].Charge_Phase, - chargePower: car.Charge_Standard_Table[chargeAlternative].Charge_Power, - chargeTime: car.Charge_Standard_Table[chargeAlternative].Charge_Time, - chargeSpeed: car.Charge_Standard_Table[chargeAlternative].Charge_Speed, - }; - chargeAlternativeTables.push(chargeAlternativeTable); - } - } - if (car.Charge_Option_Table) { - for (const chargeOption of Object.keys(car.Charge_Option_Table)) { - const chargeAlternativeTable: CarCatalogChargeOptionTable = { - type: chargeOption, - evsePhaseVolt: car.Charge_Standard_Table[chargeOption].EVSE_PhaseVolt, - evsePhaseAmp: car.Charge_Standard_Table[chargeOption].EVSE_PhaseAmp, - evsePhase: car.Charge_Standard_Table[chargeOption].EVSE_Phase, - chargePhaseVolt: car.Charge_Standard_Table[chargeOption].Charge_PhaseVolt, - chargePhaseAmp: car.Charge_Standard_Table[chargeOption].Charge_PhaseAmp, - chargePhase: car.Charge_Standard_Table[chargeOption].Charge_Phase, - chargePower: car.Charge_Standard_Table[chargeOption].Charge_Power, - chargeTime: car.Charge_Standard_Table[chargeOption].Charge_Time, - chargeSpeed: car.Charge_Standard_Table[chargeOption].Charge_Speed, - }; - chargeOptionTables.push(chargeAlternativeTable); - } - } - const carCatalog: CarCatalog = { - id: car.Vehicle_ID, - vehicleMake: car.Vehicle_Make, - vehicleModel: car.Vehicle_Model, - vehicleModelVersion: car.Vehicle_Model_Version, - availabilityStatus: car.Availability_Status, - availabilityDateFrom: car.Availability_Date_From, - availabilityDateTo: car.Availability_Date_To, - priceFromDE: car.Price_From_DE, - priceFromDEEstimate: car.Price_From_DE_Estimate, - priceFromNL: car.Price_From_NL, - priceFromNLEstimate: car.Price_From_NL_Estimate, - priceFromUK: car.Price_From_UK, - priceGrantPICGUK: car.Price_Grant_PICG_UK, - priceFromUKEstimate: car.Price_From_UK_Estimate, - drivetrainType: car.Drivetrain_Type, - drivetrainFuel: car.Drivetrain_Fuel, - drivetrainPropulsion: car.Drivetrain_Propulsion, - drivetrainPower: car.Drivetrain_Power, - drivetrainPowerHP: car.Drivetrain_Power_HP, - drivetrainTorque: car.Drivetrain_Torque, - performanceAcceleration: car.Performance_Acceleration, - performanceTopspeed: car.Performance_Topspeed, - rangeWLTP: car.Range_WLTP, - rangeWLTPEstimate: car.Range_WLTP_Estimate, - rangeNEDC: car.Range_NEDC, - rangeNEDCEstimate: car.Range_NEDC_Estimate, - rangeReal: car.Range_Real, - rangeRealMode: car.Range_Real_Mode, - rangeRealWHwy: car.Range_Real_WHwy, - rangeRealWCmb: car.Range_Real_WCmb, - rangeRealWCty: car.Range_Real_WCty, - rangeRealBHwy: car.Range_Real_BHwy, - rangeRealBCmb: car.Range_Real_BCmb, - rangeRealBCty: car.Range_Real_BCty, - efficiencyWLTP: car.Efficiency_WLTP, - efficiencyWLTPFuelEq: car.Efficiency_WLTP_FuelEq, - efficiencyWLTPV: car.Efficiency_WLTP_V, - efficiencyWLTPFuelEqV: car.Efficiency_WLTP_FuelEq_V, - efficiencyWLTPCO2: car.Efficiency_WLTP_CO2, - efficiencyNEDC: car.Efficiency_NEDC, - efficiencyNEDCFuelEq: car.Efficiency_NEDC_FuelEq, - efficiencyNEDCV: car.Efficiency_NEDC_V, - efficiencyNEDCFuelEqV: car.Efficiency_NEDC_FuelEq_V, - efficiencyNEDCCO2: car.Efficiency_NEDC_CO2, - efficiencyReal: car.Efficiency_Real, - efficiencyRealFuelEqV: car.Efficiency_Real_FuelEq_V, - efficiencyRealCO2: car.Efficiency_Real_CO2, - efficiencyRealWHwy: car.Efficiency_Real_WHwy, - efficiencyRealWCmb: car.Efficiency_Real_WCmb, - efficiencyRealWCty: car.Efficiency_Real_WCty, - efficiencyRealBHwy: car.Efficiency_Real_BHwy, - efficiencyRealBCmb: car.Efficiency_Real_BCmb, - efficiencyRealBCty: car.Efficiency_Real_BCty, - chargePlug: car.Charge_Plug, - chargePlugEstimate: car.Charge_Plug_Estimate, - chargePlugLocation: car.Charge_Plug_Location, - chargeStandardPower: car.Charge_Standard_Power, - chargeStandardPhase: car.Charge_Standard_Phase, - chargeStandardPhaseAmp: car.Charge_Standard_PhaseAmp, - chargeStandardChargeTime: car.Charge_Standard_ChargeTime, - chargeStandardChargeSpeed: car.Charge_Standard_ChargeSpeed, - chargeStandardEstimate: car.Charge_Standard_Estimate, - chargeStandardTables: chargeStandardTables, - chargeAlternativePower: car.Charge_Alternative_Power, - chargeAlternativePhase: car.Charge_Alternative_Phase, - chargeAlternativePhaseAmp: car.Charge_Alternative_PhaseAmp, - chargeAlternativeChargeTime: car.Charge_Alternative_ChargeTime, - chargeAlternativeChargeSpeed: car.Charge_Alternative_ChargeSpeed, - chargeAlternativeTables: chargeAlternativeTables, - chargeOptionPower: car.Charge_Option_Power, - chargeOptionPhase: car.Charge_Option_Phase, - chargeOptionPhaseAmp: car.Charge_Option_PhaseAmp, - chargeOptionChargeTime: car.Charge_Option_ChargeTime, - chargeOptionChargeSpeed: car.Charge_Option_ChargeSpeed, - chargeOptionTables: chargeOptionTables, - fastChargePlug: car.Fastcharge_Plug, - fastChargePlugEstimate: car.Fastcharge_Plug_Estimate, - fastChargePlugLocation: car.Fastcharge_Plug_Location, - fastChargePowerMax: car.Fastcharge_Power_Max, - fastChargePowerAvg: car.Fastcharge_Power_Avg, - fastChargeTime: car.Fastcharge_ChargeTime, - fastChargeSpeed: car.Fastcharge_ChargeSpeed, - fastChargeOptional: car.Fastcharge_Optional, - fastChargeEstimate: car.Fastcharge_Estimate, - batteryCapacityUseable: car.Battery_Capacity_Useable, - batteryCapacityFull: car.Battery_Capacity_Full, - batteryCapacityEstimate: car.Battery_Capacity_Estimate, - dimsLength: car.Dims_Length, - dimsWidth: car.Dims_Width, - dimsHeight: car.Dims_Height, - dimsWheelbase: car.Dims_Wheelbase, - dimsWeight: car.Dims_Weight, - dimsBootspace: car.Dims_Bootspace, - dimsBootspaceMax: car.Dims_Bootspace_Max, - dimsTowWeightUnbraked: car.Dims_TowWeight_Braked, - dimsRoofLoadMax: car.Dims_RoofLoad_Max, - miscBody: car.Misc_Body, - miscSegment: car.Misc_Segment, - miscSeats: car.Misc_Seats, - miscRoofrails: car.Misc_Roofrails, - miscIsofix: car.Misc_Isofix, - miscIsofixSeats: car.Misc_Isofix_Seats, - miscTurningCircle: car.Misc_TurningCircle, - euroNCAPRating: car.EuroNCAP_Rating, - euroNCAPYear: car.EuroNCAP_Year, - euroNCAPAdult: car.EuroNCAP_Adult, - euroNCAPChild: car.EuroNCAP_Child, - euroNCAPVRU: car.EuroNCAP_VRU, - euroNCAPSA: car.EuroNCAP_SA, - relatedVehicleIDSuccessor: car.Related_Vehicle_ID_Successor, - eVDBDetailURL: car.EVDB_Detail_URL, - imageURLs: car.Images ? (!Utils.isEmptyArray(car.Images) ? car.Images : [car.Images]) : [], - images: [], - videos: car.Videos, - }; - let imageURLPath = `${global.appRoot}/assets/cars/img/${carCatalog.imageURLs[0]}`; - const thumbImage = (await Jimp.read(imageURLPath)).resize(200, 150); - const thumbImageMIME = thumbImage.getMIME(); - const base64ThumbImage = await thumbImage.getBase64Async(thumbImageMIME); - carCatalog.image = 'data:' + thumbImageMIME + ';base64,' + base64ThumbImage; - await global.database.getCollection(Constants.DEFAULT_TENANT, 'carcatalogs').deleteOne({ - _id: carCatalog.id - }); - await global.database.getCollection(Constants.DEFAULT_TENANT, 'carcatalogimages').deleteMany({ - carID: carCatalog.id - }); - carCatalog.createdOn = new Date(); - carCatalog.lastChangedOn = carCatalog.createdOn; - for (const imageURL of carCatalog.imageURLs) { - imageURLPath = `${global.appRoot}/assets/cars/img/${imageURL}`; - const image = await Jimp.read(imageURLPath); - const imageMIME = image.getMIME(); - const base64Image = await image.getBase64Async(imageMIME); - const encodedImage = 'data:' + imageMIME + ';base64,' + base64Image; - // Save car catalog images - await CarStorage.saveCarImage(carCatalog.id, encodedImage); - } - // Save - await CarStorage.saveCarCatalog(carCatalog); - created++; - } - - } - } catch (error) { - await Logging.logError({ - tenantID: Constants.DEFAULT_TENANT, - module: MODULE_NAME, method: 'migrate', - action: ServerAction.CAR_CATALOG_SYNCHRONIZATION, - message: `Error while importing the Cars: ${error.message}`, - detailedMessages: { error: error.message, stack: error.stack } - }); - } // Log in the default tenant - if (created > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${created} local Car(s) catalog created in the default tenant` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - isAsynchronous(): boolean { - return true; - } - - getName(): string { - return 'ImportLocalCarCatalogTask'; - } -} diff --git a/src/migration/tasks/InitialCarImportTask.ts b/src/migration/tasks/InitialCarImportTask.ts deleted file mode 100644 index 62c3925a3a..0000000000 --- a/src/migration/tasks/InitialCarImportTask.ts +++ /dev/null @@ -1,38 +0,0 @@ -import CarFactory from '../../integration/car/CarFactory'; -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; - -const MODULE_NAME = 'InitialCarImportTask'; - -export default class InitialCarImportTask extends MigrationTask { - async migrate(): Promise { - try { - const carDatabaseImpl = await CarFactory.getCarImpl(); - if (carDatabaseImpl) { - await carDatabaseImpl.synchronizeCarCatalogs(); - } - } catch (error) { - await Logging.logError({ - tenantID: Constants.DEFAULT_TENANT, - module: MODULE_NAME, method: 'migrate', - action: ServerAction.CAR_CATALOG_SYNCHRONIZATION, - message: `Error while importing the Cars: ${error.message}`, - detailedMessages: { error: error.message, stack: error.stack } - }); - } - } - - getVersion(): string { - return '1.6'; - } - - isAsynchronous(): boolean { - return true; - } - - getName(): string { - return 'InitialCarImportTask'; - } -} diff --git a/src/migration/tasks/LogicallyDeleteTagsOfDeletedUsersTask.ts b/src/migration/tasks/LogicallyDeleteTagsOfDeletedUsersTask.ts deleted file mode 100644 index b992132e1b..0000000000 --- a/src/migration/tasks/LogicallyDeleteTagsOfDeletedUsersTask.ts +++ /dev/null @@ -1,67 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import { UpdateWriteOpResult } from 'mongodb'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'LogicallyDeleteTagsOfDeletedUsersTask'; - -export default class LogicallyDeleteTagsOfDeletedUsersTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let updated = 0; - let result: UpdateWriteOpResult; - // Get deleted Users - const users = await global.database.getCollection(tenant.id, 'users').find({ - deleted: true - }).toArray(); - if (!Utils.isEmptyArray(users)) { - for (const user of users) { - // Update - result = await global.database.getCollection(tenant.id, 'tags').updateMany( - { - userID: user._id - }, - { - $set: { - deleted: true, - } - } - ); - updated += result.modifiedCount; - } - } - // Log in the default tenant - if (updated > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${updated} Tag(s) have been marked logically deleted in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'LogicallyDeleteTagsOfDeletedUsersTask'; - } - - isAsynchronous(): boolean { - return true; - } -} - diff --git a/src/migration/tasks/MigrateCoordinatesTask.ts b/src/migration/tasks/MigrateCoordinatesTask.ts deleted file mode 100644 index 59dd8ecfe9..0000000000 --- a/src/migration/tasks/MigrateCoordinatesTask.ts +++ /dev/null @@ -1,196 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'MigrateCoordinatesTask'; - -export default class MigrateCoordinatesTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenantCompanies(tenant); - await this.migrateTenantSites(tenant); - await this.migrateTenantSiteAreas(tenant); - await this.migrateTenantUsers(tenant); - await this.migrateTenantChargingStations(tenant); - } - } - - async migrateTenantCompanies(tenant: Tenant): Promise { - let updated = 0; - const companies = await global.database.getCollection(tenant.id, 'companies').aggregate( - []).toArray(); - // Process each setting - for (const company of companies) { - if (company.address && Utils.objectHasProperty(company.address, 'longitude') && Utils.objectHasProperty(company.address, 'latitude')) { - if (company.address.longitude && company.address.latitude) { - company.address.coordinates = [ - Utils.convertToFloat(company.address.longitude), - Utils.convertToFloat(company.address.latitude) - ]; - } - delete company.address.longitude; - delete company.address.latitude; - await global.database.getCollection(tenant.id, 'companies').replaceOne( - { '_id': company._id }, - company - ); - updated++; - } - } - // Log in the default tenant - if (updated > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `updated Companies(s) have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - async migrateTenantSites(tenant: Tenant): Promise { - let updated = 0; - const sites = await global.database.getCollection(tenant.id, 'sites').aggregate( - []).toArray(); - // Process each setting - for (const site of sites) { - if (site.address && Utils.objectHasProperty(site.address, 'longitude') && Utils.objectHasProperty(site.address, 'latitude')) { - if (site.address.longitude && site.address.latitude) { - site.address.coordinates = [ - Utils.convertToFloat(site.address.longitude), - Utils.convertToFloat(site.address.latitude) - ]; - } - delete site.address.longitude; - delete site.address.latitude; - await global.database.getCollection(tenant.id, 'sites').replaceOne( - { '_id': site._id }, - site - ); - updated++; - } - } - // Log in the default tenant - if (updated > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${updated} Sites(s) have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - async migrateTenantSiteAreas(tenant: Tenant): Promise { - let updated = 0; - const siteareas = await global.database.getCollection(tenant.id, 'siteareas').aggregate( - []).toArray(); - // Process each setting - for (const sitearea of siteareas) { - if (sitearea.address && Utils.objectHasProperty(sitearea.address, 'longitude') && Utils.objectHasProperty(sitearea.address, 'latitude')) { - if (sitearea.address.longitude && sitearea.address.latitude) { - sitearea.address.coordinates = [ - Utils.convertToFloat(sitearea.address.longitude), - Utils.convertToFloat(sitearea.address.latitude) - ]; - } - delete sitearea.address.longitude; - delete sitearea.address.latitude; - await global.database.getCollection(tenant.id, 'siteareas').replaceOne( - { '_id': sitearea._id }, - sitearea - ); - updated++; - } - } - // Log in the default tenant - if (updated > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${updated} SiteArea(s) have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - async migrateTenantUsers(tenant: Tenant): Promise { - let updated = 0; - const users = await global.database.getCollection(tenant.id, 'users').aggregate( - []).toArray(); - // Process each setting - for (const user of users) { - if (user.address && Utils.objectHasProperty(user.address, 'longitude') && Utils.objectHasProperty(user.address, 'latitude')) { - if (user.address.longitude && user.address.latitude) { - user.address.coordinates = [ - Utils.convertToFloat(user.address.longitude), - Utils.convertToFloat(user.address.latitude) - ]; - } - delete user.address.longitude; - delete user.address.latitude; - await global.database.getCollection(tenant.id, 'users').replaceOne( - { '_id': user._id }, - user - ); - updated++; - } - } - // Log in the default tenant - if (updated > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${updated} User(s) have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - async migrateTenantChargingStations(tenant: Tenant): Promise { - let updated = 0; - const chargingstations = await global.database.getCollection(tenant.id, 'chargingstations').aggregate( - []).toArray(); - // Process each setting - for (const chargingstation of chargingstations) { - if (Utils.objectHasProperty(chargingstation, 'longitude') && Utils.objectHasProperty(chargingstation, 'latitude')) { - if (chargingstation.longitude && chargingstation.latitude) { - chargingstation.coordinates = [ - Utils.convertToFloat(chargingstation.longitude), - Utils.convertToFloat(chargingstation.latitude) - ]; - } - delete chargingstation.longitude; - delete chargingstation.latitude; - await global.database.getCollection(tenant.id, 'chargingstations').replaceOne( - { '_id': chargingstation._id }, - chargingstation - ); - updated++; - } - } - // Log in the default tenant - if (updated > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${updated} Charging Station(s) have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.1'; - } - - getName(): string { - return 'MigrateCoordinatesTask'; - } -} diff --git a/src/migration/tasks/MigrateCryptoSettingsFromConfigToDBTask.ts b/src/migration/tasks/MigrateCryptoSettingsFromConfigToDBTask.ts deleted file mode 100644 index 55e00f0664..0000000000 --- a/src/migration/tasks/MigrateCryptoSettingsFromConfigToDBTask.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { CryptoSettings, CryptoSettingsType, TechnicalSettings } from '../../types/Setting'; - -import Configuration from '../../utils/Configuration'; -import Constants from '../../utils/Constants'; -import MigrationTask from '../MigrationTask'; -import SettingStorage from '../../storage/mongodb/SettingStorage'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; - -export default class MigrateCryptoSettingsFromConfigToDBTask extends MigrationTask { - public async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - public async migrateTenant(tenant: Tenant): Promise { - // Crypto Key from config file - const configCryptoKey = Configuration.getCryptoConfig().key; - // Crypto Key Properties from config file - const configCryptoKeyProperties = Utils.parseConfigCryptoAlgorithm(Configuration.getCryptoConfig().algorithm); - // Crypto Key Setting from db - const cryptoSettings = await SettingStorage.getCryptoSettings(tenant.id); - // If no Crypto Key Setting exist, initialize them with Crypto Key from config file - if (!cryptoSettings) { - // Create New Crypto Key in Tenant Settings - const keySettingToSave = { - identifier: TechnicalSettings.CRYPTO, - type: CryptoSettingsType.CRYPTO, - crypto: { - key: configCryptoKey, - keyProperties: configCryptoKeyProperties, - } - } as CryptoSettings; - await SettingStorage.saveCryptoSettings(tenant.id, keySettingToSave); - } - } - - public getVersion(): string { - return '1.0'; - } - - public getName(): string { - return 'MigrateCryptoSettingsFromConfigToDB'; - } -} diff --git a/src/migration/tasks/MigrateOcpiSettingTask.ts b/src/migration/tasks/MigrateOcpiSettingTask.ts deleted file mode 100644 index 5c3167d361..0000000000 --- a/src/migration/tasks/MigrateOcpiSettingTask.ts +++ /dev/null @@ -1,59 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'MigrateOcpiSettingTask'; - -export default class MigrateOcpiSettingTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - const setting = await global.database.getCollection(tenant.id, 'settings').findOne( - { 'identifier': 'ocpi' }); - // Process each setting - if (setting && setting.content && setting.content.ocpi) { - if (!setting.content.ocpi.cpo) { - setting.content.ocpi.cpo = { - countryCode: setting.content.ocpi.countryCode, - partyID: setting.content.ocpi.partyID - }; - } - if (!setting.content.ocpi.emsp) { - setting.content.ocpi.emsp = { - countryCode: setting.content.ocpi.countryCode, - partyID: setting.content.ocpi.partyID - }; - } - delete setting.content.ocpi.countryCode; - delete setting.content.ocpi.partyID; - await global.database.getCollection(tenant.id, 'settings').replaceOne( - { '_id': setting._id }, - setting - ); - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `OCPI setting has been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'MigrateOcpiSettingTask'; - } -} diff --git a/src/migration/tasks/MigrateOcpiTransactionsTask.ts b/src/migration/tasks/MigrateOcpiTransactionsTask.ts deleted file mode 100644 index 6b69ba471f..0000000000 --- a/src/migration/tasks/MigrateOcpiTransactionsTask.ts +++ /dev/null @@ -1,66 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'MigrateOcpiTransactionsTask'; - -export default class MigrateOcpiTransactionsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let modifiedCount = 0; - // Get all transactions - const transactionsMDB = await global.database.getCollection(tenant.id, 'transactions').find( - { - 'ocpiSession': { $exists: true } - } - ).toArray(); - for (const transactionMDB of transactionsMDB) { - // Update - await global.database.getCollection(tenant.id, 'transactions').findOneAndUpdate( - { '_id': transactionMDB['_id'] }, - { - $set: { - ocpiData: { - session: transactionMDB.ocpiSession, - cdr: transactionMDB.ocpiCdr - } - }, - $unset: { - ocpiSession: '', - ocpiCdr: '' - } - }, - { upsert: true, returnDocument: 'after' } - ); - modifiedCount++; - } - // Log in the default tenant - if (modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${modifiedCount} Transactions' have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'MigrateOcpiTransactionsTask'; - } -} diff --git a/src/migration/tasks/MigrateUserSettingsTask.ts b/src/migration/tasks/MigrateUserSettingsTask.ts deleted file mode 100644 index 902a191a36..0000000000 --- a/src/migration/tasks/MigrateUserSettingsTask.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { TechnicalSettings, UserSettings, UserSettingsType } from '../../types/Setting'; - -import Constants from '../../utils/Constants'; -import MigrationTask from '../MigrationTask'; -import SettingStorage from '../../storage/mongodb/SettingStorage'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; - -export default class MigrateUserSettingsTask extends MigrationTask { - public async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - public async migrateTenant(tenant: Tenant): Promise { - const userSettings = await SettingStorage.getUserSettings(tenant.id); - // If no user setting exists, initialize it - if (!userSettings) { - // Create new user setting with account activation param - const settingsToSave = { - identifier: TechnicalSettings.USER, - content: { - type: UserSettingsType.USER, - user: { - autoActivateAccountAfterValidation: true - } - }, - createdOn: new Date(), - }; - await SettingStorage.saveSettings(tenant.id, settingsToSave); - } - } - - public getVersion(): string { - return '1.0'; - } - - public getName(): string { - return 'MigrateUserSettings'; - } -} diff --git a/src/migration/tasks/RecomputeAllTransactionsConsumptionsTask.ts b/src/migration/tasks/RecomputeAllTransactionsConsumptionsTask.ts deleted file mode 100644 index ea45573e93..0000000000 --- a/src/migration/tasks/RecomputeAllTransactionsConsumptionsTask.ts +++ /dev/null @@ -1,115 +0,0 @@ -import global, { ActionsResponse } from '../../types/GlobalType'; - -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import OCPPUtils from '../../server/ocpp/utils/OCPPUtils'; -import Promise from 'bluebird'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import TransactionStorage from '../../storage/mongodb/TransactionStorage'; -import Utils from '../../utils/Utils'; - -const MODULE_NAME = 'RecomputeAllTransactionsConsumptionsTask'; - -export default class RecomputeAllTransactionsConsumptionsTask extends MigrationTask { - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async migrate() { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async migrateTenant(tenant: Tenant) { - const consumptionsUpdated: ActionsResponse = { - inError: 0, - inSuccess: 0, - }; - const startTime = new Date().getTime(); - // Get transactions - const transactionsMDB = await global.database.getCollection(tenant.id, 'transactions') - .aggregate([ - { - $match: { - 'stop.extraInactivitySecs': { $gt: 0 }, - 'stop.extraInactivityComputed': false - } - }, - { - $project: { '_id': 1 } - } - ]).toArray(); - if (!Utils.isEmptyArray(transactionsMDB)) { - void Logging.logInfo({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${transactionsMDB.length} Transaction(s) are going to be recomputed in Tenant ${Utils.buildTenantName(tenant)}...`, - }); - await Promise.map(transactionsMDB, async (transactionMDB) => { - try { - // Recompute consumption - const timeFrom = new Date().getTime(); - // Get the Transaction - const transaction = await TransactionStorage.getTransaction(tenant.id, transactionMDB._id); - // Rebuild consumptions - // FIXME: Power limitation will be lost in consumptions (to check the implementation) - const nbrOfConsumptions = await OCPPUtils.rebuildTransactionConsumptions(tenant, transaction); - const durationSecs = Math.trunc((new Date().getTime() - timeFrom) / 1000); - consumptionsUpdated.inSuccess++; - if (nbrOfConsumptions > 0) { - void Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `> ${consumptionsUpdated.inError + consumptionsUpdated.inSuccess}/${transactionsMDB.length} - Processed Transaction ID '${transactionMDB._id}' with ${nbrOfConsumptions} consumptions in ${durationSecs}s in Tenant ${Utils.buildTenantName(tenant)}`, - }); - } else { - // Delete transaction - await TransactionStorage.deleteTransaction(tenant.id, transactionMDB._id); - void Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `> ${consumptionsUpdated.inError + consumptionsUpdated.inSuccess}/${transactionsMDB.length} - Deleted Transaction ID '${transactionMDB._id}' with no consumption in Tenant ${Utils.buildTenantName(tenant)}`, - }); - } - } catch (error) { - consumptionsUpdated.inError++; - void Logging.logError({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `> ${consumptionsUpdated.inError + consumptionsUpdated.inSuccess}/${transactionsMDB.length} - Cannot recompute the consumptions of Transaction ID '${transactionMDB._id}' in Tenant ${Utils.buildTenantName(tenant)}`, - detailedMessages: { error: error.message, stack: error.stack } - }); - } - }, { concurrency: 5 }).then(() => { - const totalDurationSecs = Math.trunc((new Date().getTime() - startTime) / 1000); - // Log in the default tenant - void Logging.logActionsResponse(Constants.DEFAULT_TENANT, ServerAction.MIGRATION, - MODULE_NAME, 'migrateTenant', consumptionsUpdated, - `{{inSuccess}} transaction(s) were successfully processed in ${totalDurationSecs} secs in Tenant ${Utils.buildTenantName(tenant)}`, - `{{inError}} transaction(s) failed to be processed in ${totalDurationSecs} secs in Tenant ${Utils.buildTenantName(tenant)}`, - `{{inSuccess}} transaction(s) were successfully processed in ${totalDurationSecs} secs and {{inError}} failed to be processed in Tenant ${Utils.buildTenantName(tenant)}`, - `All the transactions are up to date in Tenant ${Utils.buildTenantName(tenant)}` - ); - }); - } - } - - getVersion(): string { - return '1.1'; - } - - getName(): string { - return 'RecomputeAllTransactionsConsumptionsTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/RecomputeAllTransactionsWithSimplePricingTask.ts b/src/migration/tasks/RecomputeAllTransactionsWithSimplePricingTask.ts deleted file mode 100644 index 4ea1548b64..0000000000 --- a/src/migration/tasks/RecomputeAllTransactionsWithSimplePricingTask.ts +++ /dev/null @@ -1,119 +0,0 @@ -import global, { ActionsResponse } from '../../types/GlobalType'; - -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import OCPPUtils from '../../server/ocpp/utils/OCPPUtils'; -import Promise from 'bluebird'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import TransactionStorage from '../../storage/mongodb/TransactionStorage'; -import Utils from '../../utils/Utils'; -import chalk from 'chalk'; - -const TASK_NAME = 'RecomputeAllTransactionsWithSimplePricingTask'; - -export default class RecomputeAllTransactionsWithSimplePricingTask extends MigrationTask { - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async migrate() { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async migrateTenant(tenant: Tenant) { - const transactionsUpdated: ActionsResponse = { - inError: 0, - inSuccess: 0, - }; - const timeTotalFrom = new Date().getTime(); - // Get transactions - const transactionsMDB = await global.database.getCollection(tenant.id, 'transactions') - .aggregate([ - { - $match: { - 'chargeBoxID':'SAP-Mougins-15', - 'timestamp': { $lt: new Date('2021-03-15') }, - 'stop.totalConsumptionWh': { $gt: 0 }, - 'stop.pricingSource': 'simple', - 'refundData': { $exists: false }, - 'migrationTag': { $ne: `${TASK_NAME}~${this.getVersion()}` }, - } - }, - { - $project: { '_id': 1, 'migrationFlag': 1 } - } - ]).toArray(); - if (!Utils.isEmptyArray(transactionsMDB)) { - let message = `${transactionsMDB.length} Transaction(s) are going to be recomputed in Tenant ${Utils.buildTenantName(tenant)}...`; - await Logging.logInfo({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: TASK_NAME, method: 'migrateTenant', - message, - }); - Utils.isDevelopmentEnv() && console.debug(chalk.yellow(`${new Date().toISOString()} - ${message}`)); - await Promise.map(transactionsMDB, async (transactionMDB) => { - const numberOfProcessedTransactions = transactionsUpdated.inError + transactionsUpdated.inSuccess; - if (numberOfProcessedTransactions > 0 && (numberOfProcessedTransactions % 100) === 0) { - message = `> ${transactionsUpdated.inError + transactionsUpdated.inSuccess}/${transactionsMDB.length} - Transaction consumptions recomputed in Tenant ${Utils.buildTenantName(tenant)}`; - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: TASK_NAME, method: 'migrateTenant', - message - }); - Utils.isDevelopmentEnv() && console.debug(chalk.yellow(`${new Date().toISOString()} - ${message}`)); - } - try { - // Get the transaction - const transaction = await TransactionStorage.getTransaction(tenant.id, transactionMDB._id); - // Flag the transaction as migrated - transaction.migrationTag = `${TASK_NAME}~${this.getVersion()}`; - // Rebuild the pricing - await OCPPUtils.rebuildTransactionSimplePricing(tenant, transaction); - // Read the priced transaction - // FIXME: Power limitation will be lost in consumptions (to check the implementation) - const pricedTransaction = await TransactionStorage.getTransaction(tenant.id, transactionMDB._id); - // Rebuild Consumptions - await OCPPUtils.rebuildTransactionConsumptions(tenant, pricedTransaction); - transactionsUpdated.inSuccess++; - } catch (error) { - transactionsUpdated.inError++; - await Logging.logError({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: TASK_NAME, method: 'migrateTenant', - message: `> ${transactionsUpdated.inError + transactionsUpdated.inSuccess}/${transactionsMDB.length} - Cannot recompute the consumptions of Transaction ID '${transactionMDB._id}' in Tenant ${Utils.buildTenantName(tenant)}`, - detailedMessages: { error: error.message, stack: error.stack } - }); - } - }, { concurrency: 5 }).then(() => { - const totalDurationSecs = Math.trunc((new Date().getTime() - timeTotalFrom) / 1000); - // Log in the default tenant - void Logging.logActionsResponse(Constants.DEFAULT_TENANT, ServerAction.MIGRATION, - TASK_NAME, 'migrateTenant', transactionsUpdated, - `{{inSuccess}} transaction(s) were successfully processed in ${totalDurationSecs} secs in Tenant ${Utils.buildTenantName(tenant)}`, - `{{inError}} transaction(s) failed to be processed in ${totalDurationSecs} secs in Tenant ${Utils.buildTenantName(tenant)}`, - `{{inSuccess}} transaction(s) were successfully processed in ${totalDurationSecs} secs and {{inError}} failed to be processed in Tenant ${Utils.buildTenantName(tenant)}`, - `All the transactions are up to date in Tenant ${Utils.buildTenantName(tenant)}` - ); - }); - } - } - - getVersion(): string { - return '1.5'; - } - - getName(): string { - return TASK_NAME; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/RemoveDuplicateTagVisualIDsTask.ts b/src/migration/tasks/RemoveDuplicateTagVisualIDsTask.ts new file mode 100644 index 0000000000..d0cbab0c1a --- /dev/null +++ b/src/migration/tasks/RemoveDuplicateTagVisualIDsTask.ts @@ -0,0 +1,81 @@ +import Constants from '../../utils/Constants'; +import Logging from '../../utils/Logging'; +import MigrationTask from '../MigrationTask'; +import { ObjectID } from 'mongodb'; +import { ServerAction } from '../../types/Server'; +import TagStorage from '../../storage/mongodb/TagStorage'; +import Tenant from '../../types/Tenant'; +import TenantStorage from '../../storage/mongodb/TenantStorage'; +import Utils from '../../utils/Utils'; +import global from '../../types/GlobalType'; + +const MODULE_NAME = 'AddVisualIDPropertyToTagsTask'; + +export default class RemoveDuplicateTagVisualIDsTask extends MigrationTask { + async migrate(): Promise { + const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); + for (const tenant of tenants.result) { + await this.migrateTenant(tenant); + } + } + + async migrateTenant(tenant: Tenant): Promise { + let updated = 0; + // Get the dup Tags with same Visual IDs + const tags = await global.database.getCollection(tenant.id, 'tags') + .aggregate([ + { + '$group': { + '_id': '$visualID', + 'count': { '$sum': 1 } + } + }, + { + '$match': { + 'count': { $gt: 1 } + } + } + ], { + allowDiskUse: true + }) + .toArray(); + if (!Utils.isEmptyArray(tags)) { + // Make the Tag IDs unique + for (const tag of tags) { + // Get dup Tags + const duplicateTags = await TagStorage.getTags(tenant.id, { + visualIDs: [tag._id], + }, Constants.DB_PARAMS_MAX_LIMIT, ['id']); + // Update the Visual IDs + for (const duplicateTag of duplicateTags.result) { + await global.database.getCollection(tenant.id, 'tags').updateOne( + { _id: duplicateTag.id }, + { $set: { visualID: new ObjectID().toHexString() } } + ); + updated++; + } + } + } + // Log in the default tenant + if (updated > 0) { + await Logging.logDebug({ + tenantID: Constants.DEFAULT_TENANT, + module: MODULE_NAME, method: 'migrateTenant', + action: ServerAction.MIGRATION, + message: `${updated} duplicate Tag(s) Visual IDs have been made unique in Tenant ${Utils.buildTenantName(tenant)}` + }); + } + } + + getVersion(): string { + return '1.0'; + } + + getName(): string { + return 'RemoveDuplicateTagVisualIDsTask'; + } + + isAsynchronous(): boolean { + return true; + } +} diff --git a/src/migration/tasks/RenameChargingStationPropertiesTask.ts b/src/migration/tasks/RenameChargingStationPropertiesTask.ts deleted file mode 100644 index dd20cd4c96..0000000000 --- a/src/migration/tasks/RenameChargingStationPropertiesTask.ts +++ /dev/null @@ -1,45 +0,0 @@ -import ChargingStation from '../../types/ChargingStation'; -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'RenameChargingStationPropertiesTask'; - -export default class RenameChargingStationPropertiesTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Rename the property in the collection - const result = await global.database.getCollection(tenant.id, 'chargingstations').updateMany( - {}, - { $rename: { 'lastHeartBeat': 'lastSeen' } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.modifiedCount} Charging Stations' properties have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.2'; - } - - getName(): string { - return 'RenameChargingStationPropertiesTask'; - } -} diff --git a/src/migration/tasks/RenameSMTPAuthErrorTask.ts b/src/migration/tasks/RenameSMTPAuthErrorTask.ts deleted file mode 100644 index 5d36a0b67c..0000000000 --- a/src/migration/tasks/RenameSMTPAuthErrorTask.ts +++ /dev/null @@ -1,64 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import User from '../../types/User'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'RenameSMTPAuthErrorTask'; - -export default class RenameSMTPAuthErrorTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let result; - // Rename the property in the collection - result = await global.database.getCollection(tenant.id, 'users').updateMany( - { 'notifications.sendSmtpAuthError': { $exists: true } }, - { $rename: { 'notifications.sendSmtpAuthError': 'notifications.sendSmtpError' } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.modifiedCount} Users' properties have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - result = await global.database.getCollection(tenant.id, 'notifications').updateMany( - { - sourceDescr: { - $exists: true, - $eq: 'AuthentificationErrorEmailServer' - } - }, - { $set: { sourceDescr: 'EmailServerError' } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.modifiedCount} Notifications' properties have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.5'; - } - - getName(): string { - return 'RenameSMTPAuthErrorTask'; - } -} diff --git a/src/migration/tasks/RenameTagPropertiesTask.ts b/src/migration/tasks/RenameTagPropertiesTask.ts deleted file mode 100644 index 1aa67cd14d..0000000000 --- a/src/migration/tasks/RenameTagPropertiesTask.ts +++ /dev/null @@ -1,45 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tag from '../../types/Tag'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'RenameTagPropertiesTask'; - -export default class RenameTagPropertiesTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Rename the properties in the collection - const result = await global.database.getCollection(tenant.id, 'tags').updateMany( - {}, - { $rename: { 'internal': 'issuer', 'provider': 'description' } } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.modifiedCount} Tag(s) have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.1'; - } - - getName(): string { - return 'RenameTagPropertiesTask'; - } -} diff --git a/src/migration/tasks/RenameTransactionsAndConsumptionsTask.ts b/src/migration/tasks/RenameTransactionsAndConsumptionsTask.ts deleted file mode 100644 index 3f91632451..0000000000 --- a/src/migration/tasks/RenameTransactionsAndConsumptionsTask.ts +++ /dev/null @@ -1,88 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'CleanupAllTransactionsTask'; - -export default class RenameTransactionsAndConsumptionsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Consumptions - await this.renameConsumptionProperties(tenant); - await this.deleteConsumptionProperties(tenant); - } - - getVersion(): string { - return '1.1'; - } - - getName(): string { - return 'RenameTransactionsAndConsumptionsTask'; - } - - isAsynchronous(): boolean { - return true; - } - - private async deleteConsumptionProperties(tenant: Tenant): Promise { - const result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { }, - { - $unset: { - 'amperage': '', - } - } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'deleteConsumptionProperties', - message: `${result.modifiedCount} Consumption(s) unused properties have been removed in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - private async renameConsumptionProperties(tenant: Tenant): Promise { - // Renamed properties in Transactions - const result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { - 'instantVolts': { $exists: false }, - }, - { - $rename: { - 'voltage': 'instantVolts', - 'voltageL1': 'instantVoltsL1', - 'voltageL2': 'instantVoltsL2', - 'voltageL3': 'instantVoltsL3', - 'voltageDC': 'instantVoltsDC', - 'amperageL1': 'instantAmpsL1', - 'amperageL2': 'instantAmpsL2', - 'amperageL3': 'instantAmpsL3', - 'amperageDC': 'instantAmpsDC', - } - } - ); - // Log in the default tenant - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'renameConsumptionProperties', - message: `${result.modifiedCount} Consumption(s) have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } -} diff --git a/src/migration/tasks/ResetCarCatalogsHashTask.ts b/src/migration/tasks/ResetCarCatalogsHashTask.ts deleted file mode 100644 index 988c030ab2..0000000000 --- a/src/migration/tasks/ResetCarCatalogsHashTask.ts +++ /dev/null @@ -1,52 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'ResetCarCatalogsHashTask'; - -export default class ResetCarCatalogsHashTask extends MigrationTask { - async migrate(): Promise { - try { - const result = await global.database.getCollection(Constants.DEFAULT_TENANT, 'carcatalogs').updateMany({ - }, - [ - { - '$set': { - 'hash': null, - 'imagesHash': null, - } - } - ]); - if (result.modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrate', - message: `${result.modifiedCount} Car(s) catalog(s) have been updated in the default tenant` - }); - } - } catch (error) { - await Logging.logError({ - tenantID: Constants.DEFAULT_TENANT, - module: MODULE_NAME, method: 'migrate', - action: ServerAction.MIGRATION, - message: `Error while updating the Cars catalog: ${error.message}`, - detailedMessages: { error: error.message, stack: error.stack } - }); - } - } - - getVersion(): string { - return '1.1'; - } - - isAsynchronous(): boolean { - return true; - } - - getName(): string { - return 'ResetCarCatalogsHashTask'; - } -} diff --git a/src/migration/tasks/SetDefaultTagToUserTask.ts b/src/migration/tasks/SetDefaultTagToUserTask.ts deleted file mode 100644 index 6c06175286..0000000000 --- a/src/migration/tasks/SetDefaultTagToUserTask.ts +++ /dev/null @@ -1,80 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tag from '../../types/Tag'; -import TagStorage from '../../storage/mongodb/TagStorage'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import UserStorage from '../../storage/mongodb/UserStorage'; -import Utils from '../../utils/Utils'; - -const MODULE_NAME = 'SetDefaultTagToUserTask'; - -export default class SetDefaultTagToUserTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let modifiedCount = 0; - // Get all the Users - const users = await UserStorage.getUsers(tenant.id, { - issuer: true, - }, Constants.DB_PARAMS_MAX_LIMIT); - // Process them - if (!Utils.isEmptyArray(users.result)) { - for (const user of users.result) { - // Get all the User's Tags - const tags = await TagStorage.getTags(tenant.id, { - userIDs: [user.id] - }, Constants.DB_PARAMS_MAX_LIMIT); - // Process them - if (!Utils.isEmptyArray(tags.result)) { - let numberOfDefaultTag = 0; - let activeTag: Tag; - for (const tag of tags.result) { - // Count default Tag - if (tag.default) { - numberOfDefaultTag++; - } - // Keep an active tag - if (tag.active) { - activeTag = tag; - } - } - // More than one default Tag or no Tag at all - if (numberOfDefaultTag !== 1) { - // Clear default User's Tags - modifiedCount += numberOfDefaultTag; - await TagStorage.clearDefaultUserTag(tenant.id, user.id); - if (activeTag) { - activeTag.default = true; - await TagStorage.saveTag(tenant.id, activeTag); - } - } - } - } - } - // Log in the default tenant - if (modifiedCount > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - module: MODULE_NAME, method: 'migrateTenant', - action: ServerAction.MIGRATION, - message: `${modifiedCount} Tag(s) have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.1'; - } - - getName(): string { - return 'SetDefaultTagToUserTask'; - } -} diff --git a/src/migration/tasks/SiteUsersHashIDsTask.ts b/src/migration/tasks/SiteUsersHashIDsTask.ts deleted file mode 100644 index 9653d9dbfc..0000000000 --- a/src/migration/tasks/SiteUsersHashIDsTask.ts +++ /dev/null @@ -1,54 +0,0 @@ -import Constants from '../../utils/Constants'; -import Cypher from '../../utils/Cypher'; -import MigrationTask from '../MigrationTask'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import global from '../../types/GlobalType'; - -export default class SiteUsersHashIDsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - // Create Aggregation - const aggregation = []; - // Filters - aggregation.push({ - $match: { - '_id': { - $type: 'objectId' - } - } - }); - // Exec - const userSitesMDB = await global.database.getCollection(tenant.id, 'siteusers') - .aggregate(aggregation).toArray(); - // Process IDs - for (const userSiteMDB of userSitesMDB) { - const idToDelete = userSiteMDB._id; - // Convert ID - userSiteMDB._id = Cypher.hash(`${userSiteMDB.siteID.toString()}~${userSiteMDB.userID.toString()}`); - // Delete - await global.database.getCollection( - tenant.id, 'siteusers').deleteOne( - { '_id' : idToDelete } - ); - // Create - await global.database.getCollection( - tenant.id, 'siteusers').insertOne(userSiteMDB); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'SiteUsersHashIDsTask'; - } -} - diff --git a/src/migration/tasks/UnmarkTransactionExtraInactivitiesTask.ts b/src/migration/tasks/UnmarkTransactionExtraInactivitiesTask.ts deleted file mode 100644 index 8b74804bfa..0000000000 --- a/src/migration/tasks/UnmarkTransactionExtraInactivitiesTask.ts +++ /dev/null @@ -1,53 +0,0 @@ -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'UnmarkTransactionExtraInactivitiesTask'; - -export default class UnmarkTransactionExtraInactivitiesTask extends MigrationTask { - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async migrate() { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - async migrateTenant(tenant: Tenant) { - // Get transactions - const result = await global.database.getCollection(tenant.id, 'transactions').updateMany( - { - 'stop.extraInactivitySecs': { $gt: 0 } - }, - { - $set: { 'stop.extraInactivityComputed': false } - } - ); - if (result.modifiedCount > 0) { - await Logging.logInfo({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrateTenant', - message: `${result.modifiedCount} Transaction(s) have been marked to be recomputed in Tenant ${Utils.buildTenantName(tenant)}...`, - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'UnmarkTransactionExtraInactivitiesTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/migration/tasks/UpdateChargingStationStaticLimitationTask.ts b/src/migration/tasks/UpdateChargingStationStaticLimitationTask.ts deleted file mode 100644 index 792c186933..0000000000 --- a/src/migration/tasks/UpdateChargingStationStaticLimitationTask.ts +++ /dev/null @@ -1,111 +0,0 @@ -import ChargingStationStorage from '../../storage/mongodb/ChargingStationStorage'; -import ChargingStationVendorFactory from '../../integration/charging-station-vendor/ChargingStationVendorFactory'; -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { OCPPConfigurationStatus } from '../../types/ocpp/OCPPClient'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; - -const MODULE_NAME = 'UpdateChargingStationTemplatesTask'; - -export default class UpdateChargingStationStaticLimitationTask extends MigrationTask { - isAsynchronous(): boolean { - return true; - } - - getName(): string { - return 'UpdateChargingStationStaticLimitationTask'; - } - - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - // Initialize amperage limitation - await this.initChargingStationLimitAmps(tenant); - } - } - - getVersion(): string { - return '1.0'; - } - - private async initChargingStationLimitAmps(tenant: Tenant) { - let updated = 0; - // Get the charging stations - const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, { - issuer: true, includeDeleted: true - }, Constants.DB_PARAMS_MAX_LIMIT); - // Update - for (const chargingStation of chargingStations.result) { - // Check Charge Point - if (chargingStation.chargePoints) { - for (const chargePoint of chargingStation.chargePoints) { - let chargePointUpdated = false; - // Get the Vendor instance - const chargingStationVendor = ChargingStationVendorFactory.getChargingStationVendorImpl(chargingStation); - if (chargingStationVendor) { - // Get max charge point amps - const amperageChargePointMax = Utils.getChargingStationAmperage(chargingStation, chargePoint); - try { - // Call the limitation - const result = await chargingStationVendor.setStaticPowerLimitation(tenant, chargingStation, chargePoint, amperageChargePointMax); - if (result.status === OCPPConfigurationStatus.ACCEPTED || - result.status === OCPPConfigurationStatus.REBOOT_REQUIRED) { - chargePointUpdated = true; - updated++; - } else { - await Logging.logError({ - tenantID: tenant.id, - action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE, - module: MODULE_NAME, method: 'initChargingStationLimitAmps', - message: `Cannot set Charge Point static limitation to ${amperageChargePointMax}A`, - detailedMessages: { chargePoint } - }); - } - } catch (error) { - await Logging.logError({ - tenantID: tenant.id, - action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE, - module: MODULE_NAME, method: 'initChargingStationLimitAmps', - message: `Cannot set Charge Point static limitation to ${amperageChargePointMax}A`, - detailedMessages: { error: error.message, stack: error.stack, chargePoint } - }); - } - } - if (!chargePointUpdated) { - // Update each connector manually - for (const connectorID of chargePoint.connectorIDs) { - // Get max connector amps - const connector = Utils.getConnectorFromID(chargingStation, connectorID); - if (connector) { - connector.amperageLimit = Utils.getChargingStationAmperage(chargingStation, chargePoint, connectorID); - } - } - await ChargingStationStorage.saveChargingStationConnectors(tenant.id, chargingStation.id, chargingStation.connectors); - updated++; - } - } - } else if (chargingStation.connectors) { - // Update each connector manually - for (const connector of chargingStation.connectors) { - if (connector) { - connector.amperageLimit = connector.amperage; - } - } - await ChargingStationStorage.saveChargingStationConnectors(tenant.id, chargingStation.id, chargingStation.connectors); - updated++; - } - } - if (updated > 0) { - await Logging.logDebug({ - tenantID: tenant.id, - action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE, - module: MODULE_NAME, method: 'cleanUpChargingStationDBProps', - message: `${updated} Charging Stations amperage limit has been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } -} diff --git a/src/migration/tasks/UpdateConsumptionsToObjectIDsTask.ts b/src/migration/tasks/UpdateConsumptionsToObjectIDsTask.ts deleted file mode 100644 index 62a5abea13..0000000000 --- a/src/migration/tasks/UpdateConsumptionsToObjectIDsTask.ts +++ /dev/null @@ -1,96 +0,0 @@ -import Constants from '../../utils/Constants'; -import Consumption from '../../types/Consumption'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'UpdateConsumptionsToObjectIDsTask'; - -export default class UpdateConsumptionsToObjectIDsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let updated = 0; - // Create Aggregation - const aggregation = []; - aggregation.push({ - '$match': { - $or: [ - { siteID: { $type: 'string' } }, - { siteAreaID: { $type: 'string' } }, - { userID: { $type: 'string' } } - ] - } - }); - aggregation.push({ - '$limit': 1 - }); - let consumptionsMDB; - do { - // Read - consumptionsMDB = await global.database.getCollection(tenant.id, 'consumptions') - .aggregate(aggregation).toArray(); - // Check and Update whole consumption - for (const consumptionMDB of consumptionsMDB) { - // Update Sites - if (typeof consumptionMDB.siteID === 'string') { - // Update all - const result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { siteID: consumptionMDB.siteID }, - { $set: { siteID: Utils.convertToObjectID(consumptionMDB.siteID) } } - ); - updated += result.modifiedCount; - } - // Update Site Areas - if (typeof consumptionMDB.siteAreaID === 'string') { - // Update all - const result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { siteAreaID: consumptionMDB.siteAreaID }, - { $set: { siteAreaID: Utils.convertToObjectID(consumptionMDB.siteAreaID) } } - ); - updated += result.modifiedCount; - } - // Update Users - if (typeof consumptionMDB.userID === 'string') { - // Update all - const result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { userID: consumptionMDB.userID }, - { $set: { userID: Utils.convertToObjectID(consumptionMDB.userID) } } - ); - updated += result.modifiedCount; - } - } - } while (!Utils.isEmptyArray(consumptionsMDB)); - // Log - if (updated > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.MIGRATION, - module: MODULE_NAME, method: 'migrate', - message: `Tenant ${Utils.buildTenantName(tenant)} (${tenant.id}): ${updated} consumptions have been updated` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'UpdateConsumptionsToObjectIDs'; - } - - isAsynchronous(): boolean { - return true; - } -} - diff --git a/src/migration/tasks/UpdateLimitsInConsumptionsTask.ts b/src/migration/tasks/UpdateLimitsInConsumptionsTask.ts deleted file mode 100644 index 052ac5840f..0000000000 --- a/src/migration/tasks/UpdateLimitsInConsumptionsTask.ts +++ /dev/null @@ -1,114 +0,0 @@ -import ChargingStationStorage from '../../storage/mongodb/ChargingStationStorage'; -import { ConnectorCurrentLimitSource } from '../../types/ChargingStation'; -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import { UpdateWriteOpResult } from 'mongodb'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'UpdateLimitsInConsumptionsTask'; - -export default class UpdateLimitsInConsumptionsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let updated = 0; - let result: UpdateWriteOpResult; - // Get Charging Stations - const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, { - issuer: true, includeDeleted: true - }, Constants.DB_PARAMS_MAX_LIMIT); - for (const chargingStation of chargingStations.result) { - for (const connector of chargingStation.connectors) { - let limitWatts = 0, limitAmps = 0; - // Amps from chargepoint? - if (chargingStation.chargePoints) { - const chargePoint = Utils.getChargePointFromID(chargingStation, connector.chargePointID); - limitAmps = Utils.getChargingStationAmperage(chargingStation, chargePoint, connector.connectorId); - limitWatts = Utils.convertAmpToWatt(chargingStation, chargePoint, connector.connectorId, limitAmps); - // Amps from connector - } else if (connector.amperage) { - limitAmps = connector.amperage; - limitWatts = Utils.convertAmpToWatt(chargingStation, null, connector.connectorId, limitAmps); - } - if (limitAmps) { - // Update - result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { - chargeBoxID: chargingStation.id, - connectorId: connector.connectorId, - }, - { - $set: { - limitAmps: limitAmps, - limitWatts: limitWatts, - limitSource: ConnectorCurrentLimitSource.CONNECTOR - } - } - ); - updated += result.modifiedCount; - } - } - } - // Update leftover - // 22170 -> 22080 W -> 96 A - result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { - limitWatts: 22170, - }, - { - $set: { - limitAmps: 96, - limitWatts: 22080, - limitSource: ConnectorCurrentLimitSource.CONNECTOR - } - } - ); - updated += result.modifiedCount; - // Update leftover - // 7390 -> 7360 -> 32 A - result = await global.database.getCollection(tenant.id, 'consumptions').updateMany( - { - limitWatts: 7390, - }, - { - $set: { - limitAmps: 32, - limitWatts: 7360, - limitSource: ConnectorCurrentLimitSource.CONNECTOR - } - } - ); - updated += result.modifiedCount; - // Log - if (updated > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - action: ServerAction.UPDATE_CHARGING_STATION_WITH_TEMPLATE, - module: MODULE_NAME, method: 'migrateTenant', - message: `${updated} Charging Stations amperage limit has been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.0'; - } - - getName(): string { - return 'UpdateLimitsInConsumptionsTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/notification/NotificationHandler.ts b/src/notification/NotificationHandler.ts index 48fede3cfb..d941597812 100644 --- a/src/notification/NotificationHandler.ts +++ b/src/notification/NotificationHandler.ts @@ -1,3 +1,4 @@ +import Tenant, { TenantLogo } from '../types/Tenant'; import User, { UserRole } from '../types/User'; import UserNotifications, { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, Notification, NotificationSeverity, NotificationSource, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, SmtpErrorNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserNotificationKeys, VerificationEmailNotification } from '../types/UserNotifications'; @@ -31,10 +32,10 @@ export default class NotificationHandler { } ]; - static async getAdminUsers(tenantID: string, notificationKey?: UserNotificationKeys): Promise { + static async getAdminUsers(tenant: Tenant, notificationKey?: UserNotificationKeys): Promise { // Get admin users let params; - if (tenantID === Constants.DEFAULT_TENANT) { + if (tenant.id === Constants.DEFAULT_TENANT) { params = { roles: [UserRole.SUPER_ADMIN], notificationsActive: true, notifications: {} as UserNotifications }; } else { params = { roles: [UserRole.ADMIN], notificationsActive: true, notifications: {} as UserNotifications }; @@ -42,19 +43,19 @@ export default class NotificationHandler { if (notificationKey) { params.notifications[notificationKey] = true; } - const adminUsers = await UserStorage.getUsers(tenantID, params, Constants.DB_PARAMS_MAX_LIMIT); + const adminUsers = await UserStorage.getUsers(tenant.id, params, Constants.DB_PARAMS_MAX_LIMIT); // Found if (adminUsers.count > 0) { return adminUsers.result; } } - public static async hasNotifiedSource(tenantID: string, channel: string, sourceDescr: string, chargeBoxID: string, userID: string, + public static async hasNotifiedSource(tenant: Tenant, channel: string, sourceDescr: string, chargeBoxID: string, userID: string, interval: { intervalMins: number; additionalFilters?: any }): Promise { try { if (interval && interval.intervalMins) { const notifications = await NotificationStorage.getNotifications( - tenantID, { + tenant, { channel, sourceDescr, chargeBoxID, @@ -68,15 +69,15 @@ export default class NotificationHandler { } return false; } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.NOTIFICATION, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.NOTIFICATION, error); } } - public static async hasNotifiedSourceByID(tenantID: string, channel: string, notificationID: string): Promise { + public static async hasNotifiedSourceByID(tenant: Tenant, channel: string, notificationID: string): Promise { try { // Get the Notification const notifications = await NotificationStorage.getNotifications( - tenantID, + tenant, { channel: channel, sourceId: notificationID @@ -85,15 +86,18 @@ export default class NotificationHandler { ); return notifications.count > 0; } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.NOTIFICATION, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.NOTIFICATION, error); } } - public static async sendEndOfCharge(tenantID: string, user: User, chargingStation: ChargingStation, + public static async sendEndOfCharge(tenant: Tenant, user: User, chargingStation: ChargingStation, sourceData: EndOfChargeNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -104,14 +108,14 @@ export default class NotificationHandler { chargingStation, Utils.getConnectorIDFromConnectorLetter(sourceData.connectorId)); // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSource( - tenantID, notificationSource.channel, ServerAction.END_OF_CHARGE, + tenant, notificationSource.channel, ServerAction.END_OF_CHARGE, chargingStation.id, user.id, { intervalMins, additionalFilters: { transactionId: sourceData.transactionId } }); if (!hasBeenNotified) { // Enabled? if (user.notificationsActive && user.notifications.sendEndOfCharge) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.END_OF_CHARGE, { + tenant, notificationSource.channel, null, ServerAction.END_OF_CHARGE, { user, chargingStation, notificationData: { @@ -125,18 +129,21 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.END_OF_CHARGE, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.END_OF_CHARGE, error); } } } } } - public static async sendOptimalChargeReached(tenantID: string, notificationID: string, user: User, chargingStation: ChargingStation, + public static async sendOptimalChargeReached(tenant: Tenant, notificationID: string, user: User, chargingStation: ChargingStation, sourceData: OptimalChargeReachedNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -145,13 +152,13 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSourceByID( - tenantID, notificationSource.channel, notificationID); + tenant, notificationSource.channel, notificationID); if (!hasBeenNotified) { // Enabled? if (user.notificationsActive && user.notifications.sendOptimalChargeReached) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.OPTIMAL_CHARGE_REACHED, { + tenant, notificationSource.channel, notificationID, ServerAction.OPTIMAL_CHARGE_REACHED, { user, chargingStation, notificationData: { @@ -164,18 +171,21 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.OPTIMAL_CHARGE_REACHED, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.OPTIMAL_CHARGE_REACHED, error); } } } } } - public static async sendEndOfSession(tenantID: string, notificationID: string, user: User, chargingStation: ChargingStation, + public static async sendEndOfSession(tenant: Tenant, notificationID: string, user: User, chargingStation: ChargingStation, sourceData: EndOfSessionNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -184,13 +194,13 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSourceByID( - tenantID, notificationSource.channel, notificationID); + tenant, notificationSource.channel, notificationID); if (!hasBeenNotified) { // Enabled? if (user.notificationsActive && user.notifications.sendEndOfSession) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.END_OF_SESSION, { + tenant, notificationSource.channel, notificationID, ServerAction.END_OF_SESSION, { user, chargingStation, notificationData: { @@ -203,18 +213,21 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.END_OF_SESSION, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.END_OF_SESSION, error); } } } } } - public static async sendEndOfSignedSession(tenantID: string, notificationID: string, user: User, chargingStation: ChargingStation, + public static async sendEndOfSignedSession(tenant: Tenant, notificationID: string, user: User, chargingStation: ChargingStation, sourceData: EndOfSignedSessionNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -223,13 +236,13 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSourceByID( - tenantID, notificationSource.channel, notificationID); + tenant, notificationSource.channel, notificationID); if (!hasBeenNotified) { // Enabled? if (user.notificationsActive && user.notifications.sendEndOfSession) { // Save notification await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.END_OF_SESSION, { + tenant, notificationSource.channel, notificationID, ServerAction.END_OF_SESSION, { user, chargingStation, notificationData: { @@ -242,18 +255,21 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.END_OF_SESSION, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.END_OF_SESSION, error); } } } } } - public static async sendRequestPassword(tenantID: string, notificationID: string, user: User, + public static async sendRequestPassword(tenant: Tenant, notificationID: string, user: User, sourceData: RequestPasswordNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -262,23 +278,26 @@ export default class NotificationHandler { try { // Save notification await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.REQUEST_PASSWORD, { user }); + tenant, notificationSource.channel, notificationID, ServerAction.REQUEST_PASSWORD, { user }); // Send await notificationSource.notificationTask.sendRequestPassword( sourceData, user, tenant, NotificationSeverity.INFO); } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.REQUEST_PASSWORD, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.REQUEST_PASSWORD, error); } } } } } - public static async sendUserAccountStatusChanged(tenantID: string, notificationID: string, user: User, + public static async sendUserAccountStatusChanged(tenant: Tenant, notificationID: string, user: User, sourceData: UserAccountStatusChangedNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -289,24 +308,27 @@ export default class NotificationHandler { if (user.notificationsActive && user.notifications.sendUserAccountStatusChanged) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.USER_ACCOUNT_STATUS_CHANGED, { user }); + tenant, notificationSource.channel, notificationID, ServerAction.USER_ACCOUNT_STATUS_CHANGED, { user }); // Send await notificationSource.notificationTask.sendUserAccountStatusChanged( sourceData, user, tenant, NotificationSeverity.WARNING); } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.USER_ACCOUNT_STATUS_CHANGED, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.USER_ACCOUNT_STATUS_CHANGED, error); } } } } } - public static async sendNewRegisteredUser(tenantID: string, notificationID: string, user: User, + public static async sendNewRegisteredUser(tenant: Tenant, notificationID: string, user: User, sourceData: NewRegisteredUserNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -315,22 +337,25 @@ export default class NotificationHandler { try { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.NEW_REGISTERED_USER, { user }); + tenant, notificationSource.channel, notificationID, ServerAction.NEW_REGISTERED_USER, { user }); // Send await notificationSource.notificationTask.sendNewRegisteredUser( sourceData, user, tenant, NotificationSeverity.INFO); } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.NEW_REGISTERED_USER, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.NEW_REGISTERED_USER, error); } } } } } - static async sendAccountVerification(tenantID: string, notificationID: string, user: User, sourceData: AccountVerificationNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + static async sendAccountVerification(tenant: Tenant, notificationID: string, user: User, sourceData: AccountVerificationNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -339,17 +364,17 @@ export default class NotificationHandler { try { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.USER_ACCOUNT_VERIFICATION, { user }); + tenant, notificationSource.channel, notificationID, ServerAction.USER_ACCOUNT_VERIFICATION, { user }); // Send await notificationSource.notificationTask.sendAccountVerificationNotification( sourceData, user, tenant, NotificationSeverity.INFO); } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.USER_ACCOUNT_VERIFICATION, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.USER_ACCOUNT_VERIFICATION, error); } } } await this.sendAdminAccountVerification( - tenantID, + tenant, Utils.generateUUID(), user, { @@ -362,13 +387,16 @@ export default class NotificationHandler { } } - static async sendAdminAccountVerification(tenantID: string, notificationID: string, user: User, adminSourceData: AdminAccountVerificationNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + static async sendAdminAccountVerification(tenant: Tenant, notificationID: string, user: User, adminSourceData: AdminAccountVerificationNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } adminSourceData.tenantLogoURL = tenant.logo; // Get the admin - const adminUsers = await NotificationHandler.getAdminUsers(tenantID, 'sendAdminAccountVerificationNotification'); + const adminUsers = await NotificationHandler.getAdminUsers(tenant, 'sendAdminAccountVerificationNotification'); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -376,14 +404,14 @@ export default class NotificationHandler { if (notificationSource.enabled) { try { // Save - await NotificationHandler.saveNotification(tenantID, notificationSource.channel, notificationID, ServerAction.ADMIN_ACCOUNT_VERIFICATION); + await NotificationHandler.saveNotification(tenant, notificationSource.channel, notificationID, ServerAction.ADMIN_ACCOUNT_VERIFICATION); // Send for (const adminUser of adminUsers) { await notificationSource.notificationTask.sendAdminAccountVerificationNotification( adminSourceData, adminUser, tenant, NotificationSeverity.INFO); } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.ADMIN_ACCOUNT_VERIFICATION, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.ADMIN_ACCOUNT_VERIFICATION, error); } } } @@ -391,11 +419,14 @@ export default class NotificationHandler { } } - static async sendVerificationEmail(tenantID: string, notificationID: string, user: User, + static async sendVerificationEmail(tenant: Tenant, notificationID: string, user: User, sourceData: VerificationEmailNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -404,26 +435,29 @@ export default class NotificationHandler { try { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.VERIFICATION_EMAIL, { user }); + tenant, notificationSource.channel, notificationID, ServerAction.VERIFICATION_EMAIL, { user }); // Send await notificationSource.notificationTask.sendVerificationEmail( sourceData, user, tenant, NotificationSeverity.INFO); } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.VERIFICATION_EMAIL, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.VERIFICATION_EMAIL, error); } } } } } - public static async sendChargingStationStatusError(tenantID: string, notificationID: string, chargingStation: ChargingStation, + public static async sendChargingStationStatusError(tenant: Tenant, notificationID: string, chargingStation: ChargingStation, sourceData: ChargingStationStatusErrorNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID, 'sendChargingStationStatusError'); + const adminUsers = await NotificationHandler.getAdminUsers(tenant, 'sendChargingStationStatusError'); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -432,7 +466,7 @@ export default class NotificationHandler { try { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.CHARGING_STATION_STATUS_ERROR, { + tenant, notificationSource.channel, notificationID, ServerAction.CHARGING_STATION_STATUS_ERROR, { chargingStation, notificationData: { 'connectorId': sourceData.connectorId, @@ -446,7 +480,7 @@ export default class NotificationHandler { sourceData, adminUser, tenant, NotificationSeverity.ERROR); } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.CHARGING_STATION_STATUS_ERROR, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.CHARGING_STATION_STATUS_ERROR, error); } } } @@ -454,14 +488,17 @@ export default class NotificationHandler { } } - public static async sendChargingStationRegistered(tenantID: string, notificationID: string, chargingStation: ChargingStation, + public static async sendChargingStationRegistered(tenant: Tenant, notificationID: string, chargingStation: ChargingStation, sourceData: ChargingStationRegisteredNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID, 'sendChargingStationRegistered'); + const adminUsers = await NotificationHandler.getAdminUsers(tenant, 'sendChargingStationRegistered'); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -470,14 +507,14 @@ export default class NotificationHandler { try { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.CHARGING_STATION_REGISTERED, { chargingStation }); + tenant, notificationSource.channel, notificationID, ServerAction.CHARGING_STATION_REGISTERED, { chargingStation }); // Send for (const adminUser of adminUsers) { await notificationSource.notificationTask.sendChargingStationRegistered( sourceData, adminUser, tenant, NotificationSeverity.WARNING); } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.CHARGING_STATION_REGISTERED, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.CHARGING_STATION_REGISTERED, error); } } } @@ -485,14 +522,17 @@ export default class NotificationHandler { } } - public static async sendUnknownUserBadged(tenantID: string, notificationID: string, chargingStation: ChargingStation, + public static async sendUnknownUserBadged(tenant: Tenant, notificationID: string, chargingStation: ChargingStation, sourceData: UnknownUserBadgedNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID, 'sendUnknownUserBadged'); + const adminUsers = await NotificationHandler.getAdminUsers(tenant, 'sendUnknownUserBadged'); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -501,14 +541,14 @@ export default class NotificationHandler { try { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.UNKNOWN_USER_BADGED, { chargingStation }); + tenant, notificationSource.channel, notificationID, ServerAction.UNKNOWN_USER_BADGED, { chargingStation }); // Send for (const adminUser of adminUsers) { await notificationSource.notificationTask.sendUnknownUserBadged( sourceData, adminUser, tenant, NotificationSeverity.WARNING); } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.UNKNOWN_USER_BADGED, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.UNKNOWN_USER_BADGED, error); } } } @@ -516,11 +556,14 @@ export default class NotificationHandler { } } - public static async sendSessionStarted(tenantID: string, notificationID: string, user: User, chargingStation: ChargingStation, + public static async sendSessionStarted(tenant: Tenant, notificationID: string, user: User, chargingStation: ChargingStation, sourceData: TransactionStartedNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -529,13 +572,13 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSourceByID( - tenantID, notificationSource.channel, notificationID); + tenant, notificationSource.channel, notificationID); if (!hasBeenNotified) { // Enabled? if (user.notificationsActive && user.notifications.sendSessionStarted) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.TRANSACTION_STARTED, { + tenant, notificationSource.channel, notificationID, ServerAction.TRANSACTION_STARTED, { user, chargingStation, notificationData: { @@ -550,20 +593,23 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.TRANSACTION_STARTED, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.TRANSACTION_STARTED, error); } } } } } - static async sendSmtpError(tenantID: string, sourceData: SmtpErrorNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + static async sendSmtpError(tenant: Tenant, sourceData: SmtpErrorNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID, 'sendSmtpError'); + const adminUsers = await NotificationHandler.getAdminUsers(tenant, 'sendSmtpError'); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -572,14 +618,14 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSource( - tenantID, notificationSource.channel, ServerAction.EMAIL_SERVER_ERROR, + tenant, notificationSource.channel, ServerAction.EMAIL_SERVER_ERROR, null, null, { intervalMins: 60 }); if (!hasBeenNotified) { // Email enabled? if (NotificationHandler.notificationConfig.Email.enabled) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.EMAIL_SERVER_ERROR, { notificationData: { SMTPError: sourceData.SMTPError } }); + tenant, notificationSource.channel, null, ServerAction.EMAIL_SERVER_ERROR, { notificationData: { SMTPError: sourceData.SMTPError } }); // Send for (const adminUser of adminUsers) { await notificationSource.notificationTask.sendSmtpError( @@ -588,7 +634,7 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.EMAIL_SERVER_ERROR, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.EMAIL_SERVER_ERROR, error); } } } @@ -596,13 +642,16 @@ export default class NotificationHandler { } } - public static async sendOCPIPatchChargingStationsStatusesError(tenantID: string, sourceData: OCPIPatchChargingStationsStatusesErrorNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + public static async sendOCPIPatchChargingStationsStatusesError(tenant: Tenant, sourceData: OCPIPatchChargingStationsStatusesErrorNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID, 'sendOcpiPatchStatusError'); + const adminUsers = await NotificationHandler.getAdminUsers(tenant, 'sendOcpiPatchStatusError'); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -611,7 +660,7 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSource( - tenantID, notificationSource.channel, ServerAction.PATCH_EVSE_STATUS_ERROR, + tenant, notificationSource.channel, ServerAction.PATCH_EVSE_STATUS_ERROR, null, null, { intervalMins: 60 }); // Notified? if (!hasBeenNotified) { @@ -619,7 +668,7 @@ export default class NotificationHandler { if (notificationSource.enabled) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.PATCH_EVSE_STATUS_ERROR, { + tenant, notificationSource.channel, null, ServerAction.PATCH_EVSE_STATUS_ERROR, { notificationData: { location: sourceData.location } @@ -633,7 +682,7 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.PATCH_EVSE_STATUS_ERROR, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.PATCH_EVSE_STATUS_ERROR, error); } } } @@ -641,13 +690,16 @@ export default class NotificationHandler { } } - public static async sendOICPPatchChargingStationsStatusesError(tenantID: string, sourceData: OICPPatchChargingStationsStatusesErrorNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID); + public static async sendOICPPatchChargingStationsStatusesError(tenant: Tenant, sourceData: OICPPatchChargingStationsStatusesErrorNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Get admin users - const adminUsers = await NotificationHandler.getAdminUsers(tenantID, 'sendOicpPatchStatusError'); + const adminUsers = await NotificationHandler.getAdminUsers(tenant, 'sendOicpPatchStatusError'); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -656,7 +708,7 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSource( - tenantID, notificationSource.channel, ServerAction.PATCH_EVSE_STATUS_ERROR, + tenant, notificationSource.channel, ServerAction.PATCH_EVSE_STATUS_ERROR, null, null, { intervalMins: 60 }); // Notified? if (!hasBeenNotified) { @@ -664,7 +716,7 @@ export default class NotificationHandler { if (notificationSource.enabled) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.PATCH_EVSE_STATUS_ERROR); + tenant, notificationSource.channel, null, ServerAction.PATCH_EVSE_STATUS_ERROR); // Send for (const adminUser of adminUsers) { await notificationSource.notificationTask.sendOICPPatchChargingStationsStatusesError( @@ -673,7 +725,7 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.PATCH_EVSE_STATUS_ERROR, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.PATCH_EVSE_STATUS_ERROR, error); } } } @@ -681,13 +733,16 @@ export default class NotificationHandler { } } - public static async sendOICPPatchChargingStationsError(tenantID: string, sourceData: OICPPatchChargingStationsErrorNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID); + public static async sendOICPPatchChargingStationsError(tenant: Tenant, sourceData: OICPPatchChargingStationsErrorNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Get admin users - const adminUsers = await NotificationHandler.getAdminUsers(tenantID, 'sendOicpPatchStatusError'); + const adminUsers = await NotificationHandler.getAdminUsers(tenant, 'sendOicpPatchStatusError'); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -696,7 +751,7 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSource( - tenantID, notificationSource.channel, ServerAction.PATCH_EVSE_ERROR, + tenant, notificationSource.channel, ServerAction.PATCH_EVSE_ERROR, null, null, { intervalMins: 60 }); // Notified? if (!hasBeenNotified) { @@ -704,7 +759,7 @@ export default class NotificationHandler { if (notificationSource.enabled) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.PATCH_EVSE_ERROR); + tenant, notificationSource.channel, null, ServerAction.PATCH_EVSE_ERROR); // Send for (const adminUser of adminUsers) { await notificationSource.notificationTask.sendOICPPatchChargingStationsError( @@ -713,7 +768,7 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.PATCH_EVSE_ERROR, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.PATCH_EVSE_ERROR, error); } } } @@ -721,10 +776,13 @@ export default class NotificationHandler { } } - public static async sendUserAccountInactivity(tenantID: string, user: User, sourceData: UserAccountInactivityNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + public static async sendUserAccountInactivity(tenant: Tenant, user: User, sourceData: UserAccountInactivityNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -733,28 +791,31 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSource( - tenantID, notificationSource.channel, ServerAction.USER_ACCOUNT_INACTIVITY, + tenant, notificationSource.channel, ServerAction.USER_ACCOUNT_INACTIVITY, null, user.id, { intervalMins: 60 * 24 * 30 }); if (!hasBeenNotified) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.USER_ACCOUNT_INACTIVITY, { user }); + tenant, notificationSource.channel, null, ServerAction.USER_ACCOUNT_INACTIVITY, { user }); // Send await notificationSource.notificationTask.sendUserAccountInactivity( sourceData, user, tenant, NotificationSeverity.INFO); } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.USER_ACCOUNT_INACTIVITY, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.USER_ACCOUNT_INACTIVITY, error); } } } } } - public static async sendPreparingSessionNotStarted(tenantID: string, chargingStation: ChargingStation, user: User, sourceData: PreparingSessionNotStartedNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + public static async sendPreparingSessionNotStarted(tenant: Tenant, chargingStation: ChargingStation, user: User, sourceData: PreparingSessionNotStartedNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -763,14 +824,14 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSource( - tenantID, notificationSource.channel, ServerAction.PREPARING_SESSION_NOT_STARTED, + tenant, notificationSource.channel, ServerAction.PREPARING_SESSION_NOT_STARTED, chargingStation.id, user.id, { intervalMins: 15 }); if (!hasBeenNotified) { // Enabled? if (user.notificationsActive && user.notifications.sendPreparingSessionNotStarted) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.PREPARING_SESSION_NOT_STARTED, { + tenant, notificationSource.channel, null, ServerAction.PREPARING_SESSION_NOT_STARTED, { user, chargingStation, notificationData: { @@ -783,20 +844,23 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.PREPARING_SESSION_NOT_STARTED, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.PREPARING_SESSION_NOT_STARTED, error); } } } } } - public static async sendOfflineChargingStations(tenantID: string, sourceData: OfflineChargingStationNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + public static async sendOfflineChargingStations(tenant: Tenant, sourceData: OfflineChargingStationNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID); + const adminUsers = await NotificationHandler.getAdminUsers(tenant); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -805,13 +869,13 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSource( - tenantID, notificationSource.channel, ServerAction.OFFLINE_CHARGING_STATIONS, + tenant, notificationSource.channel, ServerAction.OFFLINE_CHARGING_STATIONS, null, null, { intervalMins: 60 * 24 }); // Notified? if (!hasBeenNotified) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.OFFLINE_CHARGING_STATIONS); + tenant, notificationSource.channel, null, ServerAction.OFFLINE_CHARGING_STATIONS); // Send for (const adminUser of adminUsers) { // Enabled? @@ -822,7 +886,7 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.OFFLINE_CHARGING_STATIONS, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.OFFLINE_CHARGING_STATIONS, error); } } } @@ -830,13 +894,16 @@ export default class NotificationHandler { } } - public static async sendBillingSynchronizationFailed(tenantID: string, sourceData: BillingUserSynchronizationFailedNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + public static async sendBillingSynchronizationFailed(tenant: Tenant, sourceData: BillingUserSynchronizationFailedNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID); + const adminUsers = await NotificationHandler.getAdminUsers(tenant); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -845,12 +912,12 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSourceByID( - tenantID, notificationSource.channel, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED); + tenant, notificationSource.channel, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED); // Notified? if (!hasBeenNotified) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED); + tenant, notificationSource.channel, null, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED); // Send for (const adminUser of adminUsers) { // Enabled? @@ -861,7 +928,7 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED, error); } } } @@ -869,13 +936,16 @@ export default class NotificationHandler { } } - public static async sendBillingInvoicesSynchronizationFailed(tenantID: string, sourceData: BillingInvoiceSynchronizationFailedNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + public static async sendBillingInvoicesSynchronizationFailed(tenant: Tenant, sourceData: BillingInvoiceSynchronizationFailedNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID); + const adminUsers = await NotificationHandler.getAdminUsers(tenant); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -884,12 +954,12 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSourceByID( - tenantID, notificationSource.channel, ServerAction.BILLING_INVOICE_SYNCHRONIZATION_FAILED); + tenant, notificationSource.channel, ServerAction.BILLING_INVOICE_SYNCHRONIZATION_FAILED); // Notified? if (!hasBeenNotified) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.BILLING_INVOICE_SYNCHRONIZATION_FAILED); + tenant, notificationSource.channel, null, ServerAction.BILLING_INVOICE_SYNCHRONIZATION_FAILED); // Send for (const adminUser of adminUsers) { // Enabled? @@ -900,7 +970,7 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED, error); } } } @@ -908,13 +978,16 @@ export default class NotificationHandler { } } - public static async sendBillingPeriodicOperationFailed(tenantID: string, sourceData: BillingInvoiceSynchronizationFailedNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + public static async sendBillingPeriodicOperationFailed(tenant: Tenant, sourceData: BillingInvoiceSynchronizationFailedNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID); + const adminUsers = await NotificationHandler.getAdminUsers(tenant); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -923,12 +996,12 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSourceByID( - tenantID, notificationSource.channel, ServerAction.BILLING_PERFORM_OPERATIONS); + tenant, notificationSource.channel, ServerAction.BILLING_PERFORM_OPERATIONS); // Notified? if (!hasBeenNotified) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.BILLING_PERFORM_OPERATIONS); + tenant, notificationSource.channel, null, ServerAction.BILLING_PERFORM_OPERATIONS); // Send for (const adminUser of adminUsers) { // Enabled? @@ -939,7 +1012,7 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.BILLING_PERFORM_OPERATIONS, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.BILLING_PERFORM_OPERATIONS, error); } } } @@ -948,8 +1021,10 @@ export default class NotificationHandler { } public static async sendCarsSynchronizationFailed(sourceData: CarCatalogSynchronizationFailedNotification): Promise { + // Get the tenant + const tenant = await TenantStorage.getTenant(Constants.DEFAULT_TENANT); // Get admin users - const adminUsers = await NotificationHandler.getAdminUsers(Constants.DEFAULT_TENANT); + const adminUsers = await NotificationHandler.getAdminUsers(tenant); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -958,13 +1033,13 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSource( - Constants.DEFAULT_TENANT, notificationSource.channel, ServerAction.CAR_CATALOG_SYNCHRONIZATION_FAILED, + tenant, notificationSource.channel, ServerAction.CAR_CATALOG_SYNCHRONIZATION_FAILED, null, null, { intervalMins: 60 * 24 }); // Notified? if (!hasBeenNotified) { // Save await NotificationHandler.saveNotification( - Constants.DEFAULT_TENANT, notificationSource.channel, null, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED); + tenant, notificationSource.channel, null, ServerAction.BILLING_USER_SYNCHRONIZATION_FAILED); // Send for (const adminUser of adminUsers) { // Enabled? @@ -982,13 +1057,16 @@ export default class NotificationHandler { } } - public static async sendComputeAndApplyChargingProfilesFailed(tenantID: string, chargingStation: ChargingStation, sourceData: ComputeAndApplyChargingProfilesFailedNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + public static async sendComputeAndApplyChargingProfilesFailed(tenant: Tenant, chargingStation: ChargingStation, sourceData: ComputeAndApplyChargingProfilesFailedNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID); + const adminUsers = await NotificationHandler.getAdminUsers(tenant); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -997,13 +1075,13 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSource( - tenantID, notificationSource.channel, ServerAction.COMPUTE_AND_APPLY_CHARGING_PROFILES_FAILED, + tenant, notificationSource.channel, ServerAction.COMPUTE_AND_APPLY_CHARGING_PROFILES_FAILED, sourceData.chargeBoxID, null, { intervalMins: 60 * 24 }); // Notified? if (!hasBeenNotified) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.COMPUTE_AND_APPLY_CHARGING_PROFILES_FAILED, { chargingStation: chargingStation }); + tenant, notificationSource.channel, null, ServerAction.COMPUTE_AND_APPLY_CHARGING_PROFILES_FAILED, { chargingStation: chargingStation }); // Send for (const adminUser of adminUsers) { // Enabled? @@ -1014,7 +1092,7 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.COMPUTE_AND_APPLY_CHARGING_PROFILES_FAILED, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.COMPUTE_AND_APPLY_CHARGING_PROFILES_FAILED, error); } } } @@ -1022,13 +1100,16 @@ export default class NotificationHandler { } } - public static async sendEndUserErrorNotification(tenantID: string, sourceData: EndUserErrorNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + public static async sendEndUserErrorNotification(tenant: Tenant, sourceData: EndUserErrorNotification): Promise { + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // Enrich with admins - const adminUsers = await NotificationHandler.getAdminUsers(tenantID); + const adminUsers = await NotificationHandler.getAdminUsers(tenant); if (!Utils.isEmptyArray(adminUsers)) { // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -1037,7 +1118,7 @@ export default class NotificationHandler { try { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, null, ServerAction.END_USER_REPORT_ERROR, { + tenant, notificationSource.channel, null, ServerAction.END_USER_REPORT_ERROR, { notificationData: { 'userID': sourceData.userID, } @@ -1051,7 +1132,7 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.END_USER_REPORT_ERROR, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.END_USER_REPORT_ERROR, error); } } } @@ -1059,11 +1140,14 @@ export default class NotificationHandler { } } - public static async sendSessionNotStarted(tenantID: string, notificationID: string, chargingStation: ChargingStation, + public static async sendSessionNotStarted(tenant: Tenant, notificationID: string, chargingStation: ChargingStation, sourceData: SessionNotStartedNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -1072,13 +1156,13 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSourceByID( - tenantID, notificationSource.channel, notificationID); + tenant, notificationSource.channel, notificationID); if (!hasBeenNotified) { // Enabled? if (sourceData.user.notificationsActive && sourceData.user.notifications.sendSessionNotStarted) { // Save await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.SESSION_NOT_STARTED_AFTER_AUTHORIZE, { + tenant, notificationSource.channel, notificationID, ServerAction.SESSION_NOT_STARTED_AFTER_AUTHORIZE, { user: sourceData.user, chargingStation }); @@ -1087,18 +1171,21 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.OPTIMAL_CHARGE_REACHED, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.OPTIMAL_CHARGE_REACHED, error); } } } } } - public static async sendBillingNewInvoiceNotification(tenantID: string, notificationID: string, user: User, + public static async sendBillingNewInvoiceNotification(tenant: Tenant, notificationID: string, user: User, sourceData: BillingNewInvoiceNotification): Promise { - if (tenantID !== Constants.DEFAULT_TENANT) { - // Get the Tenant - const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + if (tenant.id !== Constants.DEFAULT_TENANT) { + // Get the Tenant logo + if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') { + const tenantLogo = await TenantStorage.getTenantLogo(tenant.id); + tenant.logo = tenantLogo.logo; + } sourceData.tenantLogoURL = tenant.logo; // For each Sources for (const notificationSource of NotificationHandler.notificationSources) { @@ -1107,13 +1194,13 @@ export default class NotificationHandler { try { // Check notification const hasBeenNotified = await NotificationHandler.hasNotifiedSourceByID( - tenantID, notificationSource.channel, notificationID); + tenant, notificationSource.channel, notificationID); if (!hasBeenNotified) { // Enabled? if (sourceData.user.notificationsActive && sourceData.user.notifications.sendBillingNewInvoice) { if (sourceData.invoiceStatus) { await NotificationHandler.saveNotification( - tenantID, notificationSource.channel, notificationID, ServerAction.BILLING_NEW_INVOICE, { user }); + tenant, notificationSource.channel, notificationID, ServerAction.BILLING_NEW_INVOICE, { user }); // Send await notificationSource.notificationTask.sendBillingNewInvoice( sourceData, user, tenant, NotificationSeverity.INFO); @@ -1121,17 +1208,17 @@ export default class NotificationHandler { } } } catch (error) { - await Logging.logActionExceptionMessage(tenantID, ServerAction.BILLING_NEW_INVOICE, error); + await Logging.logActionExceptionMessage(tenant.id, ServerAction.BILLING_NEW_INVOICE, error); } } } } } - private static async saveNotification(tenantID: string, channel: string, notificationID: string, sourceDescr: ServerAction, + private static async saveNotification(tenant: Tenant, channel: string, notificationID: string, sourceDescr: ServerAction, extraParams: { user?: User, chargingStation?: ChargingStation, notificationData?: any } = {}): Promise { // Save it - await NotificationStorage.saveNotification(tenantID, { + await NotificationStorage.saveNotification(tenant, { timestamp: new Date(), channel: channel, sourceId: notificationID ? notificationID : new Date().toISOString(), @@ -1144,7 +1231,7 @@ export default class NotificationHandler { if (extraParams.user) { // User await Logging.logDebug({ - tenantID: tenantID, + tenantID: tenant.id, source: (extraParams.chargingStation ? extraParams.chargingStation.id : null), module: MODULE_NAME, method: 'saveNotification', action: sourceDescr, @@ -1154,7 +1241,7 @@ export default class NotificationHandler { } else { // Admin await Logging.logDebug({ - tenantID: tenantID, + tenantID: tenant.id, source: (extraParams.chargingStation ? extraParams.chargingStation.id : null), module: MODULE_NAME, method: 'saveNotification', action: sourceDescr, diff --git a/src/notification/email/EMailNotificationTask.ts b/src/notification/email/EMailNotificationTask.ts index 877e0ae742..13da4cea67 100644 --- a/src/notification/email/EMailNotificationTask.ts +++ b/src/notification/email/EMailNotificationTask.ts @@ -284,7 +284,7 @@ export default class EMailNotificationTask implements NotificationTask { if (sendSmtpError) { // TODO: Circular deps: src/notification/NotificationHandler.ts -> src/notification/email/EMailNotificationTask.ts -> src/notification/NotificationHandler.ts await NotificationHandler.sendSmtpError( - tenant.id, + tenant, { SMTPError: error, evseDashboardURL: data.evseDashboardURL diff --git a/src/scheduler/tasks/BillingPeriodicOperationTask.ts b/src/scheduler/tasks/BillingPeriodicOperationTask.ts index c3d33515a2..bcc242812d 100644 --- a/src/scheduler/tasks/BillingPeriodicOperationTask.ts +++ b/src/scheduler/tasks/BillingPeriodicOperationTask.ts @@ -20,7 +20,7 @@ export default class BillingPeriodicOperationTask extends SchedulerTask { const chargeActionResults = await billingImpl.chargeInvoices(); if (chargeActionResults.inError > 0) { await NotificationHandler.sendBillingPeriodicOperationFailed( - tenant.id, + tenant, { nbrInvoicesInError: chargeActionResults.inError, evseDashboardURL: Utils.buildEvseURL(tenant.subdomain), diff --git a/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts b/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts index e30d89f4ae..f0609a1e86 100644 --- a/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts +++ b/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts @@ -66,7 +66,7 @@ export default class CheckOfflineChargingStationsTask extends SchedulerTask { const chargingStationIDs: string = chargingStations.result.map((chargingStation) => chargingStation.id).join(', '); // Send notification await NotificationHandler.sendOfflineChargingStations( - tenant.id, { + tenant, { chargeBoxIDs: chargingStationIDs, evseDashboardURL: Utils.buildEvseURL(tenant.subdomain) } diff --git a/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts b/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts index 3e0901ac0c..f355217033 100644 --- a/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts +++ b/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts @@ -29,13 +29,13 @@ export default class CheckPreparingSessionNotStartedTask extends SchedulerTask { // Get site owner and then send notification if (chargingStation.siteArea && chargingStation.siteArea.siteID) { // Get Site Owners - const siteOwners = await SiteStorage.getSiteUsers(tenant.id, + const siteOwners = await SiteStorage.getSiteUsers(tenant, { siteIDs: [ chargingStation.siteArea.siteID ], siteOwnerOnly: true }, Constants.DB_PARAMS_MAX_LIMIT); if (siteOwners && siteOwners.count > 0) { // Send notification moment.locale(siteOwners.result[0].user.locale); for (const connector of chargingStation.connectors) { - await NotificationHandler.sendPreparingSessionNotStarted(tenant.id, chargingStation, siteOwners.result[0].user, { + await NotificationHandler.sendPreparingSessionNotStarted(tenant, chargingStation, siteOwners.result[0].user, { user: siteOwners.result[0].user, chargeBoxID: chargingStation.id, connectorId: Utils.getConnectorLetterFromConnectorID(connector.connectorId), diff --git a/src/scheduler/tasks/CheckSessionNotStartedAfterAuthorizeTask.ts b/src/scheduler/tasks/CheckSessionNotStartedAfterAuthorizeTask.ts index 03151d995f..0dee9e0463 100644 --- a/src/scheduler/tasks/CheckSessionNotStartedAfterAuthorizeTask.ts +++ b/src/scheduler/tasks/CheckSessionNotStartedAfterAuthorizeTask.ts @@ -23,7 +23,7 @@ export default class CheckSessionNotStartedAfterAuthorizeTask extends SchedulerT }); if (notificationTransactionNotStarted.result && notificationTransactionNotStarted.result.length > 0) { for (const notification of notificationTransactionNotStarted.result) { - await NotificationHandler.sendSessionNotStarted(tenant.id, notification.tagID + '-' + notification.authDate.toString(), notification.chargingStation, { + await NotificationHandler.sendSessionNotStarted(tenant, notification.tagID + '-' + notification.authDate.toString(), notification.chargingStation, { user: notification.user, chargeBoxID: notification.chargingStation.id, evseDashboardChargingStationURL: Utils.buildEvseChargingStationURL(tenant.subdomain, notification.chargingStation, '#all'), diff --git a/src/scheduler/tasks/CheckUserAccountInactivityTask.ts b/src/scheduler/tasks/CheckUserAccountInactivityTask.ts index 3f2ee11891..701489d8bc 100644 --- a/src/scheduler/tasks/CheckUserAccountInactivityTask.ts +++ b/src/scheduler/tasks/CheckUserAccountInactivityTask.ts @@ -31,7 +31,7 @@ export default class CheckUserAccountInactivityTask extends SchedulerTask { // Notification moment.locale(user.locale); await NotificationHandler.sendUserAccountInactivity( - tenant.id, + tenant, user, { 'user': user, diff --git a/src/scheduler/tasks/SynchronizeBillingInvoicesTask.ts b/src/scheduler/tasks/SynchronizeBillingInvoicesTask.ts index dd05936a44..b3d7507708 100644 --- a/src/scheduler/tasks/SynchronizeBillingInvoicesTask.ts +++ b/src/scheduler/tasks/SynchronizeBillingInvoicesTask.ts @@ -21,7 +21,7 @@ export default class SynchronizeBillingInvoicesTask extends SchedulerTask { const synchronizeActionResults = await billingImpl.synchronizeInvoices(); if (synchronizeActionResults.inError > 0) { await NotificationHandler.sendBillingInvoicesSynchronizationFailed( - tenant.id, + tenant, { nbrInvoicesInError: synchronizeActionResults.inError, evseDashboardURL: Utils.buildEvseURL(tenant.subdomain), diff --git a/src/scheduler/tasks/SynchronizeBillingUsersTask.ts b/src/scheduler/tasks/SynchronizeBillingUsersTask.ts index d0da64e06a..2c77c25cd9 100644 --- a/src/scheduler/tasks/SynchronizeBillingUsersTask.ts +++ b/src/scheduler/tasks/SynchronizeBillingUsersTask.ts @@ -20,7 +20,7 @@ export default class SynchronizeBillingUsersTask extends SchedulerTask { const synchronizeAction = await billingImpl.synchronizeUsers(); if (synchronizeAction.inError > 0) { await NotificationHandler.sendBillingSynchronizationFailed( - tenant.id, + tenant, { nbrUsersInError: synchronizeAction.inError, evseDashboardURL: Utils.buildEvseURL(tenant.subdomain), diff --git a/src/server/ServerUtils.ts b/src/server/ServerUtils.ts index 34a70a8faf..ef72cf2b86 100644 --- a/src/server/ServerUtils.ts +++ b/src/server/ServerUtils.ts @@ -86,7 +86,7 @@ export class ServerUtils { } else if (!serverConfig.host && serverConfig.port) { httpServer.listen(serverConfig.port, cb); } else { - // eslint-disable-next-line no-console + // eslint-disable-next-line no-console console.log(`Fail to start ${serverName} Server listening ${cluster.isWorker ? 'in worker ' + cluster.worker.id.toString() : 'in master'}, missing required port configuration`); } } diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts index 355a0091e8..73aeae9bbf 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts @@ -30,7 +30,6 @@ import { OCPIResponse } from '../../../../types/ocpi/OCPIResponse'; import { OCPIRole } from '../../../../types/ocpi/OCPIRole'; import { OCPIStatusCode } from '../../../../types/ocpi/OCPIStatusCode'; import OCPIUtils from '../../OCPIUtils'; -import { ObjectID } from 'mongodb'; import { PricingSource } from '../../../../types/Pricing'; import RoamingUtils from '../../../../utils/RoamingUtils'; import { ServerAction } from '../../../../types/Server'; @@ -116,7 +115,7 @@ export default class OCPIUtilsService { // Result const ocpiLocationsResult: DataResult = { count: 0, result: [] }; // Get all sites - const sites = await SiteStorage.getSites(tenant.id, + const sites = await SiteStorage.getSites(tenant, { issuer: true, public: true }, limit === 0 ? Constants.DB_PARAMS_MAX_LIMIT : { limit, skip }, ['id', 'name', 'address', 'lastChangedOn', 'createdOn']); @@ -127,7 +126,7 @@ export default class OCPIUtilsService { } let nbrOfSites = sites.count; if (nbrOfSites === -1) { - const sitesCount = await SiteStorage.getSites(tenant.id, + const sitesCount = await SiteStorage.getSites(tenant, { issuer: true, public: true }, Constants.DB_PARAMS_COUNT_ONLY); nbrOfSites = sitesCount.count; } @@ -628,7 +627,6 @@ export default class OCPIUtilsService { } const tagToSave = { id: token.uid, - visualID: new ObjectID().toString(), issuer: false, userID: emspUser.id, active: token.valid === true ? true : false, @@ -669,7 +667,6 @@ export default class OCPIUtilsService { await UserStorage.saveUserStatus(tenant.id, emspUser.id, UserStatus.ACTIVE); const tagToSave = { id: token.uid, - visualID: token.visual_number, issuer: false, userID: emspUser.id, active: token.valid === true ? true : false, @@ -882,7 +879,18 @@ export default class OCPIUtilsService { return 'FR*ISE_Payant1'; // Properphi case '603655d291930d0014017e0a': - return 'Tarif_EVSE_DC'; + switch (chargingStation?.siteAreaID) { + // F3C Baume les dames + case '60990f1cc48de10014ea4fdc': + switch (chargingStation?.id) { + case 'F3CBaume-CAHORS24DC': + return 'Tarif_EVSE_DC'; + case 'F3CBaume-LAFON22AC': + return 'Tarif_EVSE_AC'; + } + return ''; + } + return ''; } return ''; } diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOLocationsEndpoint.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOLocationsEndpoint.ts index 9e58ea7a1f..f259a781b1 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOLocationsEndpoint.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOLocationsEndpoint.ts @@ -122,7 +122,7 @@ export default class CPOLocationsEndpoint extends AbstractEndpoint { private async getLocation(tenant: Tenant, locationId: string, options: OCPILocationOptions): Promise { // Get site - const site = await SiteStorage.getSite(tenant.id, locationId); + const site = await SiteStorage.getSite(tenant, locationId); if (site) { return await OCPIUtilsService.convertSite2Location(tenant, site, options, true); } diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPLocationsEndpoint.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPLocationsEndpoint.ts index 686d1922b5..adeef65543 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPLocationsEndpoint.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPLocationsEndpoint.ts @@ -211,7 +211,7 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint { // Update Location const company = await ocpiClient.checkAndGetCompany(); const siteName = OCPIUtils.buildOperatorName(countryCode, partyId); - const sites = await SiteStorage.getSites(tenant.id, { companyIDs: [company.id], name: siteName }, Constants.DB_PARAMS_SINGLE_RECORD); + const sites = await SiteStorage.getSites(tenant, { companyIDs: [company.id], name: siteName }, Constants.DB_PARAMS_SINGLE_RECORD); await ocpiClient.processLocation(location, company, sites.result); } return OCPIUtils.success(); diff --git a/src/server/ocpp/services/OCPPService.ts b/src/server/ocpp/services/OCPPService.ts index ae4041a4ef..d1d5361db3 100644 --- a/src/server/ocpp/services/OCPPService.ts +++ b/src/server/ocpp/services/OCPPService.ts @@ -281,7 +281,7 @@ export default class OCPPService { action: ServerAction.METER_VALUES, user: transaction.userID, module: MODULE_NAME, method: 'handleMeterValues', - message: `Connector ID '${meterValues.connectorId.toString()}' > Transaction ID '${meterValues.transactionId.toString()}' > MeterValue have been saved`, + message: `${OCPPUtils.buildConnectorInfo(meterValues.connectorId, meterValues.transactionId)} MeterValue have been saved`, detailedMessages: { headers, normalizedMeterValues } }); } finally { @@ -449,7 +449,7 @@ export default class OCPPService { source: chargingStation.id, module: MODULE_NAME, method: 'handleStartTransaction', action: ServerAction.START_TRANSACTION, user: user, - message: `Connector ID '${transaction.connectorId}' > Transaction ID '${transaction.id}' has been started` + message: `${OCPPUtils.buildConnectorInfo(transaction.connectorId, transaction.id)} Transaction has been started successfully` }); // Accepted return { @@ -567,7 +567,7 @@ export default class OCPPService { action: ServerAction.STOP_TRANSACTION, user: alternateUser ?? (user ?? null), actionOnUser: alternateUser ? (user ?? null) : null, - message: `Connector ID '${transaction.connectorId}' > Transaction ID '${transaction.id}' has been stopped successfully`, + message: `${OCPPUtils.buildConnectorInfo(transaction.connectorId, transaction.id)} Transaction has been stopped successfully`, detailedMessages: { headers, stopTransaction } }); // Accepted @@ -630,7 +630,7 @@ export default class OCPPService { throw new BackendError({ source: chargingStation.id, module: MODULE_NAME, method: 'checkAuthorizeStopTransactionAndGetUsers', - message: `Connector ID '${transaction.connectorId.toString()}' > Transaction ID '${transaction.id.toString()}' > Transaction has already been stopped`, + message: `${OCPPUtils.buildConnectorInfo(transaction.connectorId, transaction.id)} Transaction has already been stopped`, action: ServerAction.STOP_TRANSACTION, user: (alternateUser ? alternateUser : user), actionOnUser: (alternateUser ? user : null), @@ -656,7 +656,7 @@ export default class OCPPService { source: chargingStation.id, module: MODULE_NAME, method: 'triggerSmartChargingStopTransaction', action: ServerAction.STOP_TRANSACTION, - message: `Connector ID '${transaction.connectorId.toString()}' > Transaction ID '${transaction.id.toString()}' > Smart Charging exception occurred`, + message: `${OCPPUtils.buildConnectorInfo(transaction.connectorId, transaction.id)} Smart Charging exception occurred`, detailedMessages: { error: error.message, stack: error.stack, transaction, chargingStation } }); } @@ -679,7 +679,7 @@ export default class OCPPService { tenantID: tenant.id, source: transaction.chargeBoxID, action: ServerAction.CHARGING_PROFILE_DELETE, - message: `Connector ID '${transaction.connectorId}' > Transaction ID '${transaction.id}' > TX Charging Profile with ID '${chargingProfile.id}'`, + message: `${OCPPUtils.buildConnectorInfo(transaction.connectorId, transaction.id)} TX Charging Profile with ID '${chargingProfile.id}'`, module: MODULE_NAME, method: 'deleteAllTransactionTxProfile', detailedMessages: { chargingProfile } }); @@ -688,7 +688,7 @@ export default class OCPPService { tenantID: tenant.id, source: transaction.chargeBoxID, action: ServerAction.CHARGING_PROFILE_DELETE, - message: `Connector ID '${transaction.connectorId}' > Transaction ID '${transaction.id}' > Cannot delete TX Charging Profile with ID '${chargingProfile.id}'`, + message: `${OCPPUtils.buildConnectorInfo(transaction.connectorId, transaction.id)} Cannot delete TX Charging Profile with ID '${chargingProfile.id}'`, module: MODULE_NAME, method: 'deleteAllTransactionTxProfile', detailedMessages: { error: error.message, stack: error.stack, chargingProfile } }); @@ -733,7 +733,7 @@ export default class OCPPService { source: chargingStation.id, module: MODULE_NAME, method: 'processConnectorStatusNotification', action: ServerAction.STATUS_NOTIFICATION, - message: `Connector ID '${statusNotification.connectorId}' > Transaction ID '${connector.currentTransactionID}' > Status has been saved: '${this.buildConnectorStatusDescription(connector)}'`, + message: `${OCPPUtils.buildConnectorInfo(statusNotification.connectorId, connector.currentTransactionID)} Status '${this.buildConnectorStatusDescription(connector)}' has been saved`, detailedMessages: [statusNotification, connector] }); // Notify Users @@ -753,7 +753,7 @@ export default class OCPPService { source: chargingStation.id, module: MODULE_NAME, method: 'processSmartChargingStatusNotification', action: ServerAction.STATUS_NOTIFICATION, - message: `Connector ID '${connector.connectorId.toString()}' > Transaction ID '${connector.currentTransactionID.toString()}' > Smart Charging exception occurred`, + message: `${OCPPUtils.buildConnectorInfo(connector.connectorId, connector.currentTransactionID)} Smart Charging exception occurred`, detailedMessages: { error: error.message, stack: error.stack } }); } @@ -785,7 +785,7 @@ export default class OCPPService { source: chargingStation.id, module: MODULE_NAME, method: 'hasStatusNotificationChanged', action: ServerAction.STATUS_NOTIFICATION, - message: `Connector ID '${statusNotification.connectorId}' > Transaction ID '${connector.currentTransactionID}' > Status has not changed: '${this.buildConnectorStatusDescription(connector)}'`, + message: `${OCPPUtils.buildConnectorInfo(statusNotification.connectorId, connector.currentTransactionID)} Status has not changed: '${this.buildConnectorStatusDescription(connector)}'`, detailedMessages: { connector: connector } }); return false; @@ -868,7 +868,7 @@ export default class OCPPService { source: chargingStation.id, module: MODULE_NAME, method: 'checkAndUpdateLastCompletedTransaction', action: ServerAction.STATUS_NOTIFICATION, - message: `Connector ID '${lastTransaction.connectorId}' > Transaction ID '${lastTransaction.id}' > Extra Inactivity is negative and will be ignored: ${lastTransaction.stop.extraInactivitySecs} secs`, + message: `${OCPPUtils.buildConnectorInfo(lastTransaction.connectorId, lastTransaction.id)} Extra Inactivity is negative and will be ignored: ${lastTransaction.stop.extraInactivitySecs} secs`, detailedMessages: { statusNotification } }); lastTransaction.stop.extraInactivitySecs = 0; @@ -884,7 +884,7 @@ export default class OCPPService { user: lastTransaction.userID, module: MODULE_NAME, method: 'checkAndUpdateLastCompletedTransaction', action: ServerAction.EXTRA_INACTIVITY, - message: `Connector ID '${lastTransaction.connectorId}' > Transaction ID '${lastTransaction.id}' > Extra Inactivity of ${lastTransaction.stop.extraInactivitySecs} secs has been added`, + message: `${OCPPUtils.buildConnectorInfo(lastTransaction.connectorId, lastTransaction.id)} Extra Inactivity of ${lastTransaction.stop.extraInactivitySecs} secs has been added`, detailedMessages: [statusNotification, connector, lastTransaction] }); } @@ -897,7 +897,7 @@ export default class OCPPService { user: lastTransaction.userID, module: MODULE_NAME, method: 'checkAndUpdateLastCompletedTransaction', action: ServerAction.EXTRA_INACTIVITY, - message: `Connector ID '${lastTransaction.connectorId}' > Transaction ID '${lastTransaction.id}' > No Extra Inactivity for this transaction`, + message: `${OCPPUtils.buildConnectorInfo(lastTransaction.connectorId, lastTransaction.id)} No Extra Inactivity for this transaction`, detailedMessages: [statusNotification, connector, lastTransaction] }); } @@ -921,7 +921,7 @@ export default class OCPPService { source: chargingStation.id, module: MODULE_NAME, method: 'checkAndUpdateLastCompletedTransaction', action: ServerAction.STATUS_NOTIFICATION, - message: `Connector ID '${lastTransaction.connectorId}' > Transaction ID '${lastTransaction.id}' > Received Status Notification '${statusNotification.status}' on Connector ID ${lastTransaction.connectorId ?? 'unknown'} while a transaction is ongoing, expect inconsistencies in the inactivity time computation. Ask charging station vendor to fix the firmware`, + message: `${OCPPUtils.buildConnectorInfo(lastTransaction.connectorId, lastTransaction.id)} Received Status Notification '${statusNotification.status}' on Connector ID ${lastTransaction.connectorId ?? 'unknown'} while a transaction is ongoing, expect inconsistencies in the inactivity time computation. Ask charging station vendor to fix the firmware`, detailedMessages: { statusNotification } }); } @@ -1015,7 +1015,7 @@ export default class OCPPService { }); // Send Notification (Async) NotificationHandler.sendChargingStationStatusError( - tenant.id, + tenant, Utils.generateUUID(), chargingStation, { @@ -1214,7 +1214,7 @@ export default class OCPPService { module: MODULE_NAME, method: 'updateChargingStationWithTransaction', action: ServerAction.CONSUMPTION, user: transaction.userID, - message: `Connector ID '${foundConnector.connectorId}' > Transaction ID '${foundConnector.currentTransactionID}' > Instant: ${instantPower} kW, Total: ${totalConsumption} kW.h${foundConnector.currentStateOfCharge ? ', SoC: ' + foundConnector.currentStateOfCharge.toString() + ' %' : ''}` + message: `${OCPPUtils.buildConnectorInfo(foundConnector.connectorId, foundConnector.currentTransactionID)} Instant: ${instantPower} kW, Total: ${totalConsumption} kW.h${foundConnector.currentStateOfCharge ? ', SoC: ' + foundConnector.currentStateOfCharge.toString() + ' %' : ''}` }); // Cleanup connector transaction data } else if (foundConnector) { @@ -1228,7 +1228,7 @@ export default class OCPPService { const i18nManager = I18nManager.getInstanceForLocale(transaction.user.locale); // Notify (Async) NotificationHandler.sendEndOfCharge( - tenant.id, + tenant, transaction.user, chargingStation, { @@ -1252,7 +1252,7 @@ export default class OCPPService { const i18nManager = I18nManager.getInstanceForLocale(transaction.user.locale); // Notification Before End Of Charge (Async) NotificationHandler.sendOptimalChargeReached( - tenant.id, + tenant, transaction.id.toString() + '-OCR', transaction.user, chargingStation, @@ -1500,7 +1500,7 @@ export default class OCPPService { private notifyStartTransaction(tenant: Tenant, transaction: Transaction, chargingStation: ChargingStation, user: User) { if (user) { NotificationHandler.sendSessionStarted( - tenant.id, + tenant, transaction.id.toString(), user, chargingStation, @@ -1544,7 +1544,7 @@ export default class OCPPService { const i18nManager = I18nManager.getInstanceForLocale(user.locale); // Send Notification (Async) NotificationHandler.sendEndOfSession( - tenant.id, + tenant, transaction.id.toString() + '-EOS', user, chargingStation, @@ -1566,7 +1566,7 @@ export default class OCPPService { if (transaction.stop.signedData !== '') { // Send Notification (Async) NotificationHandler.sendEndOfSignedSession( - tenant.id, + tenant, transaction.id.toString() + '-EOSS', user, chargingStation, @@ -1639,7 +1639,7 @@ export default class OCPPService { source: chargingStation.id, module: MODULE_NAME, method: 'clearChargingStationConnectorRuntimeData', action: ServerAction.START_TRANSACTION, user: user, - message: `Missing connector '${transaction.connectorId}' > Transaction ID '${transaction.id}'` + message: `${OCPPUtils.buildConnectorInfo(transaction.connectorId, transaction.id)} Connector does not exist` }); } // Update lastSeen @@ -1888,7 +1888,7 @@ export default class OCPPService { private notifyBootNotification(tenant: Tenant, chargingStation: ChargingStation) { void NotificationHandler.sendChargingStationRegistered( - tenant.id, + tenant, Utils.generateUUID(), chargingStation, { @@ -2042,7 +2042,7 @@ export default class OCPPService { source: chargingStation.id, module: MODULE_NAME, method: 'getTransactionFromMeterValues', action: ServerAction.METER_VALUES, - message: `Connector ID '${meterValues.connectorId.toString()}' > Transaction ID '${meterValues.transactionId.toString()}' > Meter Values received after the 'Transaction.End' Meter Values`, + message: `${OCPPUtils.buildConnectorInfo(meterValues.connectorId, meterValues.transactionId)} Meter Values received after the 'Transaction.End' Meter Values`, detailedMessages: { headers, meterValues } }); } @@ -2103,7 +2103,7 @@ export default class OCPPService { source: chargingStation.id, action: ServerAction.STOP_TRANSACTION, module: MODULE_NAME, method: 'checkAndApplyLastConsumptionInStopTransaction', - message: `Connector ID '${transaction.connectorId}' > Transaction ID '${transaction.id}' > Transaction.End consumption '${transaction.lastConsumption.value}' differs from Stop Transaction '${stopTransaction.meterStop}'`, + message: `${OCPPUtils.buildConnectorInfo(transaction.connectorId, transaction.id)} Transaction.End consumption '${transaction.lastConsumption.value}' differs from Stop Transaction '${stopTransaction.meterStop}'`, detailedMessages: { stopTransaction, transaction } }); } diff --git a/src/server/ocpp/utils/OCPPUtils.ts b/src/server/ocpp/utils/OCPPUtils.ts index 2906426e89..be426e0b3b 100644 --- a/src/server/ocpp/utils/OCPPUtils.ts +++ b/src/server/ocpp/utils/OCPPUtils.ts @@ -74,13 +74,21 @@ export default class OCPPUtils { action: ServerAction.ROAMING, user: transaction.userID, module: MODULE_NAME, method: 'processTransactionRoaming', - message: `Connector ID '${transaction.connectorId}' > Transaction ID '${transaction.id}' > Roaming exception occurred: ${error.message as string}`, + message: `${OCPPUtils.buildConnectorInfo(transaction.connectorId, transaction.id)} Roaming exception occurred: ${error.message as string}`, detailedMessages: { error: error.message, stack: error.stack } }); } } } + public static buildConnectorInfo(connectorID: number, transactionID: number): string { + let connectorInfo = `Connector ID '${connectorID}' >`; + if (transactionID > 0) { + connectorInfo += ` Transaction ID '${transactionID}' >`; + } + return connectorInfo; + } + public static async processOICPTransaction(tenant: Tenant, transaction: Transaction, chargingStation: ChargingStation, transactionAction: TransactionAction): Promise { if (!transaction.user || transaction.user.issuer) { @@ -1241,11 +1249,11 @@ export default class OCPPUtils { // Request and save the latest OCPP parameters let result = await Utils.executePromiseWithTimeout( Constants.DELAY_CHANGE_CONFIGURATION_EXECUTION_MILLIS, OCPPUtils.requestAndSaveChargingStationOcppParameters(tenant, chargingStation), - `Time out error (${Constants.DELAY_CHANGE_CONFIGURATION_EXECUTION_MILLIS.toString()}ms) in requesting OCPP Parameters`); + `Time out error (${Constants.DELAY_CHANGE_CONFIGURATION_EXECUTION_MILLIS.toString()} ms) in requesting OCPP Parameters`); // Update the OCPP Parameters from the template result = await Utils.executePromiseWithTimeout( Constants.DELAY_CHANGE_CONFIGURATION_EXECUTION_MILLIS, OCPPUtils.updateChargingStationOcppParametersWithTemplate(tenant, chargingStation), - `Time out error (${Constants.DELAY_CHANGE_CONFIGURATION_EXECUTION_MILLIS}ms) in updating OCPP Parameters`); + `Time out error (${Constants.DELAY_CHANGE_CONFIGURATION_EXECUTION_MILLIS} ms) in updating OCPP Parameters`); if (result.status !== OCPPConfigurationStatus.ACCEPTED) { await Logging.logError({ tenantID: tenant.id, diff --git a/src/server/rest/CentralRestServerService.ts b/src/server/rest/CentralRestServerService.ts index 6685cfc158..c663dda1b3 100644 --- a/src/server/rest/CentralRestServerService.ts +++ b/src/server/rest/CentralRestServerService.ts @@ -34,31 +34,11 @@ class RequestMapper { switch (httpVerb) { // Create case 'POST': - this.registerOneActionManyPaths( - async (action: ServerAction, req: Request, res: Response, next: NextFunction) => { - // Delegate - await ChargingStationService.handleAction(action, req, res, next); - }, - ServerAction.CHARGING_STATION_CLEAR_CACHE, - ServerAction.CHARGING_STATION_GET_CONFIGURATION, - ServerAction.CHARGING_STATION_CHANGE_CONFIGURATION, - ServerAction.CHARGING_STATION_REMOTE_STOP_TRANSACTION, - ServerAction.CHARGING_STATION_REMOTE_START_TRANSACTION, - ServerAction.CHARGING_STATION_UNLOCK_CONNECTOR, - ServerAction.CHARGING_STATION_RESET, - ServerAction.CHARGING_STATION_SET_CHARGING_PROFILE, - ServerAction.CHARGING_STATION_GET_COMPOSITE_SCHEDULE, - ServerAction.CHARGING_STATION_CLEAR_CHARGING_PROFILE, - ServerAction.CHARGING_STATION_GET_DIAGNOSTICS, - ServerAction.CHARGING_STATION_CHANGE_AVAILABILITY, - ServerAction.CHARGING_STATION_UPDATE_FIRMWARE - ); // Register REST actions this.registerJsonActionsPaths({ [ServerAction.ADD_CHARGING_STATIONS_TO_SITE_AREA]: SiteAreaService.handleAssignChargingStationsToSiteArea.bind(this), [ServerAction.REMOVE_CHARGING_STATIONS_FROM_SITE_AREA]: SiteAreaService.handleAssignChargingStationsToSiteArea.bind(this), [ServerAction.REGISTRATION_TOKEN_CREATE]: RegistrationTokenService.handleCreateRegistrationToken.bind(this), - [ServerAction.USER_CREATE]: UserService.handleCreateUser.bind(this), [ServerAction.COMPANY_CREATE]: CompanyService.handleCreateCompany.bind(this), [ServerAction.ADD_ASSET_TO_SITE_AREA]: SiteAreaService.handleAssignAssetsToSiteArea.bind(this), [ServerAction.REMOVE_ASSET_TO_SITE_AREA]: SiteAreaService.handleAssignAssetsToSiteArea.bind(this), @@ -67,8 +47,6 @@ class RequestMapper { [ServerAction.SITE_CREATE]: SiteService.handleCreateSite.bind(this), [ServerAction.ADD_USERS_TO_SITE]: SiteService.handleAssignUsersToSite.bind(this), [ServerAction.REMOVE_USERS_FROM_SITE]: SiteService.handleAssignUsersToSite.bind(this), - [ServerAction.ADD_SITES_TO_USER]: UserService.handleAssignSitesToUser.bind(this), - [ServerAction.REMOVE_SITES_FROM_USER]: UserService.handleAssignSitesToUser.bind(this), [ServerAction.SITE_AREA_CREATE]: SiteAreaService.handleCreateSiteArea.bind(this), [ServerAction.TRANSACTIONS_REFUND]: TransactionService.handleRefundTransactions.bind(this), [ServerAction.TRANSACTION_PUSH_CDR]: TransactionService.handlePushTransactionCdr.bind(this), @@ -102,7 +80,6 @@ class RequestMapper { [ServerAction.CAR_CREATE]: CarService.handleCreateCar.bind(this), [ServerAction.TAG_CREATE]: TagService.handleCreateTag.bind(this), [ServerAction.END_USER_REPORT_ERROR]: NotificationService.handleEndUserReportError.bind(this), - [ServerAction.USERS_IMPORT]: UserService.handleImportUsers.bind(this), [ServerAction.TAGS_IMPORT]: TagService.handleImportTags.bind(this), }); break; @@ -122,18 +99,8 @@ class RequestMapper { [ServerAction.CAR]: CarService.handleGetCar.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), - [ServerAction.CHARGING_STATIONS_OCPP_PARAMS_EXPORT]: ChargingStationService.handleExportChargingStationsOCPPParams.bind(this), - [ServerAction.CHARGING_STATION]: ChargingStationService.handleGetChargingStation.bind(this), - [ServerAction.CHECK_SMART_CHARGING_CONNECTION]: ChargingStationService.handleCheckSmartChargingConnection.bind(this), - [ServerAction.CHARGING_PROFILES]: ChargingStationService.handleGetChargingProfiles.bind(this), - [ServerAction.TRIGGER_SMART_CHARGING]: ChargingStationService.handleTriggerSmartCharging.bind(this), - [ServerAction.GENERATE_QR_CODE_FOR_CONNECTOR]: ChargingStationService.handleGenerateQrCodeForConnector.bind(this), - [ServerAction.CHARGING_STATION_DOWNLOAD_QR_CODE_PDF]: ChargingStationService.handleDownloadQrCodesPdf.bind(this), [ServerAction.REGISTRATION_TOKENS]: RegistrationTokenService.handleGetRegistrationTokens.bind(this), [ServerAction.REGISTRATION_TOKEN]: RegistrationTokenService.handleGetRegistrationToken.bind(this), - [ServerAction.STATUS_NOTIFICATIONS]: ChargingStationService.handleGetStatusNotifications.bind(this), - [ServerAction.BOOT_NOTIFICATION]: ChargingStationService.handleGetBootNotifications.bind(this), [ServerAction.COMPANIES]: CompanyService.handleGetCompanies.bind(this), [ServerAction.COMPANY]: CompanyService.handleGetCompany.bind(this), [ServerAction.ASSETS]: AssetService.handleGetAssets.bind(this), @@ -152,16 +119,10 @@ class RequestMapper { [ServerAction.SITE_AREA]: SiteAreaService.handleGetSiteArea.bind(this), [ServerAction.SITE_AREA_CONSUMPTION]: SiteAreaService.handleGetSiteAreaConsumption.bind(this), [ServerAction.USERS]: UserService.handleGetUsers.bind(this), - [ServerAction.USER_SITES]: UserService.handleGetSites.bind(this), - [ServerAction.USERS_IN_ERROR]: UserService.handleGetUsersInError.bind(this), - [ServerAction.USER_IMAGE]: UserService.handleGetUserImage.bind(this), - [ServerAction.USER]: UserService.handleGetUser.bind(this), - [ServerAction.USERS_EXPORT]: UserService.handleExportUsers.bind(this), [ServerAction.NOTIFICATIONS]: NotificationService.handleGetNotifications.bind(this), [ServerAction.TAGS]: TagService.handleGetTags.bind(this), [ServerAction.TAG]: TagService.handleGetTag.bind(this), [ServerAction.TAGS_EXPORT]: TagService.handleExportTags.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), @@ -187,8 +148,6 @@ class RequestMapper { [ServerAction.CHARGING_STATION_TRANSACTIONS]: TransactionService.handleGetChargingStationTransactions.bind(this), [ServerAction.TRANSACTION]: TransactionService.handleGetTransaction.bind(this), [ServerAction.TRANSACTION_CONSUMPTION]: TransactionService.handleGetTransactionConsumption.bind(this), - [ServerAction.CHARGING_STATIONS_OCPP_PARAMETERS]: ChargingStationService.handleGetChargingStationOcppParameters.bind(this), - [ServerAction.CHARGING_STATIONS_IN_ERROR]: ChargingStationService.handleGetChargingStationsInError.bind(this), [ServerAction.SETTING_BY_IDENTIFIER]: SettingService.handleGetSettingByIdentifier.bind(this), [ServerAction.SETTINGS]: SettingService.handleGetSettings.bind(this), [ServerAction.SETTING]: SettingService.handleGetSetting.bind(this), @@ -213,11 +172,6 @@ class RequestMapper { case 'PUT': // Register REST actions this.registerJsonActionsPaths({ - [ServerAction.USER_UPDATE]: UserService.handleUpdateUser.bind(this), - [ServerAction.USER_UPDATE_MOBILE_TOKEN]: UserService.handleUpdateUserMobileToken.bind(this), - [ServerAction.CHARGING_STATION_UPDATE_PARAMS]: ChargingStationService.handleUpdateChargingStationParams.bind(this), - [ServerAction.CHARGING_STATION_LIMIT_POWER]: ChargingStationService.handleChargingStationLimitPower.bind(this), - [ServerAction.CHARGING_PROFILE_UPDATE]: ChargingStationService.handleUpdateChargingProfile.bind(this), [ServerAction.TENANT_UPDATE]: TenantService.handleUpdateTenant.bind(this), [ServerAction.SITE_UPDATE]: SiteService.handleUpdateSite.bind(this), [ServerAction.SITE_AREA_UPDATE]: SiteAreaService.handleUpdateSiteArea.bind(this), @@ -246,7 +200,6 @@ class RequestMapper { case 'DELETE': // Register REST actions this.registerJsonActionsPaths({ - [ServerAction.USER_DELETE]: UserService.handleDeleteUser.bind(this), [ServerAction.TENANT_DELETE]: TenantService.handleDeleteTenant.bind(this), [ServerAction.REGISTRATION_TOKEN_DELETE]: RegistrationTokenService.handleDeleteRegistrationToken.bind(this), [ServerAction.REGISTRATION_TOKEN_REVOKE]: RegistrationTokenService.handleRevokeRegistrationToken.bind(this), @@ -254,8 +207,6 @@ class RequestMapper { [ServerAction.SITE_AREA_DELETE]: SiteAreaService.handleDeleteSiteArea.bind(this), [ServerAction.COMPANY_DELETE]: CompanyService.handleDeleteCompany.bind(this), [ServerAction.ASSET_DELETE]: AssetService.handleDeleteAsset.bind(this), - [ServerAction.CHARGING_STATION_DELETE]: ChargingStationService.handleDeleteChargingStation.bind(this), - [ServerAction.CHARGING_PROFILE_DELETE]: ChargingStationService.handleDeleteChargingProfile.bind(this), [ServerAction.TRANSACTION_DELETE]: TransactionService.handleDeleteTransaction.bind(this), [ServerAction.TRANSACTIONS_DELETE]: TransactionService.handleDeleteTransactions.bind(this), [ServerAction.INTEGRATION_CONNECTION_DELETE]: ConnectionService.handleDeleteConnection.bind(this), @@ -333,10 +284,6 @@ export default class CentralRestServerService { case ServerAction.TENANT_LOGO: await TenantService.handleGetTenantLogo(action, req, res, next); break; - // Firmware Download - case ServerAction.FIRMWARE_DOWNLOAD: - await ChargingStationService.handleGetFirmware(action, req, res, next); - break; default: // Delegate await UtilsService.handleUnknownAction(action, req, res, next); diff --git a/src/server/rest/v1/router/api/TagRouter.ts b/src/server/rest/v1/router/api/TagRouter.ts index a65edf269d..a066ce085d 100644 --- a/src/server/rest/v1/router/api/TagRouter.ts +++ b/src/server/rest/v1/router/api/TagRouter.ts @@ -17,7 +17,10 @@ export default class TagRouter { this.buildRouteTag(); this.buildRouteCreateTag(); this.buildRouteDeleteTag(); + this.buildRouteDeleteTags(); this.buildRouteUpdateTag(); + this.buildRouteImportTag(); + this.buildRouteExportTag(); return this.router; } @@ -48,9 +51,27 @@ export default class TagRouter { }); } + protected buildRouteDeleteTags(): void { + this.router.delete(`/${ServerRoute.REST_TAGS}`, async (req: Request, res: Response, next: NextFunction) => { + await RouterUtils.handleServerAction(TagService.handleDeleteTags.bind(this), ServerAction.TAGS_DELETE, req, res, next); + }); + } + protected buildRouteUpdateTag(): void { this.router.put(`/${ServerRoute.REST_TAG}`, async (req: Request, res: Response, next: NextFunction) => { await RouterUtils.handleServerAction(TagService.handleUpdateTag.bind(this), ServerAction.TAG_UPDATE, req, res, next); }); } + + protected buildRouteImportTag(): void { + this.router.post(`/${ServerRoute.REST_TAG_IMPORT}`, async (req: Request, res: Response, next: NextFunction) => { + await RouterUtils.handleServerAction(TagService.handleImportTags.bind(this), ServerAction.TAGS_IMPORT, req, res, next); + }); + } + + protected buildRouteExportTag(): void { + this.router.get(`/${ServerRoute.REST_TAG_EXPORT}`, async (req: Request, res: Response, next: NextFunction) => { + await RouterUtils.handleServerAction(TagService.handleExportTags.bind(this), ServerAction.TAGS_EXPORT, req, res, next); + }); + } } diff --git a/src/server/rest/v1/router/api/UserRouter.ts b/src/server/rest/v1/router/api/UserRouter.ts index 9c8ff9425e..2faafbe0d3 100644 --- a/src/server/rest/v1/router/api/UserRouter.ts +++ b/src/server/rest/v1/router/api/UserRouter.ts @@ -110,7 +110,7 @@ export default class UserRouter { } protected buildRouteUserImport(): void { - this.router.get(`/${ServerRoute.REST_USERS_IMPORT}`, async (req: Request, res: Response, next: NextFunction) => { + this.router.post(`/${ServerRoute.REST_USERS_IMPORT}`, async (req: Request, res: Response, next: NextFunction) => { await RouterUtils.handleServerAction(UserService.handleImportUsers.bind(this), ServerAction.USERS_IMPORT, req, res, next); }); } diff --git a/src/server/rest/v1/service/AuthService.ts b/src/server/rest/v1/service/AuthService.ts index 10c4519715..b0e628ae08 100644 --- a/src/server/rest/v1/service/AuthService.ts +++ b/src/server/rest/v1/service/AuthService.ts @@ -14,7 +14,6 @@ import { HTTPError } from '../../../../types/HTTPError'; import I18nManager from '../../../../utils/I18nManager'; import Logging from '../../../../utils/Logging'; import NotificationHandler from '../../../../notification/NotificationHandler'; -import { ObjectID } from 'mongodb'; import { ServerAction } from '../../../../types/Server'; import SessionHashService from './SessionHashService'; import SettingStorage from '../../../../storage/mongodb/SettingStorage'; @@ -22,6 +21,7 @@ import SiteStorage from '../../../../storage/mongodb/SiteStorage'; import { StatusCodes } from 'http-status-codes'; import Tag from '../../../../types/Tag'; import TagStorage from '../../../../storage/mongodb/TagStorage'; +import Tenant from '../../../../types/Tenant'; import TenantStorage from '../../../../storage/mongodb/TenantStorage'; import UserStorage from '../../../../storage/mongodb/UserStorage'; import Utils from '../../../../utils/Utils'; @@ -138,8 +138,8 @@ export default class AuthService { filteredRequest.locale = Constants.DEFAULT_LOCALE; } // Get the Tenant - const tenantID = await AuthService.getTenantID(filteredRequest.tenant); - if (!tenantID) { + const tenant = await AuthService.getTenant(filteredRequest.tenant); + if (!tenant.id) { throw new AppError({ source: Constants.CENTRAL_SERVER, errorCode: HTTPError.OBJECT_DOES_NOT_EXIST_ERROR, @@ -148,10 +148,10 @@ export default class AuthService { method: 'handleRegisterUser' }); } - req.user = { tenantID }; + req.user = { tenantID: tenant.id }; // Check Captcha const recaptchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${_centralSystemRestConfig.captchaSecretKey}&response=${filteredRequest.captcha}&remoteip=${req.connection.remoteAddress}`; - const response = await AxiosFactory.getAxiosInstance(tenantID).get(recaptchaURL); + const response = await AxiosFactory.getAxiosInstance(tenant.id).get(recaptchaURL); if (!response.data.success) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -170,10 +170,9 @@ export default class AuthService { }); } // Check Mandatory field - Object.assign(filteredRequest, { password: filteredRequest.passwords.password }); UtilsService.checkIfUserValid(filteredRequest as User, null, req); // Check email - const user = await UserStorage.getUserByEmail(tenantID, filteredRequest.email); + const user = await UserStorage.getUserByEmail(tenant.id, filteredRequest.email); if (user) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -184,7 +183,7 @@ export default class AuthService { }); } // Generate a password - const newPasswordHashed = await Utils.hashPasswordBcrypt(filteredRequest.passwords.password); + const newPasswordHashed = await Utils.hashPasswordBcrypt(filteredRequest.password); // Create the user const newUser = UserStorage.createNewUser() as User; newUser.email = filteredRequest.email; @@ -193,22 +192,21 @@ export default class AuthService { newUser.locale = filteredRequest.locale; newUser.createdOn = new Date(); const verificationToken = Utils.generateToken(filteredRequest.email); - const endUserLicenseAgreement = await UserStorage.getEndUserLicenseAgreement(tenantID, Utils.getLanguageFromLocale(newUser.locale)); + const endUserLicenseAgreement = await UserStorage.getEndUserLicenseAgreement(tenant.id, Utils.getLanguageFromLocale(newUser.locale)); // Save User - newUser.id = await UserStorage.saveUser(tenantID, newUser); + newUser.id = await UserStorage.saveUser(tenant.id, newUser); // Save User Status - if (tenantID === Constants.DEFAULT_TENANT) { - await UserStorage.saveUserRole(tenantID, newUser.id, UserRole.SUPER_ADMIN); + if (tenant.id === Constants.DEFAULT_TENANT) { + await UserStorage.saveUserRole(tenant.id, newUser.id, UserRole.SUPER_ADMIN); } else { - await UserStorage.saveUserRole(tenantID, newUser.id, UserRole.BASIC); + await UserStorage.saveUserRole(tenant.id, newUser.id, UserRole.BASIC); } // Save User Status - await UserStorage.saveUserStatus(tenantID, newUser.id, UserStatus.PENDING); + await UserStorage.saveUserStatus(tenant.id, newUser.id, UserStatus.PENDING); // Get the i18n translation class const i18nManager = I18nManager.getInstanceForLocale(newUser.locale); const tag: Tag = { id: Utils.generateTagID(newUser.name, newUser.firstName), - visualID: new ObjectID().toString(), active: true, issuer: true, userID: newUser.id, @@ -219,7 +217,7 @@ export default class AuthService { }; await TagStorage.saveTag(req.user.tenantID, tag); // Save User password - await UserStorage.saveUserPassword(tenantID, newUser.id, + await UserStorage.saveUserPassword(tenant.id, newUser.id, { password: newPasswordHashed, passwordWrongNbrTrials: 0, @@ -227,41 +225,41 @@ export default class AuthService { passwordBlockedUntil: null }); // Save User Account Verification - await UserStorage.saveUserAccountVerification(tenantID, newUser.id, { verificationToken }); + await UserStorage.saveUserAccountVerification(tenant.id, newUser.id, { verificationToken }); // Save User EULA - await UserStorage.saveUserEULA(tenantID, newUser.id, + await UserStorage.saveUserEULA(tenant.id, newUser.id, { eulaAcceptedOn: new Date(), eulaAcceptedVersion: endUserLicenseAgreement.version, eulaAcceptedHash: endUserLicenseAgreement.hash }); // Assign user to all sites with auto-assign flag set - const sites = await SiteStorage.getSites(tenantID, + const sites = await SiteStorage.getSites(tenant, { withAutoUserAssignment: true }, Constants.DB_PARAMS_MAX_LIMIT ); if (sites.count > 0) { const siteIDs = sites.result.map((site) => site.id); if (siteIDs && siteIDs.length > 0) { - await UserStorage.addSitesToUser(tenantID, newUser.id, siteIDs); + await UserStorage.addSitesToUser(tenant.id, newUser.id, siteIDs); } } // Log await Logging.logSecurityInfo({ - tenantID: tenantID, + tenantID: tenant.id, user: newUser, action: action, module: MODULE_NAME, method: 'handleRegisterUser', message: `User with Email '${req.body.email as string}' has been created successfully`, detailedMessages: { params: req.body } }); - if (tenantID !== Constants.DEFAULT_TENANT) { + if (tenant.id !== Constants.DEFAULT_TENANT) { // Send notification const evseDashboardVerifyEmailURL = Utils.buildEvseURL(filteredRequest.tenant) + '/verify-email?VerificationToken=' + verificationToken + '&Email=' + newUser.email; // Notify (Async) NotificationHandler.sendNewRegisteredUser( - tenantID, + tenant, Utils.generateUUID(), newUser, { @@ -272,12 +270,11 @@ export default class AuthService { } ).catch(() => { }); } - // Ok res.json(Constants.REST_RESPONSE_SUCCESS); next(); } - public static async checkAndSendResetPasswordConfirmationEmail(tenantID: string, filteredRequest: Partial, action: ServerAction, req: Request, + public static async checkAndSendResetPasswordConfirmationEmail(tenant: Tenant, filteredRequest: Partial, action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { // No hash: Send email with init pass hash link if (!filteredRequest.captcha) { @@ -291,7 +288,7 @@ export default class AuthService { } // Check captcha const recaptchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${_centralSystemRestConfig.captchaSecretKey}&response=${filteredRequest.captcha}&remoteip=${req.connection.remoteAddress}`; - const response = await AxiosFactory.getAxiosInstance(tenantID).get(recaptchaURL); + const response = await AxiosFactory.getAxiosInstance(tenant.id).get(recaptchaURL); // Check if (!response.data.success) { throw new AppError({ @@ -311,15 +308,15 @@ export default class AuthService { }); } // Generate a new password - const user = await UserStorage.getUserByEmail(tenantID, filteredRequest.email); + const user = await UserStorage.getUserByEmail(tenant.id, filteredRequest.email); UtilsService.assertObjectExists(action, user, `User with email '${filteredRequest.email}' does not exist`, MODULE_NAME, 'checkAndSendResetPasswordConfirmationEmail', req.user); const resetHash = Utils.generateUUID(); // Init Password info - await UserStorage.saveUserPassword(tenantID, user.id, { passwordResetHash: resetHash }); + await UserStorage.saveUserPassword(tenant.id, user.id, { passwordResetHash: resetHash }); // Log await Logging.logSecurityInfo({ - tenantID: tenantID, + tenantID: tenant.id, user: user, action: action, module: MODULE_NAME, method: 'checkAndSendResetPasswordConfirmationEmail', @@ -330,7 +327,7 @@ export default class AuthService { '/define-password?hash=' + resetHash; // Send Request Password (Async) NotificationHandler.sendRequestPassword( - tenantID, + tenant, Utils.generateUUID(), user, { @@ -339,7 +336,6 @@ export default class AuthService { 'evseDashboardResetPassURL': evseDashboardResetPassURL } ).catch(() => { }); - // Ok res.json(Constants.REST_RESPONSE_SUCCESS); next(); } @@ -349,19 +345,8 @@ export default class AuthService { const user = await UserStorage.getUserByPasswordResetHash(tenantID, filteredRequest.hash); UtilsService.assertObjectExists(action, user, `User with password reset hash '${filteredRequest.hash}' does not exist`, MODULE_NAME, 'handleUserPasswordReset', req.user); - // Check password - if (!Utils.isPasswordValid(filteredRequest.passwords.password)) { - throw new AppError({ - source: Constants.CENTRAL_SERVER, - errorCode: HTTPError.OBJECT_DOES_NOT_EXIST_ERROR, - message: `Password with reset hash '${filteredRequest.hash}' is not strong enough`, - module: MODULE_NAME, - user: user, - method: 'handleUserPasswordReset' - }); - } // Hash it - const newHashedPassword = await Utils.hashPasswordBcrypt(filteredRequest.passwords.password); + const newHashedPassword = await Utils.hashPasswordBcrypt(filteredRequest.password); // Save new password await UserStorage.saveUserPassword(tenantID, user.id, { @@ -384,7 +369,6 @@ export default class AuthService { message: 'User\'s password has been reset successfully', detailedMessages: { params: req.body } }); - // Ok res.json(Constants.REST_RESPONSE_SUCCESS); next(); } @@ -392,8 +376,8 @@ export default class AuthService { public static async handleUserPasswordReset(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { const filteredRequest = AuthValidator.getInstance().validateAuthResetPassword(req.body); // Get Tenant - const tenantID = await AuthService.getTenantID(filteredRequest.tenant); - if (!tenantID) { + const tenant = await AuthService.getTenant(filteredRequest.tenant); + if (!tenant) { throw new AppError({ source: Constants.CENTRAL_SERVER, errorCode: HTTPError.OBJECT_DOES_NOT_EXIST_ERROR, @@ -406,10 +390,10 @@ export default class AuthService { // Check hash if (filteredRequest.hash) { // Send the new password - await AuthService.resetUserPassword(tenantID, filteredRequest, action, req, res, next); + await AuthService.resetUserPassword(tenant.id, filteredRequest, action, req, res, next); } else { // Send Confirmation Email for requesting a new password - await AuthService.checkAndSendResetPasswordConfirmationEmail(tenantID, filteredRequest, action, req, res, next); + await AuthService.checkAndSendResetPasswordConfirmationEmail(tenant, filteredRequest, action, req, res, next); } } @@ -461,8 +445,8 @@ export default class AuthService { // Filter const filteredRequest = AuthValidator.getInstance().validateAuthVerifyEmail(req.query); // Get Tenant - const tenantID = await AuthService.getTenantID(filteredRequest.Tenant); - if (!tenantID) { + const tenant = await AuthService.getTenant(filteredRequest.Tenant); + if (!tenant) { throw new AppError({ source: Constants.CENTRAL_SERVER, errorCode: HTTPError.OBJECT_DOES_NOT_EXIST_ERROR, @@ -473,7 +457,7 @@ export default class AuthService { }); } // Check that this is not the super tenant - if (tenantID === Constants.DEFAULT_TENANT) { + if (tenant.id === Constants.DEFAULT_TENANT) { throw new AppError({ source: Constants.CENTRAL_SERVER, errorCode: HTTPError.GENERAL_ERROR, @@ -482,8 +466,7 @@ export default class AuthService { message: 'Cannot verify email in the Super Tenant' }); } - // Get Tenant - const tenant = await TenantStorage.getTenant(tenantID); + if (!tenant) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -491,11 +474,11 @@ export default class AuthService { action: action, module: MODULE_NAME, method: 'handleVerifyEmail', - message: `Tenant ID '${tenantID}' does not exist!` + message: `Tenant ID '${tenant.id}' does not exist!` }); } // Check email - const user = await UserStorage.getUserByEmail(tenantID, filteredRequest.Email); + const user = await UserStorage.getUserByEmail(tenant.id, filteredRequest.Email); UtilsService.assertObjectExists(action, user, `User with email '${filteredRequest.Email}' does not exist`, MODULE_NAME, 'handleVerifyEmail', req.user); // Check if account is already active @@ -520,17 +503,17 @@ export default class AuthService { message: 'Wrong Verification Token' }); } - const userSettings = await SettingStorage.getUserSettings(tenantID); + const userSettings = await SettingStorage.getUserSettings(tenant.id); const userStatus = userSettings.user.autoActivateAccountAfterValidation ? UserStatus.ACTIVE : UserStatus.INACTIVE; // Save User Status - await UserStorage.saveUserStatus(tenantID, user.id, userStatus); + await UserStorage.saveUserStatus(tenant.id, user.id, userStatus); // For integration with billing const billingImpl = await BillingFactory.getBillingImpl(tenant); if (billingImpl) { try { await billingImpl.synchronizeUser(user); await Logging.logInfo({ - tenantID: tenantID, + tenantID: tenant.id, module: MODULE_NAME, method: 'handleVerifyEmail', action: action, user: user, @@ -538,7 +521,7 @@ export default class AuthService { }); } catch (error) { await Logging.logError({ - tenantID: tenantID, + tenantID: tenant.id, module: MODULE_NAME, method: 'handleVerifyEmail', action: action, user: user, @@ -548,11 +531,11 @@ export default class AuthService { } } // Save User Verification Account - await UserStorage.saveUserAccountVerification(tenantID, user.id, + await UserStorage.saveUserAccountVerification(tenant.id, user.id, { verificationToken: null, verifiedAt: new Date() }); // Log await Logging.logSecurityInfo({ - tenantID: tenantID, + tenantID: tenant.id, user: user, action: action, module: MODULE_NAME, method: 'handleVerifyEmail', message: userStatus === UserStatus.ACTIVE ? @@ -561,7 +544,7 @@ export default class AuthService { detailedMessages: { params: req.query } }); NotificationHandler.sendAccountVerification( - tenantID, + tenant, Utils.generateUUID(), user, { @@ -570,7 +553,6 @@ export default class AuthService { 'evseDashboardURL': Utils.buildEvseURL(filteredRequest.Tenant), } ).catch(() => { }); - // Ok res.json({ status: 'Success', userStatus }); next(); } @@ -578,8 +560,8 @@ export default class AuthService { public static async handleResendVerificationEmail(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { const filteredRequest = AuthValidator.getInstance().validateAuthResendVerificationEmail(req.body); // Get the tenant - const tenantID = await AuthService.getTenantID(filteredRequest.tenant); - if (!tenantID) { + const tenant = await AuthService.getTenant(filteredRequest.tenant); + if (!tenant) { throw new AppError({ source: Constants.CENTRAL_SERVER, errorCode: HTTPError.OBJECT_DOES_NOT_EXIST_ERROR, @@ -590,7 +572,7 @@ export default class AuthService { }); } // Check that this is not the super tenant - if (tenantID === Constants.DEFAULT_TENANT) { + if (tenant.id === Constants.DEFAULT_TENANT) { throw new AppError({ source: Constants.CENTRAL_SERVER, errorCode: HTTPError.GENERAL_ERROR, @@ -603,7 +585,7 @@ export default class AuthService { // Is valid captcha? const recaptchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${_centralSystemRestConfig.captchaSecretKey}&response=${filteredRequest.captcha}&remoteip=${req.connection.remoteAddress}`; - const response = await AxiosFactory.getAxiosInstance(tenantID).get(recaptchaURL); + const response = await AxiosFactory.getAxiosInstance(tenant.id).get(recaptchaURL); if (!response.data.success) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -624,7 +606,7 @@ export default class AuthService { }); } // Is valid email? - const user = await UserStorage.getUserByEmail(tenantID, filteredRequest.email); + const user = await UserStorage.getUserByEmail(tenant.id, filteredRequest.email); UtilsService.assertObjectExists(action, user, `User with email '${filteredRequest.email}' does not exist`, MODULE_NAME, 'handleResendVerificationEmail', req.user); // Check if account is already active @@ -648,14 +630,14 @@ export default class AuthService { verificationToken = Utils.generateToken(filteredRequest.email); user.verificationToken = verificationToken; // Save User Verification Account - await UserStorage.saveUserAccountVerification(tenantID, user.id, { verificationToken }); + await UserStorage.saveUserAccountVerification(tenant.id, user.id, { verificationToken }); } else { // Get existing verificationToken verificationToken = user.verificationToken; } // Log await Logging.logSecurityInfo({ - tenantID: tenantID, + tenantID: tenant.id, user: user, action: action, module: MODULE_NAME, @@ -669,7 +651,7 @@ export default class AuthService { user.email; // Send Verification Email (Async) NotificationHandler.sendVerificationEmail( - tenantID, + tenant, Utils.generateUUID(), user, { @@ -678,7 +660,6 @@ export default class AuthService { 'evseDashboardVerifyEmailURL': evseDashboardVerifyEmailURL } ).catch(() => { }); - // Ok res.json(Constants.REST_RESPONSE_SUCCESS); next(); } @@ -785,6 +766,17 @@ export default class AuthService { return (tenant ? tenant.id : null); } + public static async getTenant(subdomain: string): Promise { + // Check + if (!subdomain) { + return Constants.DEFAULT_TENANT_OBJECT; + } + // Get it + const tenant = await TenantStorage.getTenantBySubdomain(subdomain); + // Return + return (tenant ? tenant : null); + } + public static async checkUserLogin(action: ServerAction, tenantID: string, user: User, filteredRequest: Partial, req: Request, res: Response, next: NextFunction): Promise { // User Found? @@ -852,4 +844,3 @@ export default class AuthService { } } } - diff --git a/src/server/rest/v1/service/AuthorizationService.ts b/src/server/rest/v1/service/AuthorizationService.ts index 29ebdbdb58..03ee48127e 100644 --- a/src/server/rest/v1/service/AuthorizationService.ts +++ b/src/server/rest/v1/service/AuthorizationService.ts @@ -695,9 +695,9 @@ export default class AuthorizationService { return userSites.result.map((userSite) => userSite.siteID); } - private static async getAssignedSiteIDs(tenantID: string, userToken: UserToken): Promise { + private static async getAssignedSiteIDs(tenant: Tenant, userToken: UserToken): Promise { // Get the Sites assigned to the User - const sites = await SiteStorage.getSites(tenantID, + const sites = await SiteStorage.getSites(tenant, { userID: userToken.id, issuer: true, @@ -725,7 +725,7 @@ export default class AuthorizationService { if (userToken.role !== UserRole.ADMIN && userToken.role !== UserRole.SUPER_ADMIN) { if (Utils.isTenantComponentActive(tenant, TenantComponents.ORGANIZATION)) { // Get assigned Site IDs assigned to user from DB - const siteIDs = await AuthorizationService.getAssignedSiteIDs(tenant.id, userToken); + const siteIDs = await AuthorizationService.getAssignedSiteIDs(tenant, userToken); if (!Utils.isEmptyArray(siteIDs)) { // Force the filter authorizationFilters.filters.siteIDs = siteIDs; diff --git a/src/server/rest/v1/service/ChargingStationService.ts b/src/server/rest/v1/service/ChargingStationService.ts index 364c4d361d..5738c10468 100644 --- a/src/server/rest/v1/service/ChargingStationService.ts +++ b/src/server/rest/v1/service/ChargingStationService.ts @@ -141,7 +141,7 @@ export default class ChargingStationService { partyID: oicpClient.getLocalPartyID(ServerAction.OICP_PUSH_EVSE_DATA) }; // Get Site - const site = await SiteStorage.getSite(req.tenant.id, chargingStation.siteID); + const site = await SiteStorage.getSite(req.tenant, chargingStation.siteID); // Push EVSE to OICP platform await oicpClient.pushEvseData(OICPUtils.convertChargingStation2MultipleEvses( site, chargingStation.siteArea, chargingStation, options), actionType); @@ -848,7 +848,7 @@ export default class ChargingStationService { partyID: oicpClient.getLocalPartyID(ServerAction.OICP_PUSH_EVSE_DATA) }; // Get Site - const site = await SiteStorage.getSite(req.tenant.id, chargingStation.siteID); + const site = await SiteStorage.getSite(req.tenant, chargingStation.siteID); // Push EVSE to OICP platform await oicpClient.pushEvseData(OICPUtils.convertChargingStation2MultipleEvses( site, chargingStation.siteArea, chargingStation, options), OICPActionType.DELETE); diff --git a/src/server/rest/v1/service/CompanyService.ts b/src/server/rest/v1/service/CompanyService.ts index 464821d831..ddda687398 100644 --- a/src/server/rest/v1/service/CompanyService.ts +++ b/src/server/rest/v1/service/CompanyService.ts @@ -68,6 +68,8 @@ export default class CompanyService { UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetCompanyLogo', req.user); // Fetch Tenant Object by Tenant ID const tenant = await TenantStorage.getTenant(filteredRequest.TenantID); + UtilsService.assertObjectExists(action, tenant, `Tenant ID '${filteredRequest.TenantID}' does not exist`, + MODULE_NAME, 'handleGetCompanyLogo', req.user); // Get the Logo const companyLogo = await CompanyStorage.getCompanyLogo(tenant, filteredRequest.ID); // Return diff --git a/src/server/rest/v1/service/NotificationService.ts b/src/server/rest/v1/service/NotificationService.ts index 27c4159d67..12c7c36160 100644 --- a/src/server/rest/v1/service/NotificationService.ts +++ b/src/server/rest/v1/service/NotificationService.ts @@ -31,7 +31,7 @@ export default class NotificationService { chargingStationProject = [ 'chargeBoxID' ]; } // Get the Notification - const notifications = await NotificationStorage.getNotifications(req.user.tenantID, { + const notifications = await NotificationStorage.getNotifications(req.tenant, { 'userID': filteredRequest.UserID, 'dateFrom': filteredRequest.DateFrom, 'channel': filteredRequest.Channel @@ -82,7 +82,7 @@ export default class NotificationService { evseDashboardURL: Utils.buildEvseURL(), }; // Send Notification - await NotificationHandler.sendEndUserErrorNotification(req.user.tenantID, endUserErrorNotification); + await NotificationHandler.sendEndUserErrorNotification(req.tenant, endUserErrorNotification); // Ok res.json(Constants.REST_RESPONSE_SUCCESS); next(); diff --git a/src/server/rest/v1/service/SiteService.ts b/src/server/rest/v1/service/SiteService.ts index 938c7a78ad..9fb2bf828f 100644 --- a/src/server/rest/v1/service/SiteService.ts +++ b/src/server/rest/v1/service/SiteService.ts @@ -14,6 +14,7 @@ import { SiteDataResult } from '../../../../types/DataResult'; import SiteSecurity from './security/SiteSecurity'; import SiteStorage from '../../../../storage/mongodb/SiteStorage'; import TenantComponents from '../../../../types/TenantComponents'; +import TenantStorage from '../../../../storage/mongodb/TenantStorage'; import Utils from '../../../../utils/Utils'; import UtilsService from './UtilsService'; @@ -72,7 +73,7 @@ export default class SiteService { const user = await UtilsService.checkAndGetUserAuthorization( req.tenant, req.user, filteredRequest.userID, Action.READ, action); // Update - await SiteStorage.updateSiteUserAdmin(req.user.tenantID, filteredRequest.siteID, filteredRequest.userID, filteredRequest.siteAdmin); + await SiteStorage.updateSiteUserAdmin(req.tenant, filteredRequest.siteID, filteredRequest.userID, filteredRequest.siteAdmin); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -126,7 +127,7 @@ export default class SiteService { const user = await UtilsService.checkAndGetUserAuthorization( req.tenant, req.user, filteredRequest.userID, Action.READ, action); // Update - await SiteStorage.updateSiteOwner(req.user.tenantID, filteredRequest.siteID, filteredRequest.userID, filteredRequest.siteOwner); + await SiteStorage.updateSiteOwner(req.tenant, filteredRequest.siteID, filteredRequest.userID, filteredRequest.siteOwner); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -153,9 +154,9 @@ export default class SiteService { req.tenant, req.user, site, filteredRequest.userIDs, action); // Save if (action === ServerAction.ADD_USERS_TO_SITE) { - await SiteStorage.addUsersToSite(req.user.tenantID, site.id, users.map((user) => user.id)); + await SiteStorage.addUsersToSite(req.tenant, site.id, users.map((user) => user.id)); } else { - await SiteStorage.removeUsersFromSite(req.user.tenantID, site.id, users.map((user) => user.id)); + await SiteStorage.removeUsersFromSite(req.tenant, site.id, users.map((user) => user.id)); } // Log await Logging.logSecurityInfo({ @@ -192,7 +193,7 @@ export default class SiteService { return; } // Get Users - const users = await SiteStorage.getSiteUsers(req.user.tenantID, + const users = await SiteStorage.getSiteUsers(req.tenant, { search: filteredRequest.Search, siteIDs: [ filteredRequest.SiteID ], @@ -220,7 +221,7 @@ export default class SiteService { const site = await UtilsService.checkAndGetSiteAuthorization( req.tenant, req.user, siteID, Action.DELETE, action); // Delete - await SiteStorage.deleteSite(req.user.tenantID, site.id); + await SiteStorage.deleteSite(req.tenant, site.id); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -264,7 +265,7 @@ export default class SiteService { return; } // Get the sites - const sites = await SiteStorage.getSites(req.user.tenantID, + const sites = await SiteStorage.getSites(req.tenant, { search: filteredRequest.Search, userID: filteredRequest.UserID, @@ -308,12 +309,12 @@ export default class SiteService { module: MODULE_NAME, method: 'handleGetSiteImage', }); } - // Get - const site = await SiteStorage.getSite(filteredRequest.TenantID, filteredRequest.ID); - UtilsService.assertObjectExists(action, site, `Site ID '${filteredRequest.ID}' does not exist`, - MODULE_NAME, 'handleDeleteSite', req.user); + // Get Tenant + const tenant = await TenantStorage.getTenant(filteredRequest.TenantID); + UtilsService.assertObjectExists(action, tenant, `Tenant ID '${filteredRequest.TenantID}' does not exist`, + MODULE_NAME, 'handleGetSiteImage', req.user); // Get the image - const siteImage = await SiteStorage.getSiteImage(filteredRequest.TenantID, filteredRequest.ID); + const siteImage = await SiteStorage.getSiteImage(tenant, filteredRequest.ID); if (siteImage?.image) { let header = 'image'; let encoding: BufferEncoding = 'base64'; @@ -370,7 +371,7 @@ export default class SiteService { createdOn: new Date() } as Site; // Save - site.id = await SiteStorage.saveSite(req.user.tenantID, site); + site.id = await SiteStorage.saveSite(req.tenant, site); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -415,7 +416,7 @@ export default class SiteService { site.lastChangedBy = { 'id': req.user.id }; site.lastChangedOn = new Date(); // Save - await SiteStorage.saveSite(req.user.tenantID, site, Utils.objectHasProperty(filteredRequest, 'image') ? true : false); + await SiteStorage.saveSite(req.tenant, site, Utils.objectHasProperty(filteredRequest, 'image') ? true : false); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, diff --git a/src/server/rest/v1/service/TagService.ts b/src/server/rest/v1/service/TagService.ts index 64d3f4b43d..027f52248c 100644 --- a/src/server/rest/v1/service/TagService.ts +++ b/src/server/rest/v1/service/TagService.ts @@ -26,7 +26,6 @@ import { OCPITokenWhitelist } from '../../../../types/ocpi/OCPIToken'; import OCPIUtils from '../../../ocpi/OCPIUtils'; import { ServerAction } from '../../../../types/Server'; import { StatusCodes } from 'http-status-codes'; -import TagSecurity from './security/TagSecurity'; import TagStorage from '../../../../storage/mongodb/TagStorage'; import TagValidator from '../validator/TagValidator'; import Tenant from '../../../../types/Tenant'; @@ -46,7 +45,7 @@ export default class TagService { public static async handleGetTag(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { // Filter request - const filteredRequest = TagSecurity.filterTagRequestByID(req.query); + const filteredRequest = TagValidator.getInstance().validateTagGetByID(req.query); // Check and Get Tag const tag = await UtilsService.checkAndGetTagAuthorization(req.tenant, req.user, filteredRequest.ID, Action.READ, action, { withUser: true }, true); @@ -63,7 +62,7 @@ export default class TagService { public static async handleDeleteTags(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { // Filter - const tagsIDs = TagSecurity.filterTagRequestByIDs(req.body); + const tagsIDs = TagValidator.getInstance().validateTagsDelete(req.body).tagsIDs; // Delete const result = await TagService.deleteTags(req.tenant, action, req.user, tagsIDs); // Return @@ -73,7 +72,7 @@ export default class TagService { public static async handleDeleteTag(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { // Filter - const filteredRequest = TagSecurity.filterTagRequestByID(req.query); + const filteredRequest = TagValidator.getInstance().validateTagGetByID(req.query); // Delete await TagService.deleteTags(req.tenant, action, req.user, [filteredRequest.ID]); // Return @@ -83,7 +82,7 @@ export default class TagService { public static async handleCreateTag(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { // Filter - const filteredRequest = TagSecurity.filterTagCreateRequest(req.body, req.user); + const filteredRequest = TagValidator.getInstance().validateTagCreate(req.body); // Check UtilsService.checkIfUserTagIsValid(filteredRequest, req); // Get dynamic auth @@ -115,7 +114,7 @@ export default class TagService { throw new AppError({ source: Constants.CENTRAL_SERVER, errorCode: HTTPError.TAG_VISUAL_ID_ALREADY_EXIST_ERROR, - message: `Tag with visual ID '${filteredRequest.id}' already exists`, + message: `Tag with visual ID '${filteredRequest.visualID}' already exists`, module: MODULE_NAME, method: 'handleCreateTag', user: req.user, action: action @@ -228,7 +227,7 @@ export default class TagService { public static async handleUpdateTag(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { // Filter - const filteredRequest = TagSecurity.filterTagUpdateRequest({ ...req.params, ...req.body }, req.user); + const filteredRequest = TagValidator.getInstance().validateTagUpdate({ ...req.params, ...req.body }); // Check UtilsService.checkIfUserTagIsValid(filteredRequest, req); // Check and Get Tag @@ -417,116 +416,139 @@ export default class TagService { await LockingManager.release(importTagsLock); } }); - // eslint-disable-next-line @typescript-eslint/no-misused-promises - busboy.on('file', async (fieldname: string, file: any, filename: string, encoding: string, mimetype: string) => { - if (filename.slice(-4) === '.csv') { - const converter = csvToJson({ - trim: true, - delimiter: Constants.CSV_SEPARATOR, - output: 'json', - }); - void converter.subscribe(async (tag: ImportedTag) => { - // Check connection - if (connectionClosed) { - throw new Error('HTTP connection has been closed'); - } - // Check the format of the first entry - if (!result.inSuccess && !result.inError) { - // Check header - const tagKeys = Object.keys(tag); - if (!TagRequiredImportProperties.every((property) => tagKeys.includes(property))) { - if (!res.headersSent) { - res.writeHead(HTTPError.INVALID_FILE_CSV_HEADER_FORMAT); - res.end(); + await new Promise((resolve) => { + // eslint-disable-next-line @typescript-eslint/no-misused-promises + busboy.on('file', async (fieldname: string, file: any, filename: string, encoding: string, mimetype: string) => { + if (filename.slice(-4) === '.csv') { + const converter = csvToJson({ + trim: true, + delimiter: Constants.CSV_SEPARATOR, + output: 'json', + }); + void converter.subscribe(async (tag: ImportedTag) => { + // Check connection + if (connectionClosed) { + throw new Error('HTTP connection has been closed'); + } + // Check the format of the first entry + if (!result.inSuccess && !result.inError) { + // Check header + const tagKeys = Object.keys(tag); + if (!TagRequiredImportProperties.every((property) => tagKeys.includes(property))) { + if (!res.headersSent) { + res.writeHead(HTTPError.INVALID_FILE_CSV_HEADER_FORMAT); + res.end(); + resolve(); + } + throw new Error(`Missing one of required properties: '${TagRequiredImportProperties.join(', ')}'`); } - throw new Error(`Missing one of required properties: '${TagRequiredImportProperties.join(', ')}'`); } - } - // Set default value - tag.importedBy = importedBy; - tag.importedOn = importedOn; - // Import - const importSuccess = await TagService.processTag(action, req, tag, tagsToBeImported); - if (!importSuccess) { - result.inError++; - } - // Insert batched - if (!Utils.isEmptyArray(tagsToBeImported) && (tagsToBeImported.length % Constants.IMPORT_BATCH_INSERT_SIZE) === 0) { - await TagService.insertTags(req.user.tenantID, req.user, action, tagsToBeImported, result); - } - // eslint-disable-next-line @typescript-eslint/no-misused-promises - }, async (error: CSVError) => { - // Release the lock - await LockingManager.release(importTagsLock); - // Log - await Logging.logError({ - tenantID: req.user.tenantID, - module: MODULE_NAME, method: 'handleImportTags', - action: action, - user: req.user.id, - message: `Exception while parsing the CSV '${filename}': ${error.message}`, - detailedMessages: { error: error.message, stack: error.stack } + // Set default value + tag.importedBy = importedBy; + tag.importedOn = importedOn; + // Import + const importSuccess = await TagService.processTag(action, req, tag, tagsToBeImported); + if (!importSuccess) { + result.inError++; + } + // Insert batched + if (!Utils.isEmptyArray(tagsToBeImported) && (tagsToBeImported.length % Constants.IMPORT_BATCH_INSERT_SIZE) === 0) { + await TagService.insertTags(req.user.tenantID, req.user, action, tagsToBeImported, result); + } + // eslint-disable-next-line @typescript-eslint/no-misused-promises + }, async (error: CSVError) => { + // Release the lock + await LockingManager.release(importTagsLock); + // Log + await Logging.logError({ + tenantID: req.user.tenantID, + module: MODULE_NAME, method: 'handleImportTags', + action: action, + user: req.user.id, + message: `Exception while parsing the CSV '${filename}': ${error.message}`, + detailedMessages: { error: error.message, stack: error.stack } + }); + if (!res.headersSent) { + res.writeHead(HTTPError.INVALID_FILE_FORMAT); + res.end(); + resolve(); + } + // Completed + // eslint-disable-next-line @typescript-eslint/no-misused-promises + }, async () => { + // Consider the connection closed + connectionClosed = true; + // Insert batched + if (tagsToBeImported.length > 0) { + await TagService.insertTags(req.user.tenantID, req.user, action, tagsToBeImported, result); + } + // Release the lock + await LockingManager.release(importTagsLock); + // Log + const executionDurationSecs = Utils.truncTo((new Date().getTime() - startTime) / 1000, 2); + await Logging.logActionsResponse( + req.user.tenantID, action, + MODULE_NAME, 'handleImportTags', result, + `{{inSuccess}} Tag(s) were successfully uploaded in ${executionDurationSecs}s and ready for asynchronous import`, + `{{inError}} Tag(s) failed to be uploaded in ${executionDurationSecs}s`, + `{{inSuccess}} Tag(s) were successfully uploaded in ${executionDurationSecs}s and ready for asynchronous import and {{inError}} failed to be uploaded`, + `No Tag have been uploaded in ${executionDurationSecs}s`, req.user + ); + // Create and Save async task + await AsyncTaskManager.createAndSaveAsyncTasks({ + name: AsyncTasks.TAGS_IMPORT, + action: ServerAction.TAGS_IMPORT, + type: AsyncTaskType.TASK, + tenantID: req.tenant.id, + module: MODULE_NAME, + method: 'handleImportTags', + }); + // Respond + res.json({ ...result, ...Constants.REST_RESPONSE_SUCCESS }); + next(); + resolve(); }); - if (!res.headersSent) { - res.writeHead(HTTPError.INVALID_FILE_FORMAT); - res.end(); - } - // Completed - // eslint-disable-next-line @typescript-eslint/no-misused-promises - }, async () => { - // Consider the connection closed - connectionClosed = true; - // Insert batched - if (tagsToBeImported.length > 0) { - await TagService.insertTags(req.user.tenantID, req.user, action, tagsToBeImported, result); - } - // Release the lock - await LockingManager.release(importTagsLock); - // Log - const executionDurationSecs = Utils.truncTo((new Date().getTime() - startTime) / 1000, 2); - await Logging.logActionsResponse( - req.user.tenantID, action, - MODULE_NAME, 'handleImportTags', result, - `{{inSuccess}} Tag(s) were successfully uploaded in ${executionDurationSecs}s and ready for asynchronous import`, - `{{inError}} Tag(s) failed to be uploaded in ${executionDurationSecs}s`, - `{{inSuccess}} Tag(s) were successfully uploaded in ${executionDurationSecs}s and ready for asynchronous import and {{inError}} failed to be uploaded`, - `No Tag have been uploaded in ${executionDurationSecs}s`, req.user - ); - // Create and Save async task - await AsyncTaskManager.createAndSaveAsyncTasks({ - name: AsyncTasks.TAGS_IMPORT, - action: ServerAction.TAGS_IMPORT, - type: AsyncTaskType.TASK, - tenantID: req.tenant.id, - module: MODULE_NAME, - method: 'handleImportTags', + // Start processing the file + void file.pipe(converter); + } else if (mimetype === 'application/json') { + const parser = JSONStream.parse('tags.*'); + // TODO: Handle the end of the process to send the data like the CSV + // 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 + const importSuccess = await TagService.processTag(action, req, tag, tagsToBeImported); + if (!importSuccess) { + result.inError++; + } + // Insert batched + if ((tagsToBeImported.length % Constants.IMPORT_BATCH_INSERT_SIZE) === 0) { + await TagService.insertTags(req.user.tenantID, req.user, action, tagsToBeImported, result); + } }); - // Respond - res.json({ ...result, ...Constants.REST_RESPONSE_SUCCESS }); - next(); - }); - // Start processing the file - void file.pipe(converter); - } else if (mimetype === 'application/json') { - const parser = JSONStream.parse('tags.*'); - // TODO: Handle the end of the process to send the data like the CSV - // 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 - const importSuccess = await TagService.processTag(action, req, tag, tagsToBeImported); - if (!importSuccess) { - result.inError++; - } - // Insert batched - if ((tagsToBeImported.length % Constants.IMPORT_BATCH_INSERT_SIZE) === 0) { - await TagService.insertTags(req.user.tenantID, req.user, action, tagsToBeImported, result); - } - }); - // eslint-disable-next-line @typescript-eslint/no-misused-promises - parser.on('error', async (error) => { + // eslint-disable-next-line @typescript-eslint/no-misused-promises + parser.on('error', async (error) => { + // Release the lock + await LockingManager.release(importTagsLock); + // Log + await Logging.logError({ + tenantID: req.user.tenantID, + module: MODULE_NAME, method: 'handleImportTags', + action: action, + user: req.user.id, + message: `Invalid Json file '${filename}'`, + detailedMessages: { error: error.message, stack: error.stack } + }); + if (!res.headersSent) { + res.writeHead(HTTPError.INVALID_FILE_FORMAT); + res.end(); + resolve(); + } + }); + file.pipe(parser); + } else { // Release the lock await LockingManager.release(importTagsLock); // Log @@ -535,31 +557,15 @@ export default class TagService { module: MODULE_NAME, method: 'handleImportTags', action: action, user: req.user.id, - message: `Invalid Json file '${filename}'`, - detailedMessages: { error: error.message, stack: error.stack } + message: `Invalid file format '${mimetype}'` }); if (!res.headersSent) { res.writeHead(HTTPError.INVALID_FILE_FORMAT); res.end(); + resolve(); } - }); - file.pipe(parser); - } else { - // Release the lock - await LockingManager.release(importTagsLock); - // Log - await Logging.logError({ - tenantID: req.user.tenantID, - module: MODULE_NAME, method: 'handleImportTags', - action: action, - user: req.user.id, - message: `Invalid file format '${mimetype}'` - }); - if (!res.headersSent) { - res.writeHead(HTTPError.INVALID_FILE_FORMAT); - res.end(); } - } + }); }); } catch (error) { // Release the lock @@ -726,7 +732,7 @@ export default class TagService { private static async getTags(req: Request): Promise> { // Filter - const filteredRequest = TagSecurity.filterTagsRequest(req.query); + const filteredRequest = TagValidator.getInstance().validateTagsGet(req.query); // Get authorization filters const authorizationTagsFilters = await AuthorizationService.checkAndGetTagsAuthorizationFilters( req.tenant, req.user, filteredRequest); @@ -757,7 +763,7 @@ export default class TagService { { limit: filteredRequest.Limit, skip: filteredRequest.Skip, - sort: filteredRequest.SortFields, + sort: UtilsService.httpSortFieldsToMongoDB(filteredRequest.SortFields), onlyRecordCount: filteredRequest.OnlyRecordCount }, authorizationTagsFilters.projectFields, diff --git a/src/server/rest/v1/service/TenantService.ts b/src/server/rest/v1/service/TenantService.ts index a495ab3c65..fb72482061 100644 --- a/src/server/rest/v1/service/TenantService.ts +++ b/src/server/rest/v1/service/TenantService.ts @@ -288,7 +288,7 @@ export default class TenantService { tenantUser.email + '&ResetToken=' + resetHash; // Send Register User (Async) NotificationHandler.sendNewRegisteredUser( - filteredRequest.id, + await TenantStorage.getTenant(filteredRequest.id), Utils.generateUUID(), tenantUser, { diff --git a/src/server/rest/v1/service/TransactionService.ts b/src/server/rest/v1/service/TransactionService.ts index 46943f5706..7d77d594b2 100644 --- a/src/server/rest/v1/service/TransactionService.ts +++ b/src/server/rest/v1/service/TransactionService.ts @@ -507,7 +507,7 @@ export default class TransactionService { source: chargingStation.id, user: req.user, actionOnUser: user, module: MODULE_NAME, method: 'handleTransactionSoftStop', - message: `Connector ID '${transaction.connectorId}' > Transaction ID '${transactionId}' has been stopped successfully`, + message: `${OCPPUtils.buildConnectorInfo(transaction.connectorId, transaction.id)} Transaction has been stopped successfully`, action: action, detailedMessages: { result } }); diff --git a/src/server/rest/v1/service/UserService.ts b/src/server/rest/v1/service/UserService.ts index caf870c7a2..c2ee712587 100644 --- a/src/server/rest/v1/service/UserService.ts +++ b/src/server/rest/v1/service/UserService.ts @@ -33,7 +33,6 @@ import { StartTransactionErrorCode } from '../../../../types/Transaction'; import TagStorage from '../../../../storage/mongodb/TagStorage'; import Tenant from '../../../../types/Tenant'; import TenantComponents from '../../../../types/TenantComponents'; -import TenantStorage from '../../../../storage/mongodb/TenantStorage'; import { UserInErrorType } from '../../../../types/InError'; import UserNotifications from '../../../../types/UserNotifications'; import UserStorage from '../../../../storage/mongodb/UserStorage'; @@ -123,7 +122,7 @@ export default class UserService { const userID = UserValidator.getInstance().validateUserGetByID(req.query).ID.toString(); // Check and Get User const user = await UtilsService.checkAndGetUserAuthorization( - req.tenant, req.user, userID, Action.DELETE, action); + req.tenant, req.user, userID, Action.DELETE, action, null, false, false); // Delete OCPI User if (!user.issuer) { // Delete User @@ -185,8 +184,6 @@ export default class UserService { // Update timestamp const lastChangedBy = { id: req.user.id }; const lastChangedOn = new Date(); - // Clean up request - delete filteredRequest['passwords']; // Check User validity UtilsService.checkIfUserValid(filteredRequest, user, req); // Update user @@ -243,12 +240,12 @@ export default class UserService { if (statusHasChanged && req.tenant.id !== Constants.DEFAULT_TENANT) { // Send notification (Async) NotificationHandler.sendUserAccountStatusChanged( - req.user.tenantID, + req.tenant, Utils.generateUUID(), user, { 'user': user, - 'evseDashboardURL': Utils.buildEvseURL((await TenantStorage.getTenant(req.user.tenantID)).subdomain) + 'evseDashboardURL': Utils.buildEvseURL(req.tenant.subdomain) } ).catch(() => { }); } @@ -302,7 +299,7 @@ export default class UserService { const user = await UtilsService.checkAndGetUserAuthorization( req.tenant, req.user, filteredRequest.ID.toString(), Action.READ, action, { withImage: true - }, true); + }, true, false); res.json(user); next(); } @@ -455,116 +452,139 @@ export default class UserService { await LockingManager.release(importUsersLock); } }); - // eslint-disable-next-line @typescript-eslint/no-misused-promises - busboy.on('file', async (fieldname, file, filename, encoding, mimetype) => { - if (filename.slice(-4) === '.csv') { - const converter = csvToJson({ - trim: true, - delimiter: Constants.CSV_SEPARATOR, - output: 'json', - }); - void converter.subscribe(async (user: ImportedUser) => { - // Check connection - if (connectionClosed) { - throw new Error('HTTP connection has been closed'); - } - // Check the format of the first entry - if (!result.inSuccess && !result.inError) { - // Check header - const userKeys = Object.keys(user); - if (!UserRequiredImportProperties.every((property) => userKeys.includes(property))) { - if (!res.headersSent) { - res.writeHead(HTTPError.INVALID_FILE_CSV_HEADER_FORMAT); - res.end(); + await new Promise((resolve) => { + // eslint-disable-next-line @typescript-eslint/no-misused-promises + busboy.on('file', async (fieldname, file, filename, encoding, mimetype) => { + if (filename.slice(-4) === '.csv') { + const converter = csvToJson({ + trim: true, + delimiter: Constants.CSV_SEPARATOR, + output: 'json', + }); + void converter.subscribe(async (user: ImportedUser) => { + // Check connection + if (connectionClosed) { + throw new Error('HTTP connection has been closed'); + } + // Check the format of the first entry + if (!result.inSuccess && !result.inError) { + // Check header + const userKeys = Object.keys(user); + if (!UserRequiredImportProperties.every((property) => userKeys.includes(property))) { + if (!res.headersSent) { + res.writeHead(HTTPError.INVALID_FILE_CSV_HEADER_FORMAT); + res.end(); + resolve(); + } + throw new Error(`Missing one of required properties: '${UserRequiredImportProperties.join(', ')}'`); } - throw new Error(`Missing one of required properties: '${UserRequiredImportProperties.join(', ')}'`); } - } - // Set default value - user.importedBy = importedBy; - user.importedOn = importedOn; - // Import - const importSuccess = await UserService.processUser(action, req, user, usersToBeImported); - if (!importSuccess) { - result.inError++; - } - // Insert batched - if (!Utils.isEmptyArray(usersToBeImported) && (usersToBeImported.length % Constants.IMPORT_BATCH_INSERT_SIZE) === 0) { - await UserService.insertUsers(req.user.tenantID, req.user, action, usersToBeImported, result); - } - // eslint-disable-next-line @typescript-eslint/no-misused-promises - }, async (error: CSVError) => { - // Release the lock - await LockingManager.release(importUsersLock); - // Log - await Logging.logError({ - tenantID: req.user.tenantID, - module: MODULE_NAME, method: 'handleImportUsers', - action: action, - user: req.user.id, - message: `Exception while parsing the CSV '${filename}': ${error.message}`, - detailedMessages: { error: error.message, stack: error.stack } + // Set default value + user.importedBy = importedBy; + user.importedOn = importedOn; + // Import + const importSuccess = await UserService.processUser(action, req, user, usersToBeImported); + if (!importSuccess) { + result.inError++; + } + // Insert batched + if (!Utils.isEmptyArray(usersToBeImported) && (usersToBeImported.length % Constants.IMPORT_BATCH_INSERT_SIZE) === 0) { + await UserService.insertUsers(req.user.tenantID, req.user, action, usersToBeImported, result); + } + // eslint-disable-next-line @typescript-eslint/no-misused-promises + }, async (error: CSVError) => { + // Release the lock + await LockingManager.release(importUsersLock); + // Log + await Logging.logError({ + tenantID: req.user.tenantID, + module: MODULE_NAME, method: 'handleImportUsers', + action: action, + user: req.user.id, + message: `Exception while parsing the CSV '${filename}': ${error.message}`, + detailedMessages: { error: error.message, stack: error.stack } + }); + if (!res.headersSent) { + res.writeHead(HTTPError.INVALID_FILE_FORMAT); + res.end(); + resolve(); + } + // Completed + // eslint-disable-next-line @typescript-eslint/no-misused-promises + }, async () => { + // Consider the connection closed + connectionClosed = true; + // Insert batched + if (usersToBeImported.length > 0) { + await UserService.insertUsers(req.user.tenantID, req.user, action, usersToBeImported, result); + } + // Release the lock + await LockingManager.release(importUsersLock); + // Log + const executionDurationSecs = Utils.truncTo((new Date().getTime() - startTime) / 1000, 2); + await Logging.logActionsResponse( + req.user.tenantID, action, + MODULE_NAME, 'handleImportUsers', result, + `{{inSuccess}} User(s) were successfully uploaded in ${executionDurationSecs}s and ready for asynchronous import`, + `{{inError}} User(s) failed to be uploaded in ${executionDurationSecs}s`, + `{{inSuccess}} User(s) were successfully uploaded in ${executionDurationSecs}s and ready for asynchronous import and {{inError}} failed to be uploaded`, + `No User have been uploaded in ${executionDurationSecs}s`, req.user + ); + // Create and Save async task + await AsyncTaskManager.createAndSaveAsyncTasks({ + name: AsyncTasks.USERS_IMPORT, + action: ServerAction.USERS_IMPORT, + type: AsyncTaskType.TASK, + tenantID: req.tenant.id, + module: MODULE_NAME, + method: 'handleImportUsers', + }); + // Respond + res.json({ ...result, ...Constants.REST_RESPONSE_SUCCESS }); + next(); + resolve(); }); - if (!res.headersSent) { - res.writeHead(HTTPError.INVALID_FILE_FORMAT); - res.end(); - } - // Completed - // eslint-disable-next-line @typescript-eslint/no-misused-promises - }, async () => { - // Consider the connection closed - connectionClosed = true; - // Insert batched - if (usersToBeImported.length > 0) { - await UserService.insertUsers(req.user.tenantID, req.user, action, usersToBeImported, result); - } - // Release the lock - await LockingManager.release(importUsersLock); - // Log - const executionDurationSecs = Utils.truncTo((new Date().getTime() - startTime) / 1000, 2); - await Logging.logActionsResponse( - req.user.tenantID, action, - MODULE_NAME, 'handleImportUsers', result, - `{{inSuccess}} User(s) were successfully uploaded in ${executionDurationSecs}s and ready for asynchronous import`, - `{{inError}} User(s) failed to be uploaded in ${executionDurationSecs}s`, - `{{inSuccess}} User(s) were successfully uploaded in ${executionDurationSecs}s and ready for asynchronous import and {{inError}} failed to be uploaded`, - `No User have been uploaded in ${executionDurationSecs}s`, req.user - ); - // Create and Save async task - await AsyncTaskManager.createAndSaveAsyncTasks({ - name: AsyncTasks.USERS_IMPORT, - action: ServerAction.USERS_IMPORT, - type: AsyncTaskType.TASK, - tenantID: req.tenant.id, - module: MODULE_NAME, - method: 'handleImportUsers', + // Start processing the file + void file.pipe(converter); + } else if (mimetype === 'application/json') { + const parser = JSONStream.parse('users.*'); + // TODO: Handle the end of the process to send the data like the CSV + // eslint-disable-next-line @typescript-eslint/no-misused-promises + parser.on('data', async (user: ImportedUser) => { + // Set default value + user.importedBy = importedBy; + user.importedOn = importedOn; + // Import + const importSuccess = await UserService.processUser(action, req, user, usersToBeImported); + if (!importSuccess) { + result.inError++; + } + // Insert batched + if ((usersToBeImported.length % Constants.IMPORT_BATCH_INSERT_SIZE) === 0) { + await UserService.insertUsers(req.user.tenantID, req.user, action, usersToBeImported, result); + } }); - // Respond - res.json({ ...result, ...Constants.REST_RESPONSE_SUCCESS }); - next(); - }); - // Start processing the file - void file.pipe(converter); - } else if (mimetype === 'application/json') { - const parser = JSONStream.parse('users.*'); - // TODO: Handle the end of the process to send the data like the CSV - // eslint-disable-next-line @typescript-eslint/no-misused-promises - parser.on('data', async (user: ImportedUser) => { - // Set default value - user.importedBy = importedBy; - user.importedOn = importedOn; - // Import - const importSuccess = await UserService.processUser(action, req, user, usersToBeImported); - if (!importSuccess) { - result.inError++; - } - // Insert batched - if ((usersToBeImported.length % Constants.IMPORT_BATCH_INSERT_SIZE) === 0) { - await UserService.insertUsers(req.user.tenantID, req.user, action, usersToBeImported, result); - } - }); - // eslint-disable-next-line @typescript-eslint/no-misused-promises - parser.on('error', async (error) => { + // eslint-disable-next-line @typescript-eslint/no-misused-promises + parser.on('error', async (error) => { + // Release the lock + await LockingManager.release(importUsersLock); + // Log + await Logging.logError({ + tenantID: req.user.tenantID, + module: MODULE_NAME, method: 'handleImportUsers', + action: action, + user: req.user.id, + message: `Invalid Json file '${filename}'`, + detailedMessages: { error: error.message, stack: error.stack } + }); + if (!res.headersSent) { + res.writeHead(HTTPError.INVALID_FILE_FORMAT); + res.end(); + resolve(); + } + }); + file.pipe(parser); + } else { // Release the lock await LockingManager.release(importUsersLock); // Log @@ -573,31 +593,14 @@ export default class UserService { module: MODULE_NAME, method: 'handleImportUsers', action: action, user: req.user.id, - message: `Invalid Json file '${filename}'`, - detailedMessages: { error: error.message, stack: error.stack } + message: `Invalid file format '${mimetype}'` }); if (!res.headersSent) { res.writeHead(HTTPError.INVALID_FILE_FORMAT); res.end(); } - }); - file.pipe(parser); - } else { - // Release the lock - await LockingManager.release(importUsersLock); - // Log - await Logging.logError({ - tenantID: req.user.tenantID, - module: MODULE_NAME, method: 'handleImportUsers', - action: action, - user: req.user.id, - message: `Invalid file format '${mimetype}'` - }); - if (!res.headersSent) { - res.writeHead(HTTPError.INVALID_FILE_FORMAT); - res.end(); } - } + }); }); } catch (error) { // Release the lock @@ -632,8 +635,6 @@ export default class UserService { action: action }); } - // Clean request - delete filteredRequest['passwords']; // Create const newUser: User = { ...filteredRequest, @@ -683,7 +684,7 @@ export default class UserService { } } // Assign user to all sites with auto-assign flag set - const sites = await SiteStorage.getSites(req.user.tenantID, + const sites = await SiteStorage.getSites(req.tenant, { withAutoUserAssignment: true }, Constants.DB_PARAMS_MAX_LIMIT ); @@ -777,7 +778,7 @@ export default class UserService { const users = await UserStorage.getUsers(req.user.tenantID, { search: filteredRequest.Search, - issuer: filteredRequest.Issuer, + issuer: Utils.isBoolean(filteredRequest.Issuer) || filteredRequest.Issuer ? Utils.convertToBoolean(filteredRequest.Issuer) : null, siteIDs: (filteredRequest.SiteID ? filteredRequest.SiteID.split('|') : null), userIDs: (filteredRequest.UserID ? filteredRequest.UserID.split('|') : null), tagIDs: (filteredRequest.TagID ? filteredRequest.TagID.split('|') : null), @@ -935,7 +936,7 @@ export default class UserService { private static async checkAndDeleteUserCar(tenant: Tenant, loggedUser: UserToken, user: User) { // Delete cars if (Utils.isComponentActiveFromToken(loggedUser, TenantComponents.CAR)) { - const carUsers = await CarStorage.getCarUsers(tenant, { userIDs : [user.id] }, Constants.DB_PARAMS_MAX_LIMIT); + const carUsers = await CarStorage.getCarUsers(tenant, { userIDs: [user.id] }, Constants.DB_PARAMS_MAX_LIMIT); if (carUsers.count > 0) { for (const carUser of carUsers.result) { // Owner ? diff --git a/src/server/rest/v1/service/UtilsService.ts b/src/server/rest/v1/service/UtilsService.ts index 7328245380..f5b18e0b1d 100644 --- a/src/server/rest/v1/service/UtilsService.ts +++ b/src/server/rest/v1/service/UtilsService.ts @@ -33,7 +33,6 @@ import Tag from '../../../../types/Tag'; import TagStorage from '../../../../storage/mongodb/TagStorage'; import Tenant from '../../../../types/Tenant'; import TenantComponents from '../../../../types/TenantComponents'; -import TenantStorage from '../../../../storage/mongodb/TenantStorage'; import { TransactionInErrorType } from '../../../../types/InError'; import UserStorage from '../../../../storage/mongodb/UserStorage'; import UserToken from '../../../../types/UserToken'; @@ -274,7 +273,7 @@ export default class UtilsService { }); } // Get Site - const site = await SiteStorage.getSite(tenant.id, siteID, + const site = await SiteStorage.getSite(tenant, siteID, { ...additionalFilters, ...authorizationFilter.filters, @@ -336,7 +335,7 @@ export default class UtilsService { }); } // Get Sites - let sites = (await SiteStorage.getSites(tenant.id, + let sites = (await SiteStorage.getSites(tenant, { siteIDs, ...additionalFilters, diff --git a/src/server/rest/v1/service/security/TagSecurity.ts b/src/server/rest/v1/service/security/TagSecurity.ts deleted file mode 100644 index f3c6a807c9..0000000000 --- a/src/server/rest/v1/service/security/TagSecurity.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { HttpTagRequest, HttpTagsRequest } from '../../../../../types/requests/HttpTagRequest'; - -import Tag from '../../../../../types/Tag'; -import UserToken from '../../../../../types/UserToken'; -import Utils from '../../../../../utils/Utils'; -import UtilsSecurity from './UtilsSecurity'; -import sanitize from 'mongo-sanitize'; - -export default class TagSecurity { - - public static filterTagsRequest(request: any): HttpTagsRequest { - const filteredRequest = { - Search: sanitize(request.Search), - UserID: sanitize(request.UserID), - Issuer: Utils.objectHasProperty(request, 'Issuer') ? UtilsSecurity.filterBoolean(request.Issuer) : null, - Active: Utils.objectHasProperty(request, 'Active') ? UtilsSecurity.filterBoolean(request.Active) : null, - WithUser: Utils.objectHasProperty(request, 'WithUser') ? UtilsSecurity.filterBoolean(request.WithUser) : null, - } as HttpTagsRequest; - UtilsSecurity.filterSkipAndLimit(request, filteredRequest); - UtilsSecurity.filterSort(request, filteredRequest); - UtilsSecurity.filterProject(request, filteredRequest); - return filteredRequest; - } - - public static filterTagRequestByIDs(request: any): string[] { - return request.tagsIDs.map(sanitize); - } - - public static filterTagUpdateRequest(request: any, loggedUser: UserToken): Partial { - return TagSecurity.filterTagRequest(request, loggedUser); - } - - public static filterTagCreateRequest(request: any, loggedUser: UserToken): Partial { - return TagSecurity.filterTagRequest(request, loggedUser); - } - - public static filterTagRequest(tag: Tag, loggedUser: UserToken): Tag { - let filteredTag: Tag; - if (tag) { - filteredTag = { - id: sanitize(tag.id), - visualID: sanitize(tag.visualID), - description: sanitize(tag.description), - active: UtilsSecurity.filterBoolean(tag.active), - issuer: UtilsSecurity.filterBoolean(tag.issuer), - default: UtilsSecurity.filterBoolean(tag.default), - userID: sanitize(tag.userID) - } as Tag; - } - return filteredTag; - } - - public static filterTagRequestByID(request: any): HttpTagRequest { - const filteredRequest: HttpTagRequest = { - ID: sanitize(request.ID) - }; - UtilsSecurity.filterProject(request, filteredRequest); - return filteredRequest; - } -} diff --git a/src/server/rest/v1/validator/TagValidator.ts b/src/server/rest/v1/validator/TagValidator.ts index c0fb0d01c1..73b4952663 100644 --- a/src/server/rest/v1/validator/TagValidator.ts +++ b/src/server/rest/v1/validator/TagValidator.ts @@ -1,4 +1,6 @@ -import { ImportedTag } from '../../../../types/Tag'; +import { HttpTagRequest, HttpTagsRequest } from '../../../../types/requests/HttpTagRequest'; +import Tag, { ImportedTag } from '../../../../types/Tag'; + import Schema from '../../../../types/validator/Schema'; import SchemaValidator from './SchemaValidator'; import fs from 'fs'; @@ -7,10 +9,20 @@ import global from '../../../../types/GlobalType'; export default class TagValidator extends SchemaValidator { private static instance: TagValidator|null = null; private importedTagCreation: Schema; + private tagCreate: Schema; + private tagUpdate: Schema; + private tagsGet: Schema; + private tagGet: Schema; + private tagsDelete: Schema; private constructor() { super('TagValidator'); this.importedTagCreation = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/tag/imported-tag-create-req.json`, 'utf8')); + this.tagCreate = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/tag/tag-create.json`, 'utf8')); + this.tagUpdate = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/tag/tag-update.json`, 'utf8')); + this.tagsGet = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/tag/tags-get.json`, 'utf8')); + this.tagGet = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/tag/tag-get.json`, 'utf8')); + this.tagsDelete = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/tag/tags-delete.json`, 'utf8')); } public static getInstance(): TagValidator { @@ -23,4 +35,29 @@ export default class TagValidator extends SchemaValidator { validateImportedTagCreation(importedTag: ImportedTag): void { this.validate(this.importedTagCreation, importedTag); } + + validateTagCreate(tag: Tag): Tag { + this.validate(this.tagCreate, tag); + return tag; + } + + validateTagUpdate(tag: Tag): Tag { + this.validate(this.tagUpdate, tag); + return tag; + } + + validateTagsGet(data: any): HttpTagsRequest { + this.validate(this.tagsGet, data); + return data; + } + + validateTagGetByID(data: any): HttpTagRequest { + this.validate(this.tagGet, data); + return data; + } + + validateTagsDelete(data: { tagsIDs: string[] }): { tagsIDs: string[] } { + this.validate(this.tagsDelete, data); + return data; + } } diff --git a/src/server/rest/v1/validator/UserValidator.ts b/src/server/rest/v1/validator/UserValidator.ts index 7fcdb40adb..43662c5530 100644 --- a/src/server/rest/v1/validator/UserValidator.ts +++ b/src/server/rest/v1/validator/UserValidator.ts @@ -45,7 +45,6 @@ export default class UserValidator extends SchemaValidator { validateUserCreate(user: any): Partial { this.validate(this.userCreate, user); - user.password = user.passwords.password; return user; } diff --git a/src/storage/mongodb/AssetStorage.ts b/src/storage/mongodb/AssetStorage.ts index b9dda0e71e..2639cc49eb 100644 --- a/src/storage/mongodb/AssetStorage.ts +++ b/src/storage/mongodb/AssetStorage.ts @@ -29,7 +29,7 @@ export default class AssetStorage { await DatabaseUtils.checkTenant(tenantID); // Read DB const assetImageMDB = await global.database.getCollection<{ _id: ObjectID; image: string }>(tenantID, 'assetimages') - .findOne({ _id: Utils.convertToObjectID(id) }); + .findOne({ _id: DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'getAssetImage', uniqueTimerID, assetImageMDB); return { @@ -45,10 +45,10 @@ export default class AssetStorage { await DatabaseUtils.checkTenant(tenantID); // Set const assetMDB: any = { - _id: assetToSave.id ? Utils.convertToObjectID(assetToSave.id) : new ObjectID(), + _id: assetToSave.id ? DatabaseUtils.convertToObjectID(assetToSave.id) : new ObjectID(), name: assetToSave.name, - siteAreaID: Utils.convertToObjectID(assetToSave.siteAreaID), - siteID: Utils.convertToObjectID(assetToSave.siteID), + siteAreaID: DatabaseUtils.convertToObjectID(assetToSave.siteAreaID), + siteID: DatabaseUtils.convertToObjectID(assetToSave.siteID), coordinates: Utils.containsGPSCoordinates(assetToSave.coordinates) ? assetToSave.coordinates.map( (coordinate) => Utils.convertToFloat(coordinate)) : [], assetType: assetToSave.assetType, @@ -129,7 +129,7 @@ export default class AssetStorage { filters.siteAreaID = null; } else if (!Utils.isEmptyArray(params.siteAreaIDs)) { filters.siteAreaID = { - $in: params.siteAreaIDs.map((id) => Utils.convertToObjectID(id)) + $in: params.siteAreaIDs.map((id) => DatabaseUtils.convertToObjectID(id)) }; } // Issuer @@ -139,7 +139,7 @@ export default class AssetStorage { // Sites if (!Utils.isEmptyArray(params.siteIDs)) { filters.siteID = { - $in: params.siteIDs.map((siteID) => Utils.convertToObjectID(siteID)) + $in: params.siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) }; } // Dynamic Asset @@ -149,7 +149,7 @@ export default class AssetStorage { // Limit on Asset for Basic Users if (!Utils.isEmptyArray(params.assetIDs)) { filters._id = { - $in: params.assetIDs.map((assetID) => Utils.convertToObjectID(assetID)) + $in: params.assetIDs.map((assetID) => DatabaseUtils.convertToObjectID(assetID)) }; } // Filters @@ -244,10 +244,10 @@ export default class AssetStorage { ]; } if (!Utils.isEmptyArray(params.siteAreaIDs)) { - filters.siteAreaID = { $in: params.siteAreaIDs.map((id) => Utils.convertToObjectID(id)) }; + filters.siteAreaID = { $in: params.siteAreaIDs.map((id) => DatabaseUtils.convertToObjectID(id)) }; } if (!Utils.isEmptyArray(params.siteIDs)) { - filters.siteID = { $in: params.siteIDs.map((id) => Utils.convertToObjectID(id)) }; + filters.siteID = { $in: params.siteIDs.map((id) => DatabaseUtils.convertToObjectID(id)) }; } if (Utils.objectHasProperty(params, 'issuer') && Utils.isBoolean(params.issuer)) { filters.issuer = params.issuer; @@ -319,10 +319,10 @@ export default class AssetStorage { await DatabaseUtils.checkTenant(tenantID); // Delete the Asset await global.database.getCollection(tenantID, 'assets') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Delete Image await global.database.getCollection(tenantID, 'assetimages') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteAsset', uniqueTimerID, { id }); } @@ -334,7 +334,7 @@ export default class AssetStorage { await DatabaseUtils.checkTenant(tenantID); // Modify await global.database.getCollection(tenantID, 'assetimages').findOneAndUpdate( - { '_id': Utils.convertToObjectID(assetID) }, + { '_id': DatabaseUtils.convertToObjectID(assetID) }, { $set: { image: assetImageToSave } }, { upsert: true }); // Debug diff --git a/src/storage/mongodb/AsyncTaskStorage.ts b/src/storage/mongodb/AsyncTaskStorage.ts index b6ec6ccfc7..4c6ad6c257 100644 --- a/src/storage/mongodb/AsyncTaskStorage.ts +++ b/src/storage/mongodb/AsyncTaskStorage.ts @@ -25,11 +25,11 @@ export default class AsyncTaskStorage { const uniqueTimerID = Logging.traceStart(Constants.DEFAULT_TENANT, MODULE_NAME, 'saveAsyncTask'); // Set const asyncTaskMDB: any = { - _id: asyncTaskToSave.id ? Utils.convertToObjectID(asyncTaskToSave.id) : new ObjectID(), + _id: asyncTaskToSave.id ? DatabaseUtils.convertToObjectID(asyncTaskToSave.id) : new ObjectID(), name: asyncTaskToSave.name, action: asyncTaskToSave.action, type: asyncTaskToSave.type, - tenantID: Utils.convertToObjectID(asyncTaskToSave.tenantID), + tenantID: DatabaseUtils.convertToObjectID(asyncTaskToSave.tenantID), status: asyncTaskToSave.status, parent: asyncTaskToSave.parent, execHost: asyncTaskToSave.execHost, diff --git a/src/storage/mongodb/BillingStorage.ts b/src/storage/mongodb/BillingStorage.ts index 5415516b1a..11c1fe4fb9 100644 --- a/src/storage/mongodb/BillingStorage.ts +++ b/src/storage/mongodb/BillingStorage.ts @@ -56,12 +56,12 @@ export default class BillingStorage { } if (!Utils.isEmptyArray(params.invoiceIDs)) { filters._id = { - $in: params.invoiceIDs.map((invoiceID) => Utils.convertToObjectID(invoiceID)) + $in: params.invoiceIDs.map((invoiceID) => DatabaseUtils.convertToObjectID(invoiceID)) }; } if (!Utils.isEmptyArray(params.userIDs)) { filters.userID = { - $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) + $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) }; } if (params.billingInvoiceID) { @@ -161,12 +161,12 @@ export default class BillingStorage { // Build Request // Properties to save const invoiceMDB: any = { - _id: invoiceToSave.id ? Utils.convertToObjectID(invoiceToSave.id) : new ObjectID(), + _id: invoiceToSave.id ? DatabaseUtils.convertToObjectID(invoiceToSave.id) : new ObjectID(), invoiceID: invoiceToSave.invoiceID, // eslint-disable-next-line id-blacklist number: invoiceToSave.number, liveMode: Utils.convertToBoolean(invoiceToSave.liveMode), - userID: invoiceToSave.userID ? Utils.convertToObjectID(invoiceToSave.userID) : null, + userID: invoiceToSave.userID ? DatabaseUtils.convertToObjectID(invoiceToSave.userID) : null, customerID: invoiceToSave.customerID, amount: Utils.convertToFloat(invoiceToSave.amount), amountPaid: Utils.convertToFloat(invoiceToSave.amountPaid), @@ -204,7 +204,7 @@ export default class BillingStorage { lastError: additionalData.lastError }; await global.database.getCollection(tenant.id, 'invoices').findOneAndUpdate( - { '_id': Utils.convertToObjectID(invoiceToUpdate.id) }, + { '_id': DatabaseUtils.convertToObjectID(invoiceToUpdate.id) }, { $set: updatedInvoiceMDB }); // Debug await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveInvoiceAdditionalData', uniqueTimerID, updatedInvoiceMDB); @@ -217,7 +217,7 @@ export default class BillingStorage { DatabaseUtils.checkTenantObject(tenant); // Delete the Invoice await global.database.getCollection(tenant.id, 'invoices') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteInvoice', uniqueTimerID, { id }); } diff --git a/src/storage/mongodb/CarStorage.ts b/src/storage/mongodb/CarStorage.ts index ef3fb653a2..111be9c3bb 100644 --- a/src/storage/mongodb/CarStorage.ts +++ b/src/storage/mongodb/CarStorage.ts @@ -486,7 +486,7 @@ export default class CarStorage { DatabaseUtils.checkTenantObject(tenant); // Set const carMDB: any = { - _id: carToSave.id ? Utils.convertToObjectID(carToSave.id) : new ObjectID(), + _id: carToSave.id ? DatabaseUtils.convertToObjectID(carToSave.id) : new ObjectID(), vin: carToSave.vin, licensePlate: carToSave.licensePlate, carCatalogID: Utils.convertToInt(carToSave.carCatalogID), @@ -519,8 +519,8 @@ export default class CarStorage { // Set const carUserMDB: any = { _id: Cypher.hash(`${carUserToSave.carID}~${carUserToSave.user.id}`), - userID: Utils.convertToObjectID(carUserToSave.user.id), - carID: Utils.convertToObjectID(carUserToSave.carID), + userID: DatabaseUtils.convertToObjectID(carUserToSave.user.id), + carID: DatabaseUtils.convertToObjectID(carUserToSave.carID), default: carUserToSave.default, owner: (carUserToSave.owner === true ? true : false) }; @@ -552,8 +552,8 @@ export default class CarStorage { for (const carUserToSave of carUsersToSave) { const carUserMDB = { _id: Cypher.hash(`${carUserToSave.carID}~${carUserToSave.user.id}`), - userID: Utils.convertToObjectID(carUserToSave.user.id), - carID: Utils.convertToObjectID(carUserToSave.carID), + userID: DatabaseUtils.convertToObjectID(carUserToSave.user.id), + carID: DatabaseUtils.convertToObjectID(carUserToSave.carID), default: carUserToSave.default, owner: carUserToSave.owner, }; @@ -648,7 +648,7 @@ export default class CarStorage { // Car if (!Utils.isEmptyArray(params.carIDs)) { filters._id = { - $in: params.carIDs.map((carID) => Utils.convertToObjectID(carID)) + $in: params.carIDs.map((carID) => DatabaseUtils.convertToObjectID(carID)) }; } // Filter on Users @@ -658,7 +658,7 @@ export default class CarStorage { asField: 'carUsers', oneToOneCardinality: false }); if (!Utils.isEmptyArray(params.userIDs)) { - filters['carUsers.userID'] = { $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) }; + filters['carUsers.userID'] = { $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) }; } if (params.defaultCar) { filters['carUsers.default'] = true; @@ -745,7 +745,7 @@ export default class CarStorage { const carUsersPipeline = []; if (!Utils.isEmptyArray(params.userIDs)) { carUsersPipeline.push({ - $match: { 'carUsers.userID': { $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) } } + $match: { 'carUsers.userID': { $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) } } }); } // User on Car Users @@ -776,7 +776,7 @@ export default class CarStorage { DatabaseUtils.checkTenantObject(tenant); await global.database.getCollection(tenant.id, 'carusers').updateMany( { - userID: Utils.convertToObjectID(userID), + userID: DatabaseUtils.convertToObjectID(userID), default: true }, { @@ -790,7 +790,7 @@ export default class CarStorage { DatabaseUtils.checkTenantObject(tenant); await global.database.getCollection(tenant.id, 'carusers').updateMany( { - carID: Utils.convertToObjectID(carID), + carID: DatabaseUtils.convertToObjectID(carID), owner: true }, { @@ -832,7 +832,7 @@ export default class CarStorage { const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteCarUserByCarID'); // Delete singular site area const result = await global.database.getCollection(tenant.id, 'carusers') - .deleteMany({ 'carID': Utils.convertToObjectID(carID) }); + .deleteMany({ 'carID': DatabaseUtils.convertToObjectID(carID) }); // Debug await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteCarUserByCarID', uniqueTimerID, { carID }); return result.deletedCount; @@ -843,7 +843,7 @@ export default class CarStorage { const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteCar'); // Delete singular site area await global.database.getCollection(tenant.id, 'cars') - .deleteOne({ '_id': Utils.convertToObjectID(carID) }); + .deleteOne({ '_id': DatabaseUtils.convertToObjectID(carID) }); // Debug await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteCar', uniqueTimerID, { carID }); } @@ -877,11 +877,11 @@ export default class CarStorage { } // Cars if (!Utils.isEmptyArray(params.carIDs)) { - filters.carID = { $in: params.carIDs.map((carID) => Utils.convertToObjectID(carID)) }; + filters.carID = { $in: params.carIDs.map((carID) => DatabaseUtils.convertToObjectID(carID)) }; } // Users if (!Utils.isEmptyArray(params.userIDs)) { - filters.userID = { $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) }; + filters.userID = { $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) }; } // Create Aggregation const aggregation = []; diff --git a/src/storage/mongodb/ChargingStationStorage.ts b/src/storage/mongodb/ChargingStationStorage.ts index 8e1dfee848..3279616c44 100644 --- a/src/storage/mongodb/ChargingStationStorage.ts +++ b/src/storage/mongodb/ChargingStationStorage.ts @@ -307,12 +307,12 @@ export default class ChargingStationStorage { filters.siteAreaID = null; } else if (!Utils.isEmptyArray(params.siteAreaIDs)) { // Query by siteAreaID - filters.siteAreaID = { $in: params.siteAreaIDs.map((id) => Utils.convertToObjectID(id)) }; + filters.siteAreaID = { $in: params.siteAreaIDs.map((id) => DatabaseUtils.convertToObjectID(id)) }; } // Check Site ID if (!Utils.isEmptyArray(params.siteIDs)) { // Query by siteID - filters.siteID = { $in: params.siteIDs.map((id) => Utils.convertToObjectID(id)) }; + filters.siteID = { $in: params.siteIDs.map((id) => DatabaseUtils.convertToObjectID(id)) }; } // Date before provided if (params.statusChangedBefore && moment(params.statusChangedBefore).isValid()) { @@ -443,7 +443,7 @@ export default class ChargingStationStorage { filters.issuer = true; // Site Areas if (!Utils.isEmptyArray(params.siteAreaIDs)) { - filters.siteAreaID = { $in: params.siteAreaIDs.map((id) => Utils.convertToObjectID(id)) }; + filters.siteAreaID = { $in: params.siteAreaIDs.map((id) => DatabaseUtils.convertToObjectID(id)) }; } // Add in aggregation aggregation.push({ @@ -467,7 +467,7 @@ export default class ChargingStationStorage { aggregation.push({ $match: { 'sitearea.siteID': { - $in: params.siteIDs.map((id) => Utils.convertToObjectID(id)) + $in: params.siteIDs.map((id) => DatabaseUtils.convertToObjectID(id)) } } }); @@ -549,8 +549,8 @@ export default class ChargingStationStorage { templateHashOcppVendor: chargingStationToSave.templateHashOcppVendor, issuer: Utils.convertToBoolean(chargingStationToSave.issuer), public: Utils.convertToBoolean(chargingStationToSave.public), - siteAreaID: Utils.convertToObjectID(chargingStationToSave.siteAreaID), - siteID: Utils.convertToObjectID(chargingStationToSave.siteID), + siteAreaID: DatabaseUtils.convertToObjectID(chargingStationToSave.siteAreaID), + siteID: DatabaseUtils.convertToObjectID(chargingStationToSave.siteID), chargePointSerialNumber: chargingStationToSave.chargePointSerialNumber, chargePointModel: chargingStationToSave.chargePointModel, chargeBoxSerialNumber: chargingStationToSave.chargeBoxSerialNumber, @@ -1131,7 +1131,7 @@ export default class ChargingStationStorage { currentTransactionDate: Utils.convertToDate(connector.currentTransactionDate), currentTagID: connector.currentTagID, currentTransactionID: Utils.convertToInt(connector.currentTransactionID), - currentUserID: Utils.convertToObjectID(connector.currentUserID), + currentUserID: DatabaseUtils.convertToObjectID(connector.currentUserID), status: connector.status, errorCode: connector.errorCode, info: connector.info, diff --git a/src/storage/mongodb/CompanyStorage.ts b/src/storage/mongodb/CompanyStorage.ts index fde1b77c24..636f3fb3b6 100644 --- a/src/storage/mongodb/CompanyStorage.ts +++ b/src/storage/mongodb/CompanyStorage.ts @@ -32,7 +32,7 @@ export default class CompanyStorage { DatabaseUtils.checkTenantObject(tenant); // Read DB const companyLogoMDB = await global.database.getCollection<{ _id: ObjectID; logo: string }>(tenant.id, 'companylogos') - .findOne({ _id: Utils.convertToObjectID(id) }); + .findOne({ _id: DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenant.id, MODULE_NAME, 'getCompanyLogo', uniqueTimerID, companyLogoMDB); return { @@ -48,7 +48,7 @@ export default class CompanyStorage { DatabaseUtils.checkTenantObject(tenant); // Set const companyMDB: any = { - _id: !companyToSave.id ? new ObjectID() : Utils.convertToObjectID(companyToSave.id), + _id: !companyToSave.id ? new ObjectID() : DatabaseUtils.convertToObjectID(companyToSave.id), name: companyToSave.name, issuer: Utils.convertToBoolean(companyToSave.issuer), }; @@ -125,7 +125,7 @@ export default class CompanyStorage { if (!Utils.isEmptyArray(params.companyIDs)) { // Build filter filters._id = { - $in: params.companyIDs.map((companyID) => Utils.convertToObjectID(companyID)) + $in: params.companyIDs.map((companyID) => DatabaseUtils.convertToObjectID(companyID)) }; } if (Utils.objectHasProperty(params, 'issuer') && Utils.isBoolean(params.issuer)) { @@ -229,13 +229,13 @@ export default class CompanyStorage { // Check Tenant DatabaseUtils.checkTenantObject(tenant); // Delete sites associated with Company - await SiteStorage.deleteCompanySites(tenant.id, id); + await SiteStorage.deleteCompanySites(tenant, id); // Delete the Company await global.database.getCollection(tenant.id, 'companies') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Delete Logo await global.database.getCollection(tenant.id, 'companylogos') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteCompany', uniqueTimerID, { id }); } @@ -247,7 +247,7 @@ export default class CompanyStorage { DatabaseUtils.checkTenantObject(tenant); // Modify await global.database.getCollection(tenant.id, 'companylogos').findOneAndUpdate( - { '_id': Utils.convertToObjectID(companyID) }, + { '_id': DatabaseUtils.convertToObjectID(companyID) }, { $set: { logo: companyLogoToSave } }, { upsert: true }); // Debug diff --git a/src/storage/mongodb/ConnectionStorage.ts b/src/storage/mongodb/ConnectionStorage.ts index 44b9a0f530..6342cab189 100644 --- a/src/storage/mongodb/ConnectionStorage.ts +++ b/src/storage/mongodb/ConnectionStorage.ts @@ -17,9 +17,9 @@ export default class ConnectionStorage { DatabaseUtils.checkTenantObject(tenant); // Create const connectionMDB: any = { - _id: !connectionToSave.id ? new ObjectID() : Utils.convertToObjectID(connectionToSave.id), + _id: !connectionToSave.id ? new ObjectID() : DatabaseUtils.convertToObjectID(connectionToSave.id), connectorId: connectionToSave.connectorId, - userId: Utils.convertToObjectID(connectionToSave.userId), + userId: DatabaseUtils.convertToObjectID(connectionToSave.userId), createdAt: Utils.convertToDate(connectionToSave.createdAt), updatedAt: Utils.convertToDate(connectionToSave.updatedAt), validUntil: Utils.convertToDate(connectionToSave.validUntil), @@ -39,7 +39,7 @@ export default class ConnectionStorage { DatabaseUtils.checkTenantObject(tenant); const aggregation = []; aggregation.push({ - $match: { connectorId: connectorId, userId: Utils.convertToObjectID(userId) } + $match: { connectorId: connectorId, userId: DatabaseUtils.convertToObjectID(userId) } }); // Convert Object ID to string DatabaseUtils.pushConvertObjectIDToString(aggregation, 'userId'); @@ -64,7 +64,7 @@ export default class ConnectionStorage { DatabaseUtils.checkTenantObject(tenant); const aggregation = []; aggregation.push({ - $match: { userId: Utils.convertToObjectID(userID) } + $match: { userId: DatabaseUtils.convertToObjectID(userID) } }); // Convert Object ID to string DatabaseUtils.pushConvertObjectIDToString(aggregation, 'userId'); @@ -91,7 +91,7 @@ export default class ConnectionStorage { const aggregation = []; // Filters aggregation.push({ - $match: { _id: Utils.convertToObjectID(id) } + $match: { _id: DatabaseUtils.convertToObjectID(id) } }); // Convert Object ID to string DatabaseUtils.pushConvertObjectIDToString(aggregation, 'userId'); @@ -118,7 +118,7 @@ export default class ConnectionStorage { DatabaseUtils.checkTenantObject(tenant); // Delete await global.database.getCollection(tenant.id, 'connections') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteConnection', uniqueTimerID, { id }); } @@ -130,7 +130,7 @@ export default class ConnectionStorage { DatabaseUtils.checkTenantObject(tenant); // Delete await global.database.getCollection(tenant.id, 'connections') - .deleteMany({ 'userId': Utils.convertToObjectID(userID) }); + .deleteMany({ 'userId': DatabaseUtils.convertToObjectID(userID) }); // Debug await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteConnectionByUser', uniqueTimerID, { userID }); } diff --git a/src/storage/mongodb/ConsumptionStorage.ts b/src/storage/mongodb/ConsumptionStorage.ts index 0f2e9ec270..5ba03c66c0 100644 --- a/src/storage/mongodb/ConsumptionStorage.ts +++ b/src/storage/mongodb/ConsumptionStorage.ts @@ -72,7 +72,7 @@ export default class ConsumptionStorage { const filters: FilterParams = {}; // ID if (params.assetID) { - filters.assetID = Utils.convertToObjectID(params.assetID); + filters.assetID = DatabaseUtils.convertToObjectID(params.assetID); } // Date provided? if (params.startDate || params.endDate) { @@ -153,7 +153,7 @@ export default class ConsumptionStorage { // Filters aggregation.push({ $match: { - assetID: Utils.convertToObjectID(params.assetID) + assetID: DatabaseUtils.convertToObjectID(params.assetID) } }); // Convert Object ID to string @@ -193,7 +193,7 @@ export default class ConsumptionStorage { const filters: FilterParams = {}; // ID if (params.siteAreaID) { - filters.siteAreaID = Utils.convertToObjectID(params.siteAreaID); + filters.siteAreaID = DatabaseUtils.convertToObjectID(params.siteAreaID); } // Date provided? if (params.startDate || params.endDate) { @@ -454,9 +454,9 @@ export default class ConsumptionStorage { transactionId: Utils.convertToInt(consumption.transactionId), chargeBoxID: consumption.chargeBoxID, connectorId: Utils.convertToInt(consumption.connectorId), - siteAreaID: Utils.convertToObjectID(consumption.siteAreaID), - siteID: Utils.convertToObjectID(consumption.siteID), - assetID: Utils.convertToObjectID(consumption.assetID), + siteAreaID: DatabaseUtils.convertToObjectID(consumption.siteAreaID), + siteID: DatabaseUtils.convertToObjectID(consumption.siteID), + assetID: DatabaseUtils.convertToObjectID(consumption.assetID), consumptionWh: Utils.convertToFloat(consumption.consumptionWh), consumptionAmps: Utils.convertToFloat(consumption.consumptionAmps), cumulatedAmount: Utils.convertToFloat(consumption.cumulatedAmount), @@ -487,7 +487,7 @@ export default class ConsumptionStorage { limitAmps: Utils.convertToInt(consumption.limitAmps), limitWatts: Utils.convertToInt(consumption.limitWatts), limitSource: consumption.limitSource, - userID: Utils.convertToObjectID(consumption.userID), + userID: DatabaseUtils.convertToObjectID(consumption.userID), smartChargingActive: Utils.convertToBoolean(consumption.smartChargingActive), limitSiteAreaWatts: consumption.limitSiteAreaWatts ? Utils.convertToInt(consumption.limitSiteAreaWatts) : null, limitSiteAreaAmps: consumption.limitSiteAreaAmps ? Utils.convertToInt(consumption.limitSiteAreaAmps) : null, diff --git a/src/storage/mongodb/DatabaseUtils.ts b/src/storage/mongodb/DatabaseUtils.ts index 815ff45d81..219436e0c2 100644 --- a/src/storage/mongodb/DatabaseUtils.ts +++ b/src/storage/mongodb/DatabaseUtils.ts @@ -5,6 +5,8 @@ import DbLookup from '../../types/database/DbLookup'; import { OCPPFirmwareStatus } from '../../types/ocpp/OCPPServer'; import { ObjectID } from 'mongodb'; import Tenant from '../../types/Tenant'; +import User from '../../types/User'; +import UserToken from '../../types/UserToken'; import Utils from '../../utils/Utils'; import global from '../../types/GlobalType'; @@ -369,6 +371,35 @@ export default class DatabaseUtils { } } + public static convertToObjectID(id: any): ObjectID { + let changedID: ObjectID = id; + // Check + if (typeof id === 'string') { + // Create Object + changedID = new ObjectID(id); + } + return changedID; + } + + public static convertUserToObjectID(user: User | UserToken | string): ObjectID | null { + let userID: ObjectID | null = null; + // Check Created By + if (user) { + // Check User Model + if (typeof user === 'object' && + user.constructor.name !== 'ObjectID') { + // This is the User Model + userID = DatabaseUtils.convertToObjectID(user.id); + } + // Check String + if (typeof user === 'string') { + // This is a String + userID = DatabaseUtils.convertToObjectID(user); + } + } + return userID; + } + public static async checkTenant(tenantID: string): Promise { if (!tenantID) { throw new BackendError({ @@ -390,7 +421,7 @@ export default class DatabaseUtils { } // Get the Tenant const tenant = await global.database.getCollection(Constants.DEFAULT_TENANT, 'tenants').findOne({ - '_id': Utils.convertToObjectID(tenantID) + '_id': DatabaseUtils.convertToObjectID(tenantID) }); if (!tenant) { throw new BackendError({ @@ -444,7 +475,7 @@ export default class DatabaseUtils { return obj[prop] as ObjectID; } if (obj[prop].id) { - return Utils.convertToObjectID(obj[prop].id); + return DatabaseUtils.convertToObjectID(obj[prop].id); } return null; } diff --git a/src/storage/mongodb/LockingStorage.ts b/src/storage/mongodb/LockingStorage.ts index 73b7cbb201..92fb2bef4a 100644 --- a/src/storage/mongodb/LockingStorage.ts +++ b/src/storage/mongodb/LockingStorage.ts @@ -90,7 +90,7 @@ export default class LockingStorage { // Transfer const lockMDB = { _id: lockToSave.id, - tenantID: lockToSave.tenantID !== Constants.DEFAULT_TENANT ? Utils.convertToObjectID(lockToSave.tenantID) : null, + tenantID: lockToSave.tenantID !== Constants.DEFAULT_TENANT ? DatabaseUtils.convertToObjectID(lockToSave.tenantID) : null, entity: lockToSave.entity, key: lockToSave.key, type: lockToSave.type, diff --git a/src/storage/mongodb/LoggingStorage.ts b/src/storage/mongodb/LoggingStorage.ts index 69a7d91b18..3e1d880bc3 100644 --- a/src/storage/mongodb/LoggingStorage.ts +++ b/src/storage/mongodb/LoggingStorage.ts @@ -58,8 +58,8 @@ export default class LoggingStorage { await DatabaseUtils.checkTenant(tenantID); // Set const logMDB: any = { - userID: logToSave.user ? Utils.convertUserToObjectID(logToSave.user) : null, - actionOnUserID: Utils.convertUserToObjectID(logToSave.actionOnUser), + userID: logToSave.user ? DatabaseUtils.convertUserToObjectID(logToSave.user) : null, + actionOnUserID: DatabaseUtils.convertUserToObjectID(logToSave.actionOnUser), level: logToSave.level, source: logToSave.source, host: logToSave.host ? logToSave.host : Utils.getHostname(), @@ -139,14 +139,14 @@ export default class LoggingStorage { // Filter on users if (!Utils.isEmptyArray(params.userIDs)) { filters.$or = [ - { userID: { $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) } }, - { actionOnUserID: { $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) } } + { userID: { $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) } }, + { actionOnUserID: { $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) } } ]; } // Log ID if (!Utils.isEmptyArray(params.logIDs)) { filters._id = { - $in: params.logIDs.map((logID) => Utils.convertToObjectID(logID)) + $in: params.logIDs.map((logID) => DatabaseUtils.convertToObjectID(logID)) }; } // Create Aggregation diff --git a/src/storage/mongodb/NotificationStorage.ts b/src/storage/mongodb/NotificationStorage.ts index 6fd1b395f3..ee3434f2d9 100644 --- a/src/storage/mongodb/NotificationStorage.ts +++ b/src/storage/mongodb/NotificationStorage.ts @@ -7,19 +7,20 @@ import DatabaseUtils from './DatabaseUtils'; import DbParams from '../../types/database/DbParams'; import Logging from '../../utils/Logging'; import { Notification } from '../../types/UserNotifications'; +import Tenant from '../../types/Tenant'; import Utils from '../../utils/Utils'; const MODULE_NAME = 'NotificationStorage'; export default class NotificationStorage { - static async getNotifications(tenantID: string, + static async getNotifications(tenant: Tenant, params: { userID?: string; dateFrom?: Date; channel?: string; sourceId?: string; sourceDescr?: string; additionalFilters?: any; chargeBoxID?: string }, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getNotifications'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getNotifications'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -49,7 +50,7 @@ export default class NotificationStorage { } // Set User ID? if (params.userID) { - filters.userID = Utils.convertToObjectID(params.userID); + filters.userID = DatabaseUtils.convertToObjectID(params.userID); } // Set Data if (params.additionalFilters) { @@ -70,7 +71,7 @@ export default class NotificationStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const notificationsCountMDB = await global.database.getCollection(tenantID, 'notifications') + const notificationsCountMDB = await global.database.getCollection(tenant.id, 'notifications') .aggregate([...aggregation, { $count: 'count' }], { allowDiskUse: true }) .toArray(); // Check if only the total count is requested @@ -84,13 +85,13 @@ export default class NotificationStorage { aggregation.pop(); // Charge Box DatabaseUtils.pushChargingStationLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'chargeBoxID', foreignField: '_id', + tenantID: tenant.id, aggregation: aggregation, localField: 'chargeBoxID', foreignField: '_id', asField: 'chargeBox', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); DatabaseUtils.pushConvertObjectIDToString(aggregation, 'chargeBox.siteAreaID'); // Users DatabaseUtils.pushUserLookupInAggregation({ - tenantID, aggregation: aggregation, asField: 'user', localField: 'userID', + tenantID: tenant.id, aggregation: aggregation, asField: 'user', localField: 'userID', foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); // Sort @@ -111,13 +112,13 @@ export default class NotificationStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const notificationsMDB = await global.database.getCollection(tenantID, 'notifications') + const notificationsMDB = await global.database.getCollection(tenant.id, 'notifications') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getNotifications', uniqueTimerID, notificationsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getNotifications', uniqueTimerID, notificationsMDB); // Ok return { count: (notificationsCountMDB.length > 0 ? notificationsCountMDB[0].count : 0), @@ -125,14 +126,14 @@ export default class NotificationStorage { }; } - static async saveNotification(tenantID: string, notificationToSave: Partial): Promise { + static async saveNotification(tenant: Tenant, notificationToSave: Partial): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveNotification'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveNotification'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); const ocpiEndpointMDB: any = { _id: Cypher.hash(`${notificationToSave.sourceId}~${notificationToSave.channel}`), - userID: Utils.convertToObjectID(notificationToSave.userID), + userID: DatabaseUtils.convertToObjectID(notificationToSave.userID), timestamp: Utils.convertToDate(notificationToSave.timestamp), channel: notificationToSave.channel, sourceId: notificationToSave.sourceId, @@ -141,9 +142,9 @@ export default class NotificationStorage { chargeBoxID: notificationToSave.chargeBoxID }; // Create - await global.database.getCollection(tenantID, 'notifications') + await global.database.getCollection(tenant.id, 'notifications') .insertOne(ocpiEndpointMDB); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveNotification', uniqueTimerID, ocpiEndpointMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveNotification', uniqueTimerID, ocpiEndpointMDB); } } diff --git a/src/storage/mongodb/OCPIEndpointStorage.ts b/src/storage/mongodb/OCPIEndpointStorage.ts index 51454dba42..c7a139bc0f 100644 --- a/src/storage/mongodb/OCPIEndpointStorage.ts +++ b/src/storage/mongodb/OCPIEndpointStorage.ts @@ -44,7 +44,7 @@ export default class OCPIEndpointStorage { const ocpiEndpointFilter: any = {}; // Build Request if (ocpiEndpointToSave.id) { - ocpiEndpointFilter._id = Utils.convertToObjectID(ocpiEndpointToSave.id); + ocpiEndpointFilter._id = DatabaseUtils.convertToObjectID(ocpiEndpointToSave.id); } else { ocpiEndpointFilter._id = new ObjectID(); } @@ -105,7 +105,7 @@ export default class OCPIEndpointStorage { } if (params.ocpiEndpointIDs) { filters._id = { - $in: params.ocpiEndpointIDs.map((ocpiEndpointID) => Utils.convertToObjectID(ocpiEndpointID)) + $in: params.ocpiEndpointIDs.map((ocpiEndpointID) => DatabaseUtils.convertToObjectID(ocpiEndpointID)) }; } if (params.localToken) { @@ -181,7 +181,7 @@ export default class OCPIEndpointStorage { await DatabaseUtils.checkTenant(tenantID); // Delete OcpiEndpoint await global.database.getCollection(tenantID, 'ocpiendpoints') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteOcpiEndpoint', uniqueTimerID, { id }); } diff --git a/src/storage/mongodb/OCPPStorage.ts b/src/storage/mongodb/OCPPStorage.ts index d9d1c2fba3..3fd3f3af3a 100644 --- a/src/storage/mongodb/OCPPStorage.ts +++ b/src/storage/mongodb/OCPPStorage.ts @@ -26,7 +26,7 @@ export default class OCPPStorage { tagID: authorize.idTag, authorizationId: authorize.authorizationId, chargeBoxID: authorize.chargeBoxID, - userID: authorize.user ? Utils.convertToObjectID(authorize.user.id) : null, + userID: authorize.user ? DatabaseUtils.convertToObjectID(authorize.user.id) : null, timestamp: timestamp, timezone: authorize.timezone }); diff --git a/src/storage/mongodb/OICPEndpointStorage.ts b/src/storage/mongodb/OICPEndpointStorage.ts index 2d7c80830c..8cbdc193fe 100644 --- a/src/storage/mongodb/OICPEndpointStorage.ts +++ b/src/storage/mongodb/OICPEndpointStorage.ts @@ -37,7 +37,7 @@ export default class OICPEndpointStorage { const oicpEndpointFilter: any = {}; // Build Request if (oicpEndpointToSave.id) { - oicpEndpointFilter._id = Utils.convertToObjectID(oicpEndpointToSave.id); + oicpEndpointFilter._id = DatabaseUtils.convertToObjectID(oicpEndpointToSave.id); } else { oicpEndpointFilter._id = new ObjectID(); } @@ -95,7 +95,7 @@ export default class OICPEndpointStorage { } if (params.oicpEndpointIDs) { filters._id = { - $in: params.oicpEndpointIDs.map((oicpEndpointID) => Utils.convertToObjectID(oicpEndpointID)) + $in: params.oicpEndpointIDs.map((oicpEndpointID) => DatabaseUtils.convertToObjectID(oicpEndpointID)) }; } if (params.localToken) { @@ -171,7 +171,7 @@ export default class OICPEndpointStorage { await DatabaseUtils.checkTenant(tenantID); // Delete OicpEndpoint await global.database.getCollection(tenantID, 'oicpendpoints') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteOicpEndpoint', uniqueTimerID, { id }); } diff --git a/src/storage/mongodb/PerformanceStorage.ts b/src/storage/mongodb/PerformanceStorage.ts index 82ac8db2d9..80361b5400 100644 --- a/src/storage/mongodb/PerformanceStorage.ts +++ b/src/storage/mongodb/PerformanceStorage.ts @@ -1,6 +1,7 @@ import global, { FilterParams } from '../../types/GlobalType'; import Constants from '../../utils/Constants'; +import DatabaseUtils from './DatabaseUtils'; import PerformanceRecord from '../../types/Performance'; import Utils from '../../utils/Utils'; @@ -9,7 +10,7 @@ export default class PerformanceStorage { // Set const performanceRecordMDB: any = { tenantID: performanceRecord.tenantID && performanceRecord.tenantID !== Constants.DEFAULT_TENANT ? - Utils.convertToObjectID(performanceRecord.tenantID) : Constants.DEFAULT_TENANT, + DatabaseUtils.convertToObjectID(performanceRecord.tenantID) : Constants.DEFAULT_TENANT, timestamp: Utils.convertToDate(performanceRecord.timestamp), host: performanceRecord.host, numberOfCPU: performanceRecord.numberOfCPU, @@ -28,11 +29,11 @@ export default class PerformanceStorage { }; // Add user only if provided if (performanceRecord.userID) { - performanceRecordMDB.userID = Utils.convertToObjectID(performanceRecord.userID); + performanceRecordMDB.userID = DatabaseUtils.convertToObjectID(performanceRecord.userID); } // Add parent only if provided if (performanceRecord.parentID) { - performanceRecordMDB.parentID = Utils.convertToObjectID(performanceRecord.parentID); + performanceRecordMDB.parentID = DatabaseUtils.convertToObjectID(performanceRecord.parentID); } // Add nbr charging stations only if provided if (Utils.convertToInt(performanceRecord.numberOfChargingStations) > 0) { diff --git a/src/storage/mongodb/RegistrationTokenStorage.ts b/src/storage/mongodb/RegistrationTokenStorage.ts index 30a84982f5..3375442abd 100644 --- a/src/storage/mongodb/RegistrationTokenStorage.ts +++ b/src/storage/mongodb/RegistrationTokenStorage.ts @@ -19,9 +19,9 @@ export default class RegistrationTokenStorage { await DatabaseUtils.checkTenant(tenantID); // Set const registrationTokenMDB = { - _id: registrationToken.id ? Utils.convertToObjectID(registrationToken.id) : new ObjectID(), + _id: registrationToken.id ? DatabaseUtils.convertToObjectID(registrationToken.id) : new ObjectID(), description: registrationToken.description, - siteAreaID: Utils.convertToObjectID(registrationToken.siteAreaID), + siteAreaID: DatabaseUtils.convertToObjectID(registrationToken.siteAreaID), expirationDate: Utils.convertToDate(registrationToken.expirationDate), revocationDate: Utils.convertToDate(registrationToken.revocationDate) }; @@ -62,18 +62,18 @@ export default class RegistrationTokenStorage { const filters: FilterParams = {}; // Build filter if (params.siteAreaID) { - filters.siteAreaID = Utils.convertToObjectID(params.siteAreaID); + filters.siteAreaID = DatabaseUtils.convertToObjectID(params.siteAreaID); } // Build filter if (!Utils.isEmptyArray(params.tokenIDs)) { filters._id = { - $in: params.tokenIDs.map((tokenID) => Utils.convertToObjectID(tokenID)) + $in: params.tokenIDs.map((tokenID) => DatabaseUtils.convertToObjectID(tokenID)) }; } // Sites if (!Utils.isEmptyArray(params.siteIDs)) { filters['siteArea.siteID'] = { - $in: params.siteIDs.map((siteID) => Utils.convertToObjectID(siteID)) + $in: params.siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) }; } // Filters @@ -151,7 +151,7 @@ export default class RegistrationTokenStorage { // Debug const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteRegistrationToken'); await global.database.getCollection(tenantID, 'registrationtokens') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteRegistrationToken', uniqueTimerID, { id }); } diff --git a/src/storage/mongodb/SettingStorage.ts b/src/storage/mongodb/SettingStorage.ts index 585fcfd196..0c2c44d2ce 100644 --- a/src/storage/mongodb/SettingStorage.ts +++ b/src/storage/mongodb/SettingStorage.ts @@ -46,7 +46,7 @@ export default class SettingStorage { const settingFilter: any = {}; // Build Request if (settingToSave.id) { - settingFilter._id = Utils.convertToObjectID(settingToSave.id); + settingFilter._id = DatabaseUtils.convertToObjectID(settingToSave.id); } else { settingFilter._id = new ObjectID(); } @@ -323,7 +323,7 @@ export default class SettingStorage { const filters: FilterParams = {}; // Source? if (params.settingID) { - filters._id = Utils.convertToObjectID(params.settingID); + filters._id = DatabaseUtils.convertToObjectID(params.settingID); } // Identifier if (params.identifier) { @@ -384,7 +384,7 @@ export default class SettingStorage { await DatabaseUtils.checkTenant(tenantID); // Delete Component await global.database.getCollection(tenantID, 'settings') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteSetting', uniqueTimerID, { id }); } diff --git a/src/storage/mongodb/SiteAreaStorage.ts b/src/storage/mongodb/SiteAreaStorage.ts index 016bafce37..320aa4667b 100644 --- a/src/storage/mongodb/SiteAreaStorage.ts +++ b/src/storage/mongodb/SiteAreaStorage.ts @@ -23,11 +23,11 @@ export default class SiteAreaStorage { if (assetIDs && assetIDs.length > 0) { // Update all assets await global.database.getCollection(tenantID, 'assets').updateMany( - { '_id': { $in: assetIDs.map((assetID) => Utils.convertToObjectID(assetID)) } }, + { '_id': { $in: assetIDs.map((assetID) => DatabaseUtils.convertToObjectID(assetID)) } }, { $set: { - siteAreaID: Utils.convertToObjectID(siteArea.id), - siteID: Utils.convertToObjectID(siteArea.siteID) + siteAreaID: DatabaseUtils.convertToObjectID(siteArea.id), + siteID: DatabaseUtils.convertToObjectID(siteArea.siteID) } }); } @@ -47,7 +47,7 @@ export default class SiteAreaStorage { if (assetIDs && assetIDs.length > 0) { // Update all assets await global.database.getCollection(tenantID, 'assets').updateMany( - { '_id': { $in: assetIDs.map((assetID) => Utils.convertToObjectID(assetID)) } }, + { '_id': { $in: assetIDs.map((assetID) => DatabaseUtils.convertToObjectID(assetID)) } }, { $set: { siteAreaID: null, @@ -67,7 +67,7 @@ export default class SiteAreaStorage { await DatabaseUtils.checkTenant(tenantID); // Read DB const siteAreaImageMDB = await global.database.getCollection<{ _id: ObjectID; image: string }>(tenantID, 'siteareaimages') - .findOne({ _id: Utils.convertToObjectID(id) }); + .findOne({ _id: DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'getSiteAreaImage', uniqueTimerID, siteAreaImageMDB); return { @@ -95,12 +95,12 @@ export default class SiteAreaStorage { await DatabaseUtils.checkTenant(tenantID); // Set const siteAreaMDB: any = { - _id: !siteAreaToSave.id ? new ObjectID() : Utils.convertToObjectID(siteAreaToSave.id), + _id: !siteAreaToSave.id ? new ObjectID() : DatabaseUtils.convertToObjectID(siteAreaToSave.id), name: siteAreaToSave.name, issuer: siteAreaToSave.issuer, accessControl: Utils.convertToBoolean(siteAreaToSave.accessControl), smartCharging: Utils.convertToBoolean(siteAreaToSave.smartCharging), - siteID: Utils.convertToObjectID(siteAreaToSave.siteID), + siteID: DatabaseUtils.convertToObjectID(siteAreaToSave.siteID), maximumPower: Utils.convertToFloat(siteAreaToSave.maximumPower), voltage: Utils.convertToInt(siteAreaToSave.voltage), numberOfPhases: Utils.convertToInt(siteAreaToSave.numberOfPhases), @@ -178,13 +178,13 @@ export default class SiteAreaStorage { // Site Area if (!Utils.isEmptyArray(params.siteAreaIDs)) { filters._id = { - $in: params.siteAreaIDs.map((siteAreaID) => Utils.convertToObjectID(siteAreaID)) + $in: params.siteAreaIDs.map((siteAreaID) => DatabaseUtils.convertToObjectID(siteAreaID)) }; } // Site if (!Utils.isEmptyArray(params.siteIDs)) { filters.siteID = { - $in: params.siteIDs.map((siteID) => Utils.convertToObjectID(siteID)) + $in: params.siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) }; } if (Utils.objectHasProperty(params, 'issuer') && Utils.isBoolean(params.issuer)) { @@ -337,8 +337,8 @@ export default class SiteAreaStorage { { '_id': { $in: chargingStationIDs } }, { $set: { - siteAreaID: Utils.convertToObjectID(siteArea.id), - siteID: Utils.convertToObjectID(siteArea.siteID) + siteAreaID: DatabaseUtils.convertToObjectID(siteArea.id), + siteID: DatabaseUtils.convertToObjectID(siteArea.siteID) } }); } @@ -382,21 +382,21 @@ export default class SiteAreaStorage { await DatabaseUtils.checkTenant(tenantID); // Remove Charging Station's Site Area await global.database.getCollection(tenantID, 'chargingstations').updateMany( - { siteAreaID: { $in: siteAreaIDs.map((ID) => Utils.convertToObjectID(ID)) } }, + { siteAreaID: { $in: siteAreaIDs.map((ID) => DatabaseUtils.convertToObjectID(ID)) } }, { $set: { siteAreaID: null } } ); // Remove Asset's Site Area await global.database.getCollection(tenantID, 'assets').updateMany( - { siteAreaID: { $in: siteAreaIDs.map((ID) => Utils.convertToObjectID(ID)) } }, + { siteAreaID: { $in: siteAreaIDs.map((ID) => DatabaseUtils.convertToObjectID(ID)) } }, { $set: { siteAreaID: null } } ); // Delete SiteArea await global.database.getCollection(tenantID, 'siteareas').deleteMany( - { '_id': { $in: siteAreaIDs.map((ID) => Utils.convertToObjectID(ID)) } } + { '_id': { $in: siteAreaIDs.map((ID) => DatabaseUtils.convertToObjectID(ID)) } } ); // Delete Image await global.database.getCollection(tenantID, 'sitesareaimages').deleteMany( - { '_id': { $in: siteAreaIDs.map((ID) => Utils.convertToObjectID(ID)) } } + { '_id': { $in: siteAreaIDs.map((ID) => DatabaseUtils.convertToObjectID(ID)) } } ); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteSiteAreas', uniqueTimerID, siteAreaIDs); @@ -409,7 +409,7 @@ export default class SiteAreaStorage { await DatabaseUtils.checkTenant(tenantID); // Find site areas to delete const siteareas: string[] = (await global.database.getCollection(tenantID, 'siteareas') - .find({ siteID: { $in: siteIDs.map((id) => Utils.convertToObjectID(id)) } }) + .find({ siteID: { $in: siteIDs.map((id) => DatabaseUtils.convertToObjectID(id)) } }) .project({ _id: 1 }).toArray()).map((idWrapper): string => idWrapper._id.toHexString()); // Delete site areas await SiteAreaStorage.deleteSiteAreas(tenantID, siteareas); @@ -424,7 +424,7 @@ export default class SiteAreaStorage { await DatabaseUtils.checkTenant(tenantID); // Modify await global.database.getCollection(tenantID, 'siteareaimages').findOneAndUpdate( - { '_id': Utils.convertToObjectID(siteAreaID) }, + { '_id': DatabaseUtils.convertToObjectID(siteAreaID) }, { $set: { image: siteAreaImageToSave } }, { upsert: true, returnDocument: 'after' } ); diff --git a/src/storage/mongodb/SiteStorage.ts b/src/storage/mongodb/SiteStorage.ts index 0e1bcdf4c1..227fec47a8 100644 --- a/src/storage/mongodb/SiteStorage.ts +++ b/src/storage/mongodb/SiteStorage.ts @@ -11,14 +11,15 @@ import DbParams from '../../types/database/DbParams'; import Logging from '../../utils/Logging'; import { ObjectID } from 'mongodb'; import SiteAreaStorage from './SiteAreaStorage'; +import Tenant from '../../types/Tenant'; import Utils from '../../utils/Utils'; const MODULE_NAME = 'SiteStorage'; export default class SiteStorage { - public static async getSite(tenantID: string, id: string = Constants.UNKNOWN_OBJECT_ID, + public static async getSite(tenant: Tenant, id: string = Constants.UNKNOWN_OBJECT_ID, params: { withCompany?: boolean, withImage?: boolean; } = {}, projectFields?: string[]): Promise { - const sitesMDB = await SiteStorage.getSites(tenantID, { + const sitesMDB = await SiteStorage.getSites(tenant, { siteIDs: [id], withCompany: params.withCompany, withImage: params.withImage, @@ -26,47 +27,47 @@ export default class SiteStorage { return sitesMDB.count === 1 ? sitesMDB.result[0] : null; } - public static async getSiteImage(tenantID: string, id: string): Promise { + public static async getSiteImage(tenant: Tenant, id: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getSiteImage'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getSiteImage'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Read DB - const siteImageMDB = await global.database.getCollection<{ _id: ObjectID; image: string }>(tenantID, 'siteimages') - .findOne({ _id: Utils.convertToObjectID(id) }); + const siteImageMDB = await global.database.getCollection<{ _id: ObjectID; image: string }>(tenant.id, 'siteimages') + .findOne({ _id: DatabaseUtils.convertToObjectID(id) }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getSiteImage', uniqueTimerID, siteImageMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getSiteImage', uniqueTimerID, siteImageMDB); return { id: id, image: siteImageMDB ? siteImageMDB.image : null }; } - public static async removeUsersFromSite(tenantID: string, siteID: string, userIDs: string[]): Promise { + public static async removeUsersFromSite(tenant: Tenant, siteID: string, userIDs: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'removeUsersFromSite'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'removeUsersFromSite'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Site provided? if (siteID) { // At least one User if (userIDs && userIDs.length > 0) { // Execute - await global.database.getCollection(tenantID, 'siteusers').deleteMany({ - 'userID': { $in: userIDs.map((userID) => Utils.convertToObjectID(userID)) }, - 'siteID': Utils.convertToObjectID(siteID) + await global.database.getCollection(tenant.id, 'siteusers').deleteMany({ + 'userID': { $in: userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) }, + 'siteID': DatabaseUtils.convertToObjectID(siteID) }); } } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'removeUsersFromSite', uniqueTimerID, userIDs); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'removeUsersFromSite', uniqueTimerID, userIDs); } - public static async addUsersToSite(tenantID: string, siteID: string, userIDs: string[]): Promise { + public static async addUsersToSite(tenant: Tenant, siteID: string, userIDs: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'addUsersToSite'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'addUsersToSite'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Site provided? if (siteID) { // At least one User @@ -77,26 +78,26 @@ export default class SiteStorage { // Add siteUsers.push({ '_id': Cypher.hash(`${siteID}~${userID}`), - 'userID': Utils.convertToObjectID(userID), - 'siteID': Utils.convertToObjectID(siteID), + 'userID': DatabaseUtils.convertToObjectID(userID), + 'siteID': DatabaseUtils.convertToObjectID(siteID), 'siteAdmin': false }); } // Execute - await global.database.getCollection(tenantID, 'siteusers').insertMany(siteUsers); + await global.database.getCollection(tenant.id, 'siteusers').insertMany(siteUsers); } } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'addUsersToSite', uniqueTimerID, userIDs); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'addUsersToSite', uniqueTimerID, userIDs); } - public static async getSiteUsers(tenantID: string, + public static async getSiteUsers(tenant: Tenant, params: { search?: string; siteIDs: string[]; siteOwnerOnly?: boolean }, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getSitesUsers'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getSitesUsers'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -110,7 +111,7 @@ export default class SiteStorage { aggregation.push({ $match: { siteID: { - $in: params.siteIDs.map((siteID) => Utils.convertToObjectID(siteID)) + $in: params.siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) } } }); @@ -124,7 +125,7 @@ export default class SiteStorage { } // Users DatabaseUtils.pushUserLookupInAggregation({ - tenantID, aggregation, localField: 'userID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: 'userID', foreignField: '_id', asField: 'user', oneToOneCardinality: true, oneToOneCardinalityNotNull: true }); // Filter deleted users @@ -151,12 +152,12 @@ export default class SiteStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const usersCountMDB = await global.database.getCollection>(tenantID, 'siteusers') + const usersCountMDB = await global.database.getCollection>(tenant.id, 'siteusers') .aggregate([...aggregation, { $count: 'count' }], { allowDiskUse: true }) .toArray(); // Check if only the total count is requested if (dbParams.onlyRecordCount) { - await Logging.traceEnd(tenantID, MODULE_NAME, 'getSitesUsers', uniqueTimerID, usersCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getSitesUsers', uniqueTimerID, usersCountMDB); return { count: (usersCountMDB.length > 0 ? usersCountMDB[0].count : 0), result: [] @@ -187,13 +188,13 @@ export default class SiteStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const siteUsersMDB = await global.database.getCollection<{ user: User; siteID: string; siteAdmin: boolean; siteOwner: boolean }>(tenantID, 'siteusers') + const siteUsersMDB = await global.database.getCollection<{ user: User; siteID: string; siteAdmin: boolean; siteOwner: boolean }>(tenant.id, 'siteusers') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getSitesUsers', uniqueTimerID, siteUsersMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getSitesUsers', uniqueTimerID, siteUsersMDB); return { count: (usersCountMDB.length > 0 ? (usersCountMDB[0].count === Constants.DB_RECORD_COUNT_CEIL ? -1 : usersCountMDB[0].count) : 0), @@ -201,52 +202,52 @@ export default class SiteStorage { }; } - public static async updateSiteOwner(tenantID: string, siteID: string, userID: string, siteOwner: boolean): Promise { - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'updateSiteOwner'); - await DatabaseUtils.checkTenant(tenantID); - await global.database.getCollection(tenantID, 'siteusers').updateMany( + public static async updateSiteOwner(tenant: Tenant, siteID: string, userID: string, siteOwner: boolean): Promise { + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'updateSiteOwner'); + DatabaseUtils.checkTenantObject(tenant); + await global.database.getCollection(tenant.id, 'siteusers').updateMany( { - siteID: Utils.convertToObjectID(siteID), + siteID: DatabaseUtils.convertToObjectID(siteID), siteOwner: true }, { $set: { siteOwner: false } }); - await global.database.getCollection(tenantID, 'siteusers').updateOne( + await global.database.getCollection(tenant.id, 'siteusers').updateOne( { - siteID: Utils.convertToObjectID(siteID), - userID: Utils.convertToObjectID(userID) + siteID: DatabaseUtils.convertToObjectID(siteID), + userID: DatabaseUtils.convertToObjectID(userID) }, { $set: { siteOwner: siteOwner } }); - await Logging.traceEnd(tenantID, MODULE_NAME, 'updateSiteOwner', uniqueTimerID, { siteID, userID }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'updateSiteOwner', uniqueTimerID, { siteID, userID }); } - public static async updateSiteUserAdmin(tenantID: string, siteID: string, userID: string, siteAdmin: boolean): Promise { - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'updateSiteUserAdmin'); - await DatabaseUtils.checkTenant(tenantID); + public static async updateSiteUserAdmin(tenant: Tenant, siteID: string, userID: string, siteAdmin: boolean): Promise { + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'updateSiteUserAdmin'); + DatabaseUtils.checkTenantObject(tenant); - await global.database.getCollection(tenantID, 'siteusers').updateOne( + await global.database.getCollection(tenant.id, 'siteusers').updateOne( { - siteID: Utils.convertToObjectID(siteID), - userID: Utils.convertToObjectID(userID) + siteID: DatabaseUtils.convertToObjectID(siteID), + userID: DatabaseUtils.convertToObjectID(userID) }, { $set: { siteAdmin } }); - await Logging.traceEnd(tenantID, MODULE_NAME, 'updateSiteUserAdmin', uniqueTimerID, { siteID, userID, siteAdmin }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'updateSiteUserAdmin', uniqueTimerID, { siteID, userID, siteAdmin }); } - public static async saveSite(tenantID: string, siteToSave: Site, saveImage = true): Promise { + public static async saveSite(tenant: Tenant, siteToSave: Site, saveImage = true): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveSite'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveSite'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); const siteFilter: any = {}; // Build Request if (siteToSave.id) { - siteFilter._id = Utils.convertToObjectID(siteToSave.id); + siteFilter._id = DatabaseUtils.convertToObjectID(siteToSave.id); } else { siteFilter._id = new ObjectID(); } @@ -255,7 +256,7 @@ export default class SiteStorage { _id: siteFilter._id, issuer: Utils.convertToBoolean(siteToSave.issuer), public: Utils.convertToBoolean(siteToSave.public), - companyID: Utils.convertToObjectID(siteToSave.companyID), + companyID: DatabaseUtils.convertToObjectID(siteToSave.companyID), autoUserSiteAssignment: Utils.convertToBoolean(siteToSave.autoUserSiteAssignment), name: siteToSave.name, }; @@ -275,35 +276,35 @@ export default class SiteStorage { // Add Last Changed/Created props DatabaseUtils.addLastChangedCreatedProps(siteMDB, siteToSave); // Modify and return the modified document - await global.database.getCollection(tenantID, 'sites').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'sites').findOneAndUpdate( siteFilter, { $set: siteMDB }, { upsert: true } ); if (saveImage) { - await SiteStorage.saveSiteImage(tenantID, siteFilter._id.toHexString(), siteToSave.image); + await SiteStorage.saveSiteImage(tenant, siteFilter._id.toHexString(), siteToSave.image); } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveSite', uniqueTimerID, siteMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveSite', uniqueTimerID, siteMDB); return siteFilter._id.toHexString(); } - public static async saveSiteImage(tenantID: string, siteID: string, siteImageToSave: string): Promise { + public static async saveSiteImage(tenant: Tenant, siteID: string, siteImageToSave: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveSiteImage'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveSiteImage'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify - await global.database.getCollection(tenantID, 'siteimages').findOneAndUpdate( - { _id: Utils.convertToObjectID(siteID) }, + await global.database.getCollection(tenant.id, 'siteimages').findOneAndUpdate( + { _id: DatabaseUtils.convertToObjectID(siteID) }, { $set: { image: siteImageToSave } }, { upsert: true, returnDocument: 'after' } ); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveSiteImage', uniqueTimerID, siteImageToSave); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveSiteImage', uniqueTimerID, siteImageToSave); } - public static async getSites(tenantID: string, + public static async getSites(tenant: Tenant, params: { search?: string; companyIDs?: string[]; withAutoUserAssignment?: boolean; siteIDs?: string[]; userID?: string; excludeSitesOfUserID?: boolean; issuer?: boolean; public?: boolean; name?: string; @@ -312,9 +313,9 @@ export default class SiteStorage { } = {}, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getSites'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getSites'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -351,13 +352,13 @@ export default class SiteStorage { // Site if (!Utils.isEmptyArray(params.siteIDs)) { filters._id = { - $in: params.siteIDs.map((siteID) => Utils.convertToObjectID(siteID)) + $in: params.siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) }; } // Company if (!Utils.isEmptyArray(params.companyIDs)) { filters.companyID = { - $in: params.companyIDs.map((company) => Utils.convertToObjectID(company)) + $in: params.companyIDs.map((company) => DatabaseUtils.convertToObjectID(company)) }; } // Issuer @@ -375,13 +376,13 @@ export default class SiteStorage { // Get users if (params.userID || params.excludeSitesOfUserID) { DatabaseUtils.pushCollectionLookupInAggregation('siteusers', - { tenantID, aggregation, localField: '_id', foreignField: 'siteID', asField: 'siteusers' } + { tenantID: tenant.id, aggregation, localField: '_id', foreignField: 'siteID', asField: 'siteusers' } ); if (params.userID) { - filters['siteusers.userID'] = Utils.convertToObjectID(params.userID); + filters['siteusers.userID'] = DatabaseUtils.convertToObjectID(params.userID); } if (params.excludeSitesOfUserID) { - filters['siteusers.userID'] = { $ne: Utils.convertToObjectID(params.excludeSitesOfUserID) }; + filters['siteusers.userID'] = { $ne: DatabaseUtils.convertToObjectID(params.excludeSitesOfUserID) }; } } // Set filters @@ -393,12 +394,12 @@ export default class SiteStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const sitesCountMDB = await global.database.getCollection(tenantID, 'sites') + const sitesCountMDB = await global.database.getCollection(tenant.id, 'sites') .aggregate([...aggregation, { $count: 'count' }], { allowDiskUse: true }) .toArray(); // Check if only the total count is requested if (dbParams.onlyRecordCount) { - await Logging.traceEnd(tenantID, MODULE_NAME, 'getSites', uniqueTimerID, sitesCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getSites', uniqueTimerID, sitesCountMDB); return { count: (sitesCountMDB.length > 0 ? sitesCountMDB[0].count : 0), result: [] @@ -428,7 +429,7 @@ export default class SiteStorage { // Add Company if (params.withCompany) { DatabaseUtils.pushCompanyLookupInAggregation({ - tenantID, aggregation, localField: 'companyID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: 'companyID', foreignField: '_id', asField: 'company', oneToOneCardinality: true }); } @@ -440,7 +441,7 @@ export default class SiteStorage { $concat: [ `${Utils.buildRestServerURL()}/client/util/SiteImage?ID=`, { $toString: '$_id' }, - `&TenantID=${tenantID}&LastChangedOn=`, + `&TenantID=${tenant.id}&LastChangedOn=`, { $toString: '$lastChangedOn' } ] } @@ -450,13 +451,13 @@ export default class SiteStorage { // Convert Object ID to string DatabaseUtils.pushConvertObjectIDToString(aggregation, 'companyID'); // Add Last Changed / Created - DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation); + DatabaseUtils.pushCreatedLastChangedInAggregation(tenant.id, aggregation); // Handle the ID DatabaseUtils.pushRenameDatabaseID(aggregation); // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const sitesMDB = await global.database.getCollection(tenantID, 'sites') + const sitesMDB = await global.database.getCollection(tenant.id, 'sites') .aggregate(aggregation, { allowDiskUse: true }) @@ -468,7 +469,7 @@ export default class SiteStorage { for (const siteMDB of sitesMDB) { if (params.withOnlyChargingStations || params.withAvailableChargingStations) { // Get the chargers - const chargingStations = await ChargingStationStorage.getChargingStations(tenantID, + const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, { siteIDs: [siteMDB.id], includeDeleted: false }, Constants.DB_PARAMS_MAX_LIMIT); // Skip site with no charging stations if asked if (params.withOnlyChargingStations && chargingStations.count === 0) { @@ -488,7 +489,7 @@ export default class SiteStorage { } } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getSites', uniqueTimerID, sites); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getSites', uniqueTimerID, sites); return { projectedFields: projectFields, count: (sitesCountMDB.length > 0 ? @@ -497,61 +498,61 @@ export default class SiteStorage { }; } - public static async deleteSite(tenantID: string, id: string): Promise { - await SiteStorage.deleteSites(tenantID, [id]); + public static async deleteSite(tenant: Tenant, id: string): Promise { + await SiteStorage.deleteSites(tenant, [id]); } - public static async deleteSites(tenantID: string, ids: string[]): Promise { + public static async deleteSites(tenant: Tenant, ids: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteSites'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteSites'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Delete all Site Areas - await SiteAreaStorage.deleteSiteAreasFromSites(tenantID, ids); + await SiteAreaStorage.deleteSiteAreasFromSites(tenant.id, ids); // Convert - const cids: ObjectID[] = ids.map((id) => Utils.convertToObjectID(id)); + const cids: ObjectID[] = ids.map((id) => DatabaseUtils.convertToObjectID(id)); // Delete Site - await global.database.getCollection(tenantID, 'sites') + await global.database.getCollection(tenant.id, 'sites') .deleteMany({ '_id': { $in: cids } }); // Delete Image - await global.database.getCollection(tenantID, 'siteimages') + await global.database.getCollection(tenant.id, 'siteimages') .deleteMany({ '_id': { $in: cids } }); // Delete Site's Users - await global.database.getCollection(tenantID, 'siteusers') + await global.database.getCollection(tenant.id, 'siteusers') .deleteMany({ 'siteID': { $in: cids } }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteSites', uniqueTimerID, { ids }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteSites', uniqueTimerID, { ids }); } - public static async deleteCompanySites(tenantID: string, companyID: string): Promise { + public static async deleteCompanySites(tenant: Tenant, companyID: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteCompanySites'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteCompanySites'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Get Sites of Company - const siteIDs: string[] = (await global.database.getCollection<{ _id: ObjectID }>(tenantID, 'sites') - .find({ companyID: Utils.convertToObjectID(companyID) }) + const siteIDs: string[] = (await global.database.getCollection<{ _id: ObjectID }>(tenant.id, 'sites') + .find({ companyID: DatabaseUtils.convertToObjectID(companyID) }) .project({ _id: 1 }) .toArray()) .map((site): string => site._id.toHexString()); // Delete all Site Areas - await SiteAreaStorage.deleteSiteAreasFromSites(tenantID, siteIDs); + await SiteAreaStorage.deleteSiteAreasFromSites(tenant.id, siteIDs); // Delete Sites - await SiteStorage.deleteSites(tenantID, siteIDs); + await SiteStorage.deleteSites(tenant, siteIDs); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteCompanySites', uniqueTimerID, { companyID }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteCompanySites', uniqueTimerID, { companyID }); } - public static async siteHasUser(tenantID: string, siteID: string, userID: string): Promise { + public static async siteHasUser(tenant: Tenant, siteID: string, userID: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'siteHasUser'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'siteHasUser'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Exec - const result = await global.database.getCollection(tenantID, 'siteusers').findOne( - { siteID: Utils.convertToObjectID(siteID), userID: Utils.convertToObjectID(userID) }); + const result = await global.database.getCollection(tenant.id, 'siteusers').findOne( + { siteID: DatabaseUtils.convertToObjectID(siteID), userID: DatabaseUtils.convertToObjectID(userID) }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteCompanySites', uniqueTimerID, { siteID }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteCompanySites', uniqueTimerID, { siteID }); // Check if (!result) { return false; diff --git a/src/storage/mongodb/StatisticsStorage.ts b/src/storage/mongodb/StatisticsStorage.ts index a546c9cfe5..01606ee443 100644 --- a/src/storage/mongodb/StatisticsStorage.ts +++ b/src/storage/mongodb/StatisticsStorage.ts @@ -35,13 +35,13 @@ export default class StatisticsStorage { // Site if (!Utils.isEmptyArray(params.siteIDs)) { filters.siteID = { - $in: params.siteIDs.map((siteID) => Utils.convertToObjectID(siteID)) + $in: params.siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) }; } // Filter on Site Area? if (!Utils.isEmptyArray(params.siteAreaIDs)) { filters.siteAreaID = { - $in: params.siteAreaIDs.map((siteAreaID) => Utils.convertToObjectID(siteAreaID)) + $in: params.siteAreaIDs.map((siteAreaID) => DatabaseUtils.convertToObjectID(siteAreaID)) }; } // Filter on Charge Box? @@ -53,7 +53,7 @@ export default class StatisticsStorage { // Filter on User? if (!Utils.isEmptyArray(params.userIDs)) { filters.userID = { - $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) + $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) }; } // Create Aggregation @@ -156,13 +156,13 @@ export default class StatisticsStorage { // Filter on Site? if (!Utils.isEmptyArray(params.siteIDs)) { filters.siteID = { - $in: params.siteIDs.map((siteID) => Utils.convertToObjectID(siteID)) + $in: params.siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) }; } // Filter on Site Area? if (!Utils.isEmptyArray(params.siteAreaIDs)) { filters.siteAreaID = { - $in: params.siteAreaIDs.map((siteAreaID) => Utils.convertToObjectID(siteAreaID)) + $in: params.siteAreaIDs.map((siteAreaID) => DatabaseUtils.convertToObjectID(siteAreaID)) }; } // Filter on Charge Box? @@ -174,7 +174,7 @@ export default class StatisticsStorage { // Filter on User? if (!Utils.isEmptyArray(params.userIDs)) { filters.userID = { - $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) + $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) }; } // Create Aggregation diff --git a/src/storage/mongodb/TagStorage.ts b/src/storage/mongodb/TagStorage.ts index 6ef7d502dc..6e5d4dc74d 100644 --- a/src/storage/mongodb/TagStorage.ts +++ b/src/storage/mongodb/TagStorage.ts @@ -6,6 +6,7 @@ import { DataResult } from '../../types/DataResult'; import DatabaseUtils from './DatabaseUtils'; import DbParams from '../../types/database/DbParams'; import Logging from '../../utils/Logging'; +import { ObjectID } from 'mongodb'; import Utils from '../../utils/Utils'; import moment from 'moment'; @@ -20,11 +21,11 @@ export default class TagStorage { await DatabaseUtils.checkTenant(tenantID); const tagMDB = { _id: tag.id, - userID: Utils.convertToObjectID(tag.userID), + userID: DatabaseUtils.convertToObjectID(tag.userID), issuer: Utils.convertToBoolean(tag.issuer), active: Utils.convertToBoolean(tag.active), default: Utils.convertToBoolean(tag.default), - visualID: tag.visualID, + visualID: tag.visualID ?? new ObjectID().toHexString(), ocpiToken: tag.ocpiToken, description: tag.description }; @@ -219,7 +220,7 @@ export default class TagStorage { await DatabaseUtils.checkTenant(tenantID); await global.database.getCollection(tenantID, 'tags').updateMany( { - userID: Utils.convertToObjectID(userID), + userID: DatabaseUtils.convertToObjectID(userID), default: true }, { @@ -251,7 +252,7 @@ export default class TagStorage { // Delete const result = await global.database.getCollection(tenantID, 'tags').deleteMany( { - 'userID': Utils.convertToObjectID(userID), + 'userID': DatabaseUtils.convertToObjectID(userID), } ); // Debug @@ -272,22 +273,14 @@ export default class TagStorage { public static async getTagByVisualID(tenantID: string, visualID: string, params: { withUser?: boolean; withNbrTransactions?: boolean } = {}, projectFields?: string[]): Promise { - const tagMDB = await TagStorage.getTagByVisualIDs(tenantID, [visualID], { + const tagMDB = await TagStorage.getTags(tenantID, { + visualIDs: [visualID], withUser: params.withUser, withNbrTransactions: params.withNbrTransactions, }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); return tagMDB.count === 1 ? tagMDB.result[0] : null; } - public static async getTagByVisualIDs(tenantID: string, visualIDs: string[], - params: { withUser?: boolean; withNbrTransactions?: boolean } = {}, dbParams: DbParams, projectFields?: string[]): Promise> { - return await TagStorage.getTags(tenantID, { - visualIDs: visualIDs, - withUser: params.withUser, - withNbrTransactions: params.withNbrTransactions, - }, dbParams, projectFields); - } - public static async getFirstActiveUserTag(tenantID: string, userID: string, params: { issuer?: boolean; } = {}, projectFields?: string[]): Promise { const tagMDB = await TagStorage.getTags(tenantID, { @@ -347,7 +340,7 @@ export default class TagStorage { } // Users if (!Utils.isEmptyArray(params.userIDs)) { - filters.userID = { $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) }; + filters.userID = { $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) }; if (params.defaultTag) { filters.default = true; } diff --git a/src/storage/mongodb/TenantStorage.ts b/src/storage/mongodb/TenantStorage.ts index e28de55017..fa33951969 100644 --- a/src/storage/mongodb/TenantStorage.ts +++ b/src/storage/mongodb/TenantStorage.ts @@ -52,7 +52,7 @@ export default class TenantStorage { const tenantFilter: any = {}; // Build Request if (tenantToSave.id) { - tenantFilter._id = Utils.convertToObjectID(tenantToSave.id); + tenantFilter._id = DatabaseUtils.convertToObjectID(tenantToSave.id); } else { tenantFilter._id = new ObjectID(); } @@ -127,7 +127,7 @@ export default class TenantStorage { // Tenant if (!Utils.isEmptyArray(params.tenantIDs)) { filters._id = { - $in: params.tenantIDs.map((tenantID) => Utils.convertToObjectID(tenantID)) + $in: params.tenantIDs.map((tenantID) => DatabaseUtils.convertToObjectID(tenantID)) }; } // Name @@ -223,7 +223,7 @@ export default class TenantStorage { // Delete await global.database.getCollection(Constants.DEFAULT_TENANT, 'tenants') .findOneAndDelete({ - '_id': Utils.convertToObjectID(id) + '_id': DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(Constants.DEFAULT_TENANT, MODULE_NAME, 'deleteTenant', uniqueTimerID, { id }); @@ -245,7 +245,7 @@ export default class TenantStorage { await DatabaseUtils.checkTenant(tenantID); // Read DB const tenantLogoMDB = await global.database.getCollection<{ _id: ObjectID; logo: string }>(Constants.DEFAULT_TENANT, 'tenantlogos') - .findOne({ _id: Utils.convertToObjectID(tenantID) }); + .findOne({ _id: DatabaseUtils.convertToObjectID(tenantID) }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'getTenantLogo', uniqueTimerID, tenantLogoMDB); return { @@ -261,7 +261,7 @@ export default class TenantStorage { await DatabaseUtils.checkTenant(tenantID); // Modify await global.database.getCollection(Constants.DEFAULT_TENANT, 'tenantlogos').findOneAndUpdate( - { '_id': Utils.convertToObjectID(tenantID) }, + { '_id': DatabaseUtils.convertToObjectID(tenantID) }, { $set: { logo: tenantLogoToSave } }, { upsert: true }); // Debug diff --git a/src/storage/mongodb/TransactionStorage.ts b/src/storage/mongodb/TransactionStorage.ts index 71c55148bd..81052fa13f 100644 --- a/src/storage/mongodb/TransactionStorage.ts +++ b/src/storage/mongodb/TransactionStorage.ts @@ -53,13 +53,13 @@ export default class TransactionStorage { const transactionMDB: any = { _id: Utils.convertToInt(transactionToSave.id), issuer: Utils.convertToBoolean(transactionToSave.issuer), - siteID: Utils.convertToObjectID(transactionToSave.siteID), - siteAreaID: Utils.convertToObjectID(transactionToSave.siteAreaID), + siteID: DatabaseUtils.convertToObjectID(transactionToSave.siteID), + siteAreaID: DatabaseUtils.convertToObjectID(transactionToSave.siteAreaID), connectorId: Utils.convertToInt(transactionToSave.connectorId), tagID: transactionToSave.tagID, - carID: transactionToSave.carID ? Utils.convertToObjectID(transactionToSave.carID) : null, + carID: transactionToSave.carID ? DatabaseUtils.convertToObjectID(transactionToSave.carID) : null, carCatalogID: transactionToSave.carCatalogID ? Utils.convertToInt(transactionToSave.carCatalogID) : null, - userID: Utils.convertToObjectID(transactionToSave.userID), + userID: DatabaseUtils.convertToObjectID(transactionToSave.userID), chargeBoxID: transactionToSave.chargeBoxID, meterStart: Utils.convertToInt(transactionToSave.meterStart), timestamp: Utils.convertToDate(transactionToSave.timestamp), @@ -107,7 +107,7 @@ export default class TransactionStorage { if (transactionToSave.stop) { // Add stop transactionMDB.stop = { - userID: Utils.convertToObjectID(transactionToSave.stop.userID), + userID: DatabaseUtils.convertToObjectID(transactionToSave.stop.userID), timestamp: Utils.convertToDate(transactionToSave.stop.timestamp), tagID: transactionToSave.stop.tagID, meterStop: transactionToSave.stop.meterStop, @@ -156,7 +156,7 @@ export default class TransactionStorage { transactionMDB.remotestop = { timestamp: Utils.convertToDate(transactionToSave.remotestop.timestamp), tagID: transactionToSave.remotestop.tagID, - userID: Utils.convertToObjectID(transactionToSave.remotestop.userID) + userID: DatabaseUtils.convertToObjectID(transactionToSave.remotestop.userID) }; } if (transactionToSave.refundData) { @@ -173,7 +173,7 @@ export default class TransactionStorage { lastUpdate: Utils.convertToDate(transactionToSave.billingData.lastUpdate), stop: { status: transactionToSave.billingData.stop?.status, - invoiceID: Utils.convertToObjectID(transactionToSave.billingData.stop?.invoiceID), + invoiceID: DatabaseUtils.convertToObjectID(transactionToSave.billingData.stop?.invoiceID), invoiceNumber: transactionToSave.billingData.stop?.invoiceNumber, invoiceStatus: transactionToSave.billingData.stop?.invoiceStatus, invoiceItem: transactionToSave.billingData.stop?.invoiceItem, @@ -220,7 +220,7 @@ export default class TransactionStorage { ] }, { $set: { - userID: Utils.convertToObjectID(userID) + userID: DatabaseUtils.convertToObjectID(userID) } }); // Debug @@ -273,7 +273,7 @@ export default class TransactionStorage { endDateTime?: Date; stop?: any; minimalPrice?: boolean; reportIDs?: string[]; tagIDs?: string[]; inactivityStatus?: string[]; ocpiSessionID?: string; ocpiAuthorizationID?: string; ocpiSessionDateFrom?: Date; ocpiSessionDateTo?: Date; ocpiCdrDateFrom?: Date; ocpiCdrDateTo?: Date; ocpiSessionChecked?: boolean; ocpiCdrChecked?: boolean; oicpSessionID?: string; - statistics?: 'refund' | 'history'; refundStatus?: string[]; withTag?: boolean; hasUserID?: boolean; + statistics?: 'refund' | 'history' | 'ongoing'; refundStatus?: string[]; withTag?: boolean; hasUserID?: boolean; }, dbParams: DbParams, projectFields?: string[]): Promise<{ @@ -299,13 +299,13 @@ export default class TransactionStorage { // User / Site Admin if (params.ownerID) { ownerMatch.$or.push({ - userID: Utils.convertToObjectID(params.ownerID) + userID: DatabaseUtils.convertToObjectID(params.ownerID) }); } if (params.siteAdminIDs) { ownerMatch.$or.push({ siteID: { - $in: params.siteAdminIDs.map((siteID) => Utils.convertToObjectID(siteID)) + $in: params.siteAdminIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) } }); } @@ -343,7 +343,7 @@ export default class TransactionStorage { } // User if (params.userIDs) { - filters.userID = { $in: params.userIDs.map((siteID) => Utils.convertToObjectID(siteID)) }; + filters.userID = { $in: params.userIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) }; } // Charge Box if (params.chargeBoxIDs) { @@ -363,7 +363,7 @@ export default class TransactionStorage { // Connector if (!Utils.isEmptyArray(params.connectorIDs)) { filters.connectorId = { - $in: params.connectorIDs.map((connectorID) => Utils.convertToObjectID(connectorID)) + $in: params.connectorIDs.map((connectorID) => DatabaseUtils.convertToObjectID(connectorID)) }; } // Date provided? @@ -419,13 +419,13 @@ export default class TransactionStorage { // Site's area ID if (params.siteAreaIDs) { filters.siteAreaID = { - $in: params.siteAreaIDs.map((siteAreaID) => Utils.convertToObjectID(siteAreaID)) + $in: params.siteAreaIDs.map((siteAreaID) => DatabaseUtils.convertToObjectID(siteAreaID)) }; } // Site ID if (params.siteIDs) { filters.siteID = { - $in: params.siteIDs.map((siteID) => Utils.convertToObjectID(siteID)) + $in: params.siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) }; } // Refund status @@ -480,6 +480,21 @@ export default class TransactionStorage { } }; break; + case 'ongoing': // For ongoing case + statsQuery = { + $group: { + _id: null, + firstTimestamp: { $min: '$timestamp' }, + lastTimestamp: { $max: '$timestamp' }, + totalConsumptionWattHours: { $sum: '$currentTotalConsumptionWh' }, + totalDurationSecs: { $sum: '$currentTotalDurationSecs' }, + totalPrice: { $sum: '$currentCumulatedPrice' }, + totalInactivitySecs: { $sum: '$currentTotalInactivitySecs' }, + currency: { $addToSet: '$priceUnit' }, + count: { $sum: 1 } + } + }; + break; case 'refund': // For refund case statsQuery = { $group: { @@ -515,6 +530,7 @@ export default class TransactionStorage { if (!transactionCountMDB) { switch (params.statistics) { case 'history': + case 'ongoing': transactionCountMDB = { totalConsumptionWattHours: 0, totalDurationSecs: 0, @@ -695,13 +711,13 @@ export default class TransactionStorage { filters['refundData.reportId'] = { '$ne': null }; if (params.ownerID) { ownerMatch.$or.push({ - userID: Utils.convertToObjectID(params.ownerID) + userID: DatabaseUtils.convertToObjectID(params.ownerID) }); } if (params.siteAdminIDs) { ownerMatch.$or.push({ siteID: { - $in: params.siteAdminIDs.map((siteID) => Utils.convertToObjectID(siteID)) + $in: params.siteAdminIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) } }); } @@ -838,7 +854,7 @@ export default class TransactionStorage { } // User / Site Admin if (params.userIDs) { - match.userID = { $in: params.userIDs.map((user) => Utils.convertToObjectID(user)) }; + match.userID = { $in: params.userIDs.map((user) => DatabaseUtils.convertToObjectID(user)) }; } // Charge Box if (params.chargingStationIDs) { @@ -859,19 +875,19 @@ export default class TransactionStorage { // Site Areas if (params.siteAreaIDs) { match.siteAreaID = { - $in: params.siteAreaIDs.map((area) => Utils.convertToObjectID(area)) + $in: params.siteAreaIDs.map((area) => DatabaseUtils.convertToObjectID(area)) }; } // Sites if (params.siteIDs) { match.siteID = { - $in: params.siteIDs.map((site) => Utils.convertToObjectID(site)) + $in: params.siteIDs.map((site) => DatabaseUtils.convertToObjectID(site)) }; } // Connectors if (!Utils.isEmptyArray(params.connectorIDs)) { match.connectorId = { - $in: params.connectorIDs.map((connectorID) => Utils.convertToObjectID(connectorID)) + $in: params.connectorIDs.map((connectorID) => DatabaseUtils.convertToObjectID(connectorID)) }; } // Create Aggregation diff --git a/src/storage/mongodb/UserStorage.ts b/src/storage/mongodb/UserStorage.ts index 434872aadb..8fc2b0e123 100644 --- a/src/storage/mongodb/UserStorage.ts +++ b/src/storage/mongodb/UserStorage.ts @@ -131,7 +131,7 @@ export default class UserStorage { await DatabaseUtils.checkTenant(tenantID); // Read DB const userImageMDB = await global.database.getCollection<{ _id: ObjectID; image: string }>(tenantID, 'userimages') - .findOne({ _id: Utils.convertToObjectID(id) }); + .findOne({ _id: DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'getUserImage', uniqueTimerID, userImageMDB); return { @@ -150,8 +150,8 @@ export default class UserStorage { if (!Utils.isEmptyArray(siteIDs)) { // Create the lis await global.database.getCollection(tenantID, 'siteusers').deleteMany({ - 'userID': Utils.convertToObjectID(userID), - 'siteID': { $in: siteIDs.map((siteID) => Utils.convertToObjectID(siteID)) } + 'userID': DatabaseUtils.convertToObjectID(userID), + 'siteID': { $in: siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) } }); } } @@ -171,8 +171,8 @@ export default class UserStorage { for (const siteID of siteIDs) { siteUsersMDB.push({ '_id': Cypher.hash(`${siteID}~${userID}`), - 'userID': Utils.convertToObjectID(userID), - 'siteID': Utils.convertToObjectID(siteID), + 'userID': DatabaseUtils.convertToObjectID(userID), + 'siteID': DatabaseUtils.convertToObjectID(siteID), 'siteAdmin': false }); } @@ -200,14 +200,14 @@ export default class UserStorage { // Build Request const userFilter: any = {}; if (userToSave.id) { - userFilter._id = Utils.convertToObjectID(userToSave.id); + userFilter._id = DatabaseUtils.convertToObjectID(userToSave.id); } else { userFilter.email = userToSave.email; } // Properties to save // eslint-disable-next-line prefer-const const userMDB: any = { - _id: userToSave.id ? Utils.convertToObjectID(userToSave.id) : new ObjectID(), + _id: userToSave.id ? DatabaseUtils.convertToObjectID(userToSave.id) : new ObjectID(), issuer: Utils.convertToBoolean(userToSave.issuer), name: userToSave.name, firstName: userToSave.firstName, @@ -277,14 +277,14 @@ export default class UserStorage { public static async saveImportedUser(tenantID: string, importedUserToSave: ImportedUser): Promise { const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveImportedUser'); const userMDB = { - _id: importedUserToSave.id ? Utils.convertToObjectID(importedUserToSave.id) : new ObjectID(), + _id: importedUserToSave.id ? DatabaseUtils.convertToObjectID(importedUserToSave.id) : new ObjectID(), email: importedUserToSave.email, firstName: importedUserToSave.firstName, name: importedUserToSave.name, status: importedUserToSave.status, errorDescription: importedUserToSave.errorDescription, importedOn: Utils.convertToDate(importedUserToSave.importedOn), - importedBy: Utils.convertToObjectID(importedUserToSave.importedBy) + importedBy: DatabaseUtils.convertToObjectID(importedUserToSave.importedBy) }; await global.database.getCollection(tenantID, 'importedusers').findOneAndUpdate( { _id: userMDB._id }, @@ -299,14 +299,14 @@ export default class UserStorage { public static async saveImportedUsers(tenantID: string, importedUsersToSave: ImportedUser[]): Promise { const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveImportedUsers'); const importedUsersToSaveMDB: any = importedUsersToSave.map((importedUserToSave) => ({ - _id: importedUserToSave.id ? Utils.convertToObjectID(importedUserToSave.id) : new ObjectID(), + _id: importedUserToSave.id ? DatabaseUtils.convertToObjectID(importedUserToSave.id) : new ObjectID(), email: importedUserToSave.email, firstName: importedUserToSave.firstName, name: importedUserToSave.name, status: importedUserToSave.status, errorDescription: importedUserToSave.errorDescription, importedOn: Utils.convertToDate(importedUserToSave.importedOn), - importedBy: Utils.convertToObjectID(importedUserToSave.importedBy) + importedBy: DatabaseUtils.convertToObjectID(importedUserToSave.importedBy) })); // Insert all at once const result = await global.database.getCollection(tenantID, 'importedusers').insertMany( @@ -326,7 +326,7 @@ export default class UserStorage { // Delete await global.database.getCollection(tenantID, 'importedusers').deleteOne( { - '_id': Utils.convertToObjectID(importedUserID), + '_id': DatabaseUtils.convertToObjectID(importedUserID), }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteImportedUser', uniqueTimerID, { id: importedUserID }); @@ -354,7 +354,7 @@ export default class UserStorage { await DatabaseUtils.checkTenant(tenantID); // Modify and return the modified document await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: params }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserPassword', uniqueTimerID); @@ -367,7 +367,7 @@ export default class UserStorage { await DatabaseUtils.checkTenant(tenantID); // Modify and return the modified document await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: { status } }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserStatus', uniqueTimerID); @@ -380,7 +380,7 @@ export default class UserStorage { await DatabaseUtils.checkTenant(tenantID); // Modify and return the modified document await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: { lastSelectedCarID } }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserStatus', uniqueTimerID); @@ -394,7 +394,7 @@ export default class UserStorage { await DatabaseUtils.checkTenant(tenantID); // Modify and return the modified document await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: params }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserMobileToken', uniqueTimerID); @@ -408,7 +408,7 @@ export default class UserStorage { await DatabaseUtils.checkTenant(tenantID); // Modify and return the modified document await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: params }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserMobilePhone', uniqueTimerID); @@ -421,7 +421,7 @@ export default class UserStorage { await DatabaseUtils.checkTenant(tenantID); // Modify and return the modified document await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: { role } }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserRole', uniqueTimerID); @@ -435,7 +435,7 @@ export default class UserStorage { await DatabaseUtils.checkTenant(tenantID); // Modify and return the modified document await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: params }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserRole', uniqueTimerID, params); @@ -449,7 +449,7 @@ export default class UserStorage { await DatabaseUtils.checkTenant(tenantID); // Modify and return the modified document await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: params }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserAccountVerification', uniqueTimerID, params); @@ -475,7 +475,7 @@ export default class UserStorage { } // Modify and return the modified document await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: updatedUserMDB }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserAdminData', uniqueTimerID, params); @@ -499,11 +499,11 @@ export default class UserStorage { }; // Modify and return the modified document await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: updatedUserMDB }); } else { await global.database.getCollection(tenantID, 'users').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $unset: { billingData: '' } }); // This removes the field from the document } // Debug @@ -527,7 +527,7 @@ export default class UserStorage { } // Modify and return the modified document await global.database.getCollection(tenantID, 'userimages').findOneAndUpdate( - { '_id': Utils.convertToObjectID(userID) }, + { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: { image: userImageToSave } }, { upsert: true, returnDocument: 'after' }); // Debug @@ -568,7 +568,7 @@ export default class UserStorage { } // Users if (!Utils.isEmptyArray(params.userIDs)) { - filters._id = { $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) }; + filters._id = { $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) }; } // Issuer if (Utils.objectHasProperty(params, 'issuer') && Utils.isBoolean(params.issuer)) { @@ -576,7 +576,7 @@ export default class UserStorage { } // Exclude Users if (!Utils.isEmptyArray(params.excludeUserIDs)) { - filters._id = { $nin: params.excludeUserIDs.map((userID) => Utils.convertToObjectID(userID)) }; + filters._id = { $nin: params.excludeUserIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) }; } // Email if (params.email) { @@ -641,14 +641,14 @@ export default class UserStorage { }); // Add Car ID in OR const carIDFilter = {}; - carIDFilter['carUsers.carID'] = { $ne: Utils.convertToObjectID(params.notAssignedToCarID) }; + carIDFilter['carUsers.carID'] = { $ne: DatabaseUtils.convertToObjectID(params.notAssignedToCarID) }; notAssignedToCarIDFilter.$or.push(carIDFilter); // Bypass Car ID if users has been removed in UI if (params.includeCarUserIDs) { const includeCarUserIDsFilter = {}; includeCarUserIDsFilter['carUsers.userID'] = { $in: params.includeCarUserIDs.map((includeCarUserID) => - Utils.convertToObjectID(includeCarUserID)) + DatabaseUtils.convertToObjectID(includeCarUserID)) }; notAssignedToCarIDFilter.$or.push(includeCarUserIDsFilter); } @@ -673,12 +673,12 @@ export default class UserStorage { }); if (params.siteIDs) { aggregation.push({ - $match: { 'siteusers.siteID': { $in: params.siteIDs.map((site) => Utils.convertToObjectID(site)) } } + $match: { 'siteusers.siteID': { $in: params.siteIDs.map((site) => DatabaseUtils.convertToObjectID(site)) } } }); } if (params.excludeSiteID) { aggregation.push({ - $match: { 'siteusers.siteID': { $ne: Utils.convertToObjectID(params.excludeSiteID) } } + $match: { 'siteusers.siteID': { $ne: DatabaseUtils.convertToObjectID(params.excludeSiteID) } } }); } } @@ -953,19 +953,19 @@ export default class UserStorage { await DatabaseUtils.checkTenant(tenantID); // Delete User Image await global.database.getCollection(tenantID, 'userimages') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Delete Site Users await global.database.getCollection(tenantID, 'siteusers') - .deleteMany({ 'userID': Utils.convertToObjectID(id) }); + .deleteMany({ 'userID': DatabaseUtils.convertToObjectID(id) }); // Delete Tags await global.database.getCollection(tenantID, 'tags') - .deleteMany({ 'userID': Utils.convertToObjectID(id) }); + .deleteMany({ 'userID': DatabaseUtils.convertToObjectID(id) }); // Delete Connections await global.database.getCollection(tenantID, 'connections') - .deleteMany({ 'userId': Utils.convertToObjectID(id) }); + .deleteMany({ 'userId': DatabaseUtils.convertToObjectID(id) }); // Delete User await global.database.getCollection(tenantID, 'users') - .findOneAndDelete({ '_id': Utils.convertToObjectID(id) }); + .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteUser', uniqueTimerID, { id }); } @@ -988,7 +988,7 @@ export default class UserStorage { // Filter if (!Utils.isEmptyArray(params.userIDs)) { filters.userID = { - $in: params.userIDs.map((userID) => Utils.convertToObjectID(userID)) + $in: params.userIDs.map((userID) => DatabaseUtils.convertToObjectID(userID)) }; } if (params.siteAdmin) { diff --git a/src/types/Server.ts b/src/types/Server.ts index 16ceb39f78..dabb5d8bac 100644 --- a/src/types/Server.ts +++ b/src/types/Server.ts @@ -506,6 +506,8 @@ export enum ServerRoute { REST_TAGS = 'tags', REST_TAG = 'tags/:id', + REST_TAG_IMPORT = 'tags/action/import', + REST_TAG_EXPORT = 'tags/action/export', REST_ASSET_CONSUMPTION = 'assets/:assetID/consumption', diff --git a/src/types/Tag.ts b/src/types/Tag.ts index aded79c962..8c4ee8d789 100644 --- a/src/types/Tag.ts +++ b/src/types/Tag.ts @@ -7,7 +7,7 @@ import User from './User'; export default interface Tag extends CreatedUpdatedProps, AuthorizationActions { id: string; description?: string; - visualID: string; + visualID?: string; issuer: boolean; active: boolean; userID?: string; diff --git a/src/types/requests/HttpTransactionRequest.ts b/src/types/requests/HttpTransactionRequest.ts index 7f5929d954..72d19f577e 100644 --- a/src/types/requests/HttpTransactionRequest.ts +++ b/src/types/requests/HttpTransactionRequest.ts @@ -37,7 +37,7 @@ export interface HttpTransactionsRequest extends HttpDatabaseRequest { RefundStatus?: string; InactivityStatus?: InactivityStatus; MinimalPrice?: boolean; - Statistics?: 'refund' | 'history'; + Statistics?: 'refund' | 'history' | 'ongoing'; ReportIDs?: string; Status?: 'completed' | 'active'; } diff --git a/src/types/requests/HttpUserRequest.ts b/src/types/requests/HttpUserRequest.ts index 0c483358c6..2bae94bb02 100644 --- a/src/types/requests/HttpUserRequest.ts +++ b/src/types/requests/HttpUserRequest.ts @@ -68,9 +68,7 @@ export interface HttpResetPasswordRequest { email: string; tenant: string; captcha: string; - passwords: { password: string; repeatPassword: string }; // Frontend... - password?: string; - repeatPassword?: string; + password: string; hash: string; } export interface HttpCheckEulaRequest { @@ -80,7 +78,7 @@ export interface HttpCheckEulaRequest { export interface HttpRegisterUserRequest extends HttpLoginRequest { name: string; firstName: string; - passwords: { password: string }; // Frontend... + password: string; captcha: string; status: string; locale: string; diff --git a/src/utils/Constants.ts b/src/utils/Constants.ts index 1c414986fd..b2e38c7f10 100644 --- a/src/utils/Constants.ts +++ b/src/utils/Constants.ts @@ -293,6 +293,7 @@ export default class Constants { public static readonly REGEX_VALIDATION_LATITUDE = /^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)$/; public static readonly REGEX_VALIDATION_LONGITUDE = /^[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$/; + public static readonly REGEX_URL_PATTERN = /^(?:https?|wss?):\/\/((?:[\w-]+)(?:\.[\w-]+)*)(?:[\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?$/; public static readonly MAX_GPS_DISTANCE_METERS = 40000000; // Earth public static readonly EXCEPTION_JSON_KEYS_IN_SENSITIVE_DATA = Object.freeze([ diff --git a/src/utils/I18nManager.ts b/src/utils/I18nManager.ts index 91674b5926..d41060aef7 100644 --- a/src/utils/I18nManager.ts +++ b/src/utils/I18nManager.ts @@ -74,8 +74,11 @@ export default class I18nManager { return '0'; } - public formatDateTime(value: Date, format = 'LLL'): string { + public formatDateTime(value: Date, format = 'LLL', timezone: string = null): string { moment.locale(this.language); + if (timezone) { + return moment(new Date(value)).tz(timezone).format(format); + } return moment(new Date(value)).format(format); } } diff --git a/src/utils/Logging.ts b/src/utils/Logging.ts index 36f0b8c5ad..a801a062e1 100644 --- a/src/utils/Logging.ts +++ b/src/utils/Logging.ts @@ -57,7 +57,7 @@ export default class Logging { } const sizeOfDataKB = Utils.truncTo(sizeof(data) / 1024, 2); const numberOfRecords = Array.isArray(data) ? data.length : 0; - const message = `${module}.${method} ${found ? '- ' + executionDurationMillis.toString() + 'ms' : ''} ${!Utils.isEmptyJSon(data) ? '- ' + sizeOfDataKB.toString() + 'kB' : ''} ${Array.isArray(data) ? '- ' + numberOfRecords.toString() + 'rec' : ''}`; + const message = `${module}.${method} ${found ? '- ' + executionDurationMillis.toString() + 'ms' : ''} ${!Utils.isEmptyJSon(data) ? '- ' + sizeOfDataKB.toString() + 'kB' : ''} ${Array.isArray(data) ? '- ' + numberOfRecords.toString() + ' rec(s)' : ''}`; Utils.isDevelopmentEnv() && console.debug(chalk.green(message)); if (sizeOfDataKB > Constants.PERF_MAX_DATA_VOLUME_KB) { const error = new Error(`Data must be < ${Constants.PERF_MAX_DATA_VOLUME_KB}kB, got ${sizeOfDataKB}kB`); @@ -78,7 +78,7 @@ export default class Logging { } } if (executionDurationMillis > Constants.PERF_MAX_RESPONSE_TIME_MILLIS) { - const error = new Error(`Execution must be < ${Constants.PERF_MAX_RESPONSE_TIME_MILLIS}ms, got ${executionDurationMillis}ms`); + const error = new Error(`Execution must be < ${Constants.PERF_MAX_RESPONSE_TIME_MILLIS} ms, got ${executionDurationMillis} ms`); await Logging.logWarning({ tenantID, source: Constants.CENTRAL_SERVER, @@ -315,10 +315,10 @@ export default class Logging { if (res.getHeader('content-length')) { sizeOfDataKB = Utils.truncTo(res.getHeader('content-length') as number / 1024, 2); } - const message = `Express HTTP Response - ${(executionDurationMillis > 0) ? executionDurationMillis : '?'}ms - ${(sizeOfDataKB > 0) ? sizeOfDataKB : '?'}kB >> ${req.method}/${res.statusCode} '${req.url}'`; + const message = `Express HTTP Response - ${(executionDurationMillis > 0) ? executionDurationMillis : '?'} ms - ${(sizeOfDataKB > 0) ? sizeOfDataKB : '?'} kB >> ${req.method}/${res.statusCode} '${req.url}'`; Utils.isDevelopmentEnv() && console.debug(chalk.green(message)); if (sizeOfDataKB > Constants.PERF_MAX_DATA_VOLUME_KB) { - const error = new Error(`Data must be < ${Constants.PERF_MAX_DATA_VOLUME_KB}kB, got ${(sizeOfDataKB > 0) ? sizeOfDataKB : '?'}kB`); + const error = new Error(`Data must be < ${Constants.PERF_MAX_DATA_VOLUME_KB} kB, got ${(sizeOfDataKB > 0) ? sizeOfDataKB : '?'} kB`); await Logging.logWarning({ tenantID, source: Constants.CENTRAL_SERVER, @@ -336,7 +336,7 @@ export default class Logging { } } if (executionDurationMillis > Constants.PERF_MAX_RESPONSE_TIME_MILLIS) { - const error = new Error(`Execution must be < ${Constants.PERF_MAX_RESPONSE_TIME_MILLIS}ms, got ${(executionDurationMillis > 0) ? executionDurationMillis : '?'}ms`); + const error = new Error(`Execution must be < ${Constants.PERF_MAX_RESPONSE_TIME_MILLIS} ms, got ${(executionDurationMillis > 0) ? executionDurationMillis : '?'} ms`); await Logging.logWarning({ tenantID, source: Constants.CENTRAL_SERVER, @@ -415,7 +415,7 @@ export default class Logging { if (response.config.headers['Content-Length']) { sizeOfDataKB = Utils.truncTo(response.config.headers['Content-Length'] / 1024, 2); } - const message = `Axios HTTP Response - ${(executionDurationMillis > 0) ? executionDurationMillis : '?'}ms - ${(sizeOfDataKB > 0) ? sizeOfDataKB : '?'}kB << ${response.config.method.toLocaleUpperCase()}/${response.status} '${response.config.url}'`; + const message = `Axios HTTP Response - ${(executionDurationMillis > 0) ? executionDurationMillis : '?'} ms - ${(sizeOfDataKB > 0) ? sizeOfDataKB : '?'} kB << ${response.config.method.toLocaleUpperCase()}/${response.status} '${response.config.url}'`; Utils.isDevelopmentEnv() && console.log(chalk.green(message)); if (sizeOfDataKB > Constants.PERF_MAX_DATA_VOLUME_KB) { const error = new Error(`Data must be < ${Constants.PERF_MAX_DATA_VOLUME_KB}`); @@ -436,7 +436,7 @@ export default class Logging { } } if (executionDurationMillis > Constants.PERF_MAX_RESPONSE_TIME_MILLIS) { - const error = new Error(`Execution must be < ${Constants.PERF_MAX_RESPONSE_TIME_MILLIS}ms, got ${(executionDurationMillis > 0) ? executionDurationMillis : '?'}ms`); + const error = new Error(`Execution must be < ${Constants.PERF_MAX_RESPONSE_TIME_MILLIS} ms, got ${(executionDurationMillis > 0) ? executionDurationMillis : '?'} ms`); await Logging.logWarning({ tenantID, source: Constants.CENTRAL_SERVER, @@ -487,7 +487,7 @@ export default class Logging { await Logging.logSecurityDebug({ tenantID: tenantID, action: ServerAction.HTTP_RESPONSE, - message: `Axios HTTP Response - ${(executionDurationMillis > 0) ? executionDurationMillis : '?'}ms - ${(sizeOfDataKB > 0) ? sizeOfDataKB : '?'}kB << ${response.config.method.toLocaleUpperCase()}/${response.status} '${response.config.url}'`, + message: `Axios HTTP Response - ${(executionDurationMillis > 0) ? executionDurationMillis : '?'} ms - ${(sizeOfDataKB > 0) ? sizeOfDataKB : '?'} kB << ${response.config.method.toLocaleUpperCase()}/${response.status} '${response.config.url}'`, module: MODULE_NAME, method: 'interceptor', detailedMessages: { status: response.status, @@ -934,7 +934,7 @@ export default class Logging { const message = `${direction} OCPP Request '${action}' on '${chargingStationID}' has been processed ${found ? 'in ' + executionDurationMillis.toString() + 'ms' : ''}`; Utils.isDevelopmentEnv() && console.debug(chalk.green(message)); if (executionDurationMillis > Constants.PERF_MAX_RESPONSE_TIME_MILLIS) { - const error = new Error(`Execution must be < ${Constants.PERF_MAX_RESPONSE_TIME_MILLIS}ms, got ${executionDurationMillis}ms`); + const error = new Error(`Execution must be < ${Constants.PERF_MAX_RESPONSE_TIME_MILLIS} ms, got ${executionDurationMillis} ms`); await Logging.logWarning({ tenantID, source: Constants.CENTRAL_SERVER, diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 1f8019a1a9..c0e62e3ed2 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -16,7 +16,6 @@ import Constants from './Constants'; import Cypher from './Cypher'; import { Decimal } from 'decimal.js'; import Logging from './Logging'; -import { ObjectID } from 'mongodb'; import QRCode from 'qrcode'; import { Request } from 'express'; import { ServerAction } from '../types/Server'; @@ -453,16 +452,6 @@ export default class Utils { return userToken.activeComponents.includes(componentName); } - public static convertToObjectID(id: any): ObjectID { - let changedID: ObjectID = id; - // Check - if (typeof id === 'string') { - // Create Object - changedID = new ObjectID(id); - } - return changedID; - } - public static convertToInt(value: any): number { let changedValue: number = value; if (!value) { @@ -497,25 +486,6 @@ export default class Utils { return Utils.createDecimal(pricePerkWh).mul(Utils.convertToFloat(consumptionWh)).div(1000).toNumber(); } - public static convertUserToObjectID(user: User | UserToken | string): ObjectID | null { - let userID: ObjectID | null = null; - // Check Created By - if (user) { - // Check User Model - if (typeof user === 'object' && - user.constructor.name !== 'ObjectID') { - // This is the User Model - userID = Utils.convertToObjectID(user.id); - } - // Check String - if (typeof user === 'string') { - // This is a String - userID = Utils.convertToObjectID(user); - } - } - return userID; - } - public static convertAmpToWatt(chargingStation: ChargingStation, chargePoint: ChargePoint, connectorID = 0, ampValue: number): number { const voltage = Utils.getChargingStationVoltage(chargingStation, chargePoint, connectorID); if (voltage) { diff --git a/test/api/AuthenticationTest.ts b/test/api/AuthenticationTest.ts index 831b4fddc9..bb9b211804 100644 --- a/test/api/AuthenticationTest.ts +++ b/test/api/AuthenticationTest.ts @@ -161,7 +161,7 @@ describe('Authentication Service (utall)', function() { // testData.createdUsersAdminTenant.push(newUser); // const userAPI = new CentralServerService(testData.adminTenant, { // email: newUser.email, - // password: newUser.passwords.password + // password: newUser.password // }); // let validResponse = await userAPI.userApi.readById(newUser.id); // // Check @@ -191,7 +191,7 @@ describe('Authentication Service (utall)', function() { // testData.createdUsersAdminTenant.push(newUser); // const userAPI = new CentralServerService(testData.adminTenant, { // email: newUser.email, - // password: newUser.passwords.password + // password: newUser.password // }); // let validResponse = await userAPI.userApi.readById(newUser.id); // // Check @@ -234,17 +234,17 @@ describe('Authentication Service (utall)', function() { it('Should not allow registration without password', async () => { // Call const newUser = UserFactory.buildRegisterUser(); - delete newUser.passwords; + delete newUser.password; const response = await CentralServerService.defaultInstance.authenticationApi.registerUser(newUser, testData.adminTenant); // Check expect(response.status).to.be.eql(StatusCodes.INTERNAL_SERVER_ERROR); expect(response.data).to.not.have.property('token'); }); - it('Should not allow registration with empty strings passwords', async () => { + it('Should not allow registration with empty string password', async () => { // Call const newUser = UserFactory.buildRegisterUser(); - newUser.passwords = { password: '', repeatPassword: '' }; + newUser.password = ''; const response = await CentralServerService.defaultInstance.authenticationApi.registerUser(newUser, testData.adminTenant); // Check expect(response.status).to.be.eql(StatusCodes.INTERNAL_SERVER_ERROR); @@ -254,7 +254,7 @@ describe('Authentication Service (utall)', function() { it('Should not allow registration with weak password', async () => { // Call const newUser = UserFactory.buildRegisterUser(); - newUser.passwords = { password: '1234', repeatPassword: '1234' }; + newUser.password = '1234'; const response = await CentralServerService.defaultInstance.authenticationApi.registerUser(newUser, testData.adminTenant); // Check expect(response.status).to.be.eql(StatusCodes.INTERNAL_SERVER_ERROR); @@ -269,14 +269,6 @@ describe('Authentication Service (utall)', function() { expect(response.data).to.not.have.property('token'); }); - it('Should not allow authentication with a password that doesn\'t match requirements', async () => { - // Call - const response = await CentralServerService.defaultInstance.authenticationApi.login(testData.adminEmail, '1234', true); - // Check - expect(response.status).to.be.eql(StatusCodes.INTERNAL_SERVER_ERROR); - expect(response.data).to.not.have.property('token'); - }); - it('Should not allow authentication without password', async () => { // Call const response = await CentralServerService.defaultInstance.authenticationApi.login( diff --git a/test/api/SmartChargingTest.ts b/test/api/SmartChargingTest.ts index 9e726b39f9..5e0c371915 100644 --- a/test/api/SmartChargingTest.ts +++ b/test/api/SmartChargingTest.ts @@ -318,6 +318,15 @@ describe('Smart Charging Service', function() { expect(response.data).containSubset(Constants.REST_RESPONSE_SUCCESS); }); + it('Should not connect to Smart Charging Provider with invalid URL', async () => { + const sapSmartChargingSettings = TestData.getSmartChargingSettings(); + sapSmartChargingSettings.password = await Cypher.encrypt(testData.tenantContext.getTenant().id, sapSmartChargingSettings.password); + sapSmartChargingSettings.optimizerUrl = ''; + await TestData.saveSmartChargingSettings(testData, sapSmartChargingSettings); + const response = await testData.userService.smartChargingApi.testConnection({}); + expect(response.data.message).include('SAP Smart Charging service configuration is incorrect'); + }); + describe('Test for three phased site area', () => { before(() => { testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_BASIC); diff --git a/test/api/UserTest.ts b/test/api/UserTest.ts index 51d6de0a30..7c3d30510e 100644 --- a/test/api/UserTest.ts +++ b/test/api/UserTest.ts @@ -130,8 +130,7 @@ describe('User', function() { Factory.user.build() ); testData.newUser.issuer = true; - // Remove Passwords - delete testData.newUser['passwords']; + delete testData.newUser['password']; testData.createdUsers.push(testData.newUser); }); @@ -166,7 +165,7 @@ describe('User', function() { // Update await testData.userService.updateEntity( testData.userService.userApi, - { ...testData.newUser, passwords: { password: testData.newUser.password } } + { ...testData.newUser, password: testData.newUser.password } ); }); diff --git a/test/api/client/UserApi.ts b/test/api/client/UserApi.ts index 30b0b7090f..aca9bb6b8a 100644 --- a/test/api/client/UserApi.ts +++ b/test/api/client/UserApi.ts @@ -1,9 +1,10 @@ +import AuthenticatedBaseApi from './utils/AuthenticatedBaseApi'; import CrudApi from './utils/CrudApi'; import { ServerRoute } from '../../../src/types/Server'; import TestConstants from './utils/TestConstants'; export default class UserApi extends CrudApi { - public constructor(authenticatedApi) { + public constructor(authenticatedApi: AuthenticatedBaseApi) { super(authenticatedApi); } @@ -40,27 +41,27 @@ export default class UserApi extends CrudApi { } public async readTags(params, paging = TestConstants.DEFAULT_PAGING, ordering = TestConstants.DEFAULT_ORDERING) { - return super.readAll(params, paging, ordering, '/client/api/Tags'); + return super.readAll(params, paging, ordering, this.buildRestEndpointUrl(ServerRoute.REST_TAGS)); } public async readTag(id) { - return super.read({ ID: id }, '/client/api/Tag'); + return super.read({ ID: id }, this.buildRestEndpointUrl(ServerRoute.REST_TAG, { id })); } public async updateTag(data) { - return super.update(data, '/client/api/TagUpdate'); + return super.update(data, this.buildRestEndpointUrl(ServerRoute.REST_TAG, { id: data.id })); } public async createTag(data) { - return super.create(data, '/client/api/TagCreate'); + return super.create(data, this.buildRestEndpointUrl(ServerRoute.REST_TAGS)); } public async deleteTag(id) { - return super.delete(id, '/client/api/TagDelete'); + return super.delete(id, this.buildRestEndpointUrl(ServerRoute.REST_TAG, { id })); } public async exportTags(params) { - return await super.read(params, '/client/api/TagsExport'); + return await super.read(params, this.buildRestEndpointUrl(ServerRoute.REST_TAG_EXPORT)); } public async exportUsers(params) { diff --git a/test/api/client/utils/CrudApi.ts b/test/api/client/utils/CrudApi.ts index cbe1e80ad9..af96006173 100644 --- a/test/api/client/utils/CrudApi.ts +++ b/test/api/client/utils/CrudApi.ts @@ -1,5 +1,4 @@ import AuthenticatedBaseApi from './AuthenticatedBaseApi'; -import SafeUrlAssembler from 'safe-url-assembler'; import { ServerRoute } from '../../../../src/types/Server'; import TestConstants from './TestConstants'; @@ -135,14 +134,14 @@ export default class CrudApi { } // Build URL targeting REST endpoints - protected buildRestEndpointUrl(urlPatternAsString: ServerRoute, params: { - // Just a flat list of key/value pairs! - [name: string]: string | number | null; - } = {}): string { - const url = SafeUrlAssembler('/v1/api/') - .template(urlPatternAsString) - .param(params); - return url.toString(); + protected buildRestEndpointUrl(urlPatternAsString: ServerRoute, params: { [name: string]: string | number | null } = {}): string { + let resolvedUrlPattern = urlPatternAsString as string; + for (const key in params) { + if (Object.prototype.hasOwnProperty.call(params, key)) { + resolvedUrlPattern = resolvedUrlPattern.replace(`:${key}`, encodeURIComponent(params[key])); + } + } + return '/v1/api/' + resolvedUrlPattern; } // Build the paging in the Queryparam diff --git a/test/api/context/ContextBuilder.ts b/test/api/context/ContextBuilder.ts index 369386a79a..bf6bb75598 100644 --- a/test/api/context/ContextBuilder.ts +++ b/test/api/context/ContextBuilder.ts @@ -287,8 +287,8 @@ export default class ContextBuilder { siteTemplate.id = siteContextDef.id; siteTemplate.issuer = true; site = siteTemplate; - site.id = await SiteStorage.saveSite(buildTenant.id, siteTemplate, true); - await SiteStorage.addUsersToSite(buildTenant.id, site.id, userListToAssign.map((user) => user.id)); + site.id = await SiteStorage.saveSite(buildTenant, siteTemplate, true); + await SiteStorage.addUsersToSite(buildTenant, site.id, userListToAssign.map((user) => user.id)); const siteContext = new SiteContext(site, newTenantContext); // Create site areas of current site for (const siteAreaDef of ContextDefinition.TENANT_SITEAREA_LIST.filter((siteArea) => siteArea.siteName === site.name)) { diff --git a/test/factories/UserFactory.ts b/test/factories/UserFactory.ts index 3e4ad708fb..6271146891 100644 --- a/test/factories/UserFactory.ts +++ b/test/factories/UserFactory.ts @@ -6,13 +6,7 @@ const userFactory = Factory.define('user') .attr('firstName', () => faker.name.firstName()) .attr('name', () => faker.name.lastName().toUpperCase()) .attr('email', () => faker.internet.email().toLowerCase()) - .attr('passwords', () => { - const password = faker.internet.password() + '@1Aa'; - return { - password: password, - repeatPassword: password - }; - }) + .attr('password', () => faker.internet.password() + '@1Aa') .attr('notifications', (): UserNotifications => ({ sendSessionStarted: true, sendOptimalChargeReached: true, @@ -47,13 +41,7 @@ const registerUserFactory = Factory.define('user') .attr('firstName', () => faker.name.firstName()) .attr('name', () => faker.name.lastName()) .attr('email', () => faker.internet.email()) - .attr('passwords', () => { - const password = faker.internet.password() + '@1Aa'; - return { - password: password, - repeatPassword: password - }; - }) + .attr('password', () => faker.internet.password() + '@1Aa') .attr('acceptEula', true) .attr('locale', 'en_US') .attr('captcha', '03AMGVjXiyflPJpUOJF-AW2YP9-uQZvbVKsnx2CaESTX7mr59laYB0KKn7QERpWk-cadi1e2D0oYyjClv6UcYJ3IrYI951f2uopiLQv8ykAKEz3TQ3ZWgYJQSvItSZ7cd8wSFl7EF9aVEIHJobWg4OljtmSf2YUyXFnma76ih089LfUe0uSQC8piAT6DJ5WVcNaR827jbJrzCtYSPFX8u_GSFM6jCQU0RdnFgTuFIst2hyZ_FfiKJSpG9pSF2avSie1R-y6PVJktxNHdDaTuN4PK-AucjKrHSO9A');