diff --git a/package-lock.json b/package-lock.json index bae1c36be9..fb53db51d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ev-server", - "version": "2.4.79", + "version": "2.4.80", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 67588f1912..26c4bcd23c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ev-server", - "version": "2.4.79", + "version": "2.4.80", "engines": { "node": "14.x.x", "npm": "6.x.x" diff --git a/src/assets/configs-aws b/src/assets/configs-aws index d60f6b08be..d6656e9fef 160000 --- a/src/assets/configs-aws +++ b/src/assets/configs-aws @@ -1 +1 @@ -Subproject commit d60f6b08be5421586c6bdb6ddfebd2b8def91bc5 +Subproject commit d6656e9fef1de04904e5044c1894e8c24ceddb16 diff --git a/src/assets/server/notification/email/de/user-create-password.json b/src/assets/server/notification/email/de/user-create-password.json new file mode 100644 index 0000000000..ed6d991520 --- /dev/null +++ b/src/assets/server/notification/email/de/user-create-password.json @@ -0,0 +1,37 @@ +{ + "subject": "New account created", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "New account created", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Hi <%- (user.firstName?user.firstName:user.name) %>,", + "", + "An account has just been created on your behalf in <%- subdomain %> organisation.", + "Click on the link bellow to create your password.", + "You will have to log in at least once to this organisation to be able to charge your car." + ], + "actions": [{ + "title": "Create your password", + "url": "<%- evseDashboardCreatePasswordURL %>" + }], + "afterActionLines": [ + "Best Regards,", + "EV Admins." + ], + "footer": { + } + } +} diff --git a/src/assets/server/notification/email/de/verification-email-user-import.json b/src/assets/server/notification/email/de/verification-email-user-import.json new file mode 100644 index 0000000000..f542791354 --- /dev/null +++ b/src/assets/server/notification/email/de/verification-email-user-import.json @@ -0,0 +1,38 @@ +{ + "subject": "New account created", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "New account created", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Hi <%- (user.firstName?user.firstName:user.name) %>,", + "", + "An account has just been created on your behalf in <%- tenantName %> organisation.", + "", + "Click on the link below to complete the activation." + ], + "actions": [{ + "title": "Activate your Account", + "url": "<%- evseDashboardVerifyEmailURL %>" + }], + "afterActionLines": [ + "Best Regards,", + "EV Admins." + ], + "footer": { + } + } +} + diff --git a/src/assets/server/notification/email/en/user-create-password.json b/src/assets/server/notification/email/en/user-create-password.json new file mode 100644 index 0000000000..40e460d82d --- /dev/null +++ b/src/assets/server/notification/email/en/user-create-password.json @@ -0,0 +1,37 @@ +{ + "subject": "New account created", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "New account created", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Hi <%- (user.firstName?user.firstName:user.name) %>,", + "", + "An account has just been created on your behalf in <%- tenantName %> organisation.", + "Click on the link bellow to create your password.", + "You will have to log in at least once to this organisation to be able to charge your car." + ], + "actions": [{ + "title": "Create your password", + "url": "<%- evseDashboardCreatePasswordURL %>" + }], + "afterActionLines": [ + "Best Regards,", + "EV Admins." + ], + "footer": { + } + } +} diff --git a/src/assets/server/notification/email/en/verification-email-user-import.json b/src/assets/server/notification/email/en/verification-email-user-import.json new file mode 100644 index 0000000000..f542791354 --- /dev/null +++ b/src/assets/server/notification/email/en/verification-email-user-import.json @@ -0,0 +1,38 @@ +{ + "subject": "New account created", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "New account created", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Hi <%- (user.firstName?user.firstName:user.name) %>,", + "", + "An account has just been created on your behalf in <%- tenantName %> organisation.", + "", + "Click on the link below to complete the activation." + ], + "actions": [{ + "title": "Activate your Account", + "url": "<%- evseDashboardVerifyEmailURL %>" + }], + "afterActionLines": [ + "Best Regards,", + "EV Admins." + ], + "footer": { + } + } +} + diff --git a/src/assets/server/notification/email/es/user-create-password.json b/src/assets/server/notification/email/es/user-create-password.json new file mode 100644 index 0000000000..ed6d991520 --- /dev/null +++ b/src/assets/server/notification/email/es/user-create-password.json @@ -0,0 +1,37 @@ +{ + "subject": "New account created", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "New account created", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Hi <%- (user.firstName?user.firstName:user.name) %>,", + "", + "An account has just been created on your behalf in <%- subdomain %> organisation.", + "Click on the link bellow to create your password.", + "You will have to log in at least once to this organisation to be able to charge your car." + ], + "actions": [{ + "title": "Create your password", + "url": "<%- evseDashboardCreatePasswordURL %>" + }], + "afterActionLines": [ + "Best Regards,", + "EV Admins." + ], + "footer": { + } + } +} diff --git a/src/assets/server/notification/email/es/verification-email-user-import.json b/src/assets/server/notification/email/es/verification-email-user-import.json new file mode 100644 index 0000000000..f542791354 --- /dev/null +++ b/src/assets/server/notification/email/es/verification-email-user-import.json @@ -0,0 +1,38 @@ +{ + "subject": "New account created", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "New account created", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Hi <%- (user.firstName?user.firstName:user.name) %>,", + "", + "An account has just been created on your behalf in <%- tenantName %> organisation.", + "", + "Click on the link below to complete the activation." + ], + "actions": [{ + "title": "Activate your Account", + "url": "<%- evseDashboardVerifyEmailURL %>" + }], + "afterActionLines": [ + "Best Regards,", + "EV Admins." + ], + "footer": { + } + } +} + diff --git a/src/assets/server/notification/email/fr/account-verification-notification.json b/src/assets/server/notification/email/fr/account-verification-notification.json index 5e411ac16b..3acee7bd7e 100644 --- a/src/assets/server/notification/email/fr/account-verification-notification.json +++ b/src/assets/server/notification/email/fr/account-verification-notification.json @@ -28,7 +28,7 @@ "url": "<%- evseDashboardURL %>" }], "afterActionLines": [ - "Best Regards,", + "Cordialement,", "EV Admins." ], "footer": { diff --git a/src/assets/server/notification/email/fr/admin-account-verification-notification.json b/src/assets/server/notification/email/fr/admin-account-verification-notification.json index cc2c93e5a9..09d5c4fdf3 100644 --- a/src/assets/server/notification/email/fr/admin-account-verification-notification.json +++ b/src/assets/server/notification/email/fr/admin-account-verification-notification.json @@ -28,7 +28,7 @@ "url": "<%- evseUserToVerifyURL %>" }], "afterActionLines": [ - "Best Regards,", + "Cordialement,", "EV Admins." ], "footer": { diff --git a/src/assets/server/notification/email/fr/compute-and-apply-charging-profiles-failed.json b/src/assets/server/notification/email/fr/compute-and-apply-charging-profiles-failed.json index c153e9cd99..f3b33530c2 100644 --- a/src/assets/server/notification/email/fr/compute-and-apply-charging-profiles-failed.json +++ b/src/assets/server/notification/email/fr/compute-and-apply-charging-profiles-failed.json @@ -28,7 +28,7 @@ "url": "<%- evseDashboardURL %>" }, "afterActionLines": [ - "Best Regards,", + "Cordialement,", "EV Admins." ], "footer": { diff --git a/src/assets/server/notification/email/fr/user-create-password.json b/src/assets/server/notification/email/fr/user-create-password.json new file mode 100644 index 0000000000..27fd831889 --- /dev/null +++ b/src/assets/server/notification/email/fr/user-create-password.json @@ -0,0 +1,37 @@ +{ + "subject": "Nouveau compte créé", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "Nouveau compte créé", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Bonjour <%- (user.firstName?user.firstName:user.name) %>,", + "", + "Un compte a été créé en votre nom sur l'organisation <%- subdomain %>.", + "Cliquez sur le lien ci-dessous pour créer un mot de passe.", + "Vous devrez vous connecter au moins une fois sur cette organisation pour pouvoir charger votre voiture." + ], + "actions": [{ + "title": "Créer votre mot de passe", + "url": "<%- evseDashboardCreatePasswordURL %>" + }], + "afterActionLines": [ + "Cordialement,", + "EV Admins." + ], + "footer": { + } + } +} diff --git a/src/assets/server/notification/email/fr/verification-email-user-import.json b/src/assets/server/notification/email/fr/verification-email-user-import.json new file mode 100644 index 0000000000..5525f38dc4 --- /dev/null +++ b/src/assets/server/notification/email/fr/verification-email-user-import.json @@ -0,0 +1,37 @@ +{ + "subject": "Nouveau compte créé", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "Nouveau compte créé", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Bonjour <%- (user.firstName?user.firstName:user.name) %>,", + "", + "Un compte a été créé en votre nom sur l'organisation <%- subdomain %>.", + "Cliquez sur le lien ci-dessous pour compléter l'activation." + ], + "actions": [{ + "title": "Activez votre Compte", + "url": "<%- evseDashboardVerifyEmailURL %>" + }], + "afterActionLines": [ + "Cordialement,", + "EV Admins." + ], + "footer": { + } + } +} + diff --git a/src/assets/server/notification/email/it/user-create-password.json b/src/assets/server/notification/email/it/user-create-password.json new file mode 100644 index 0000000000..ed6d991520 --- /dev/null +++ b/src/assets/server/notification/email/it/user-create-password.json @@ -0,0 +1,37 @@ +{ + "subject": "New account created", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "New account created", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Hi <%- (user.firstName?user.firstName:user.name) %>,", + "", + "An account has just been created on your behalf in <%- subdomain %> organisation.", + "Click on the link bellow to create your password.", + "You will have to log in at least once to this organisation to be able to charge your car." + ], + "actions": [{ + "title": "Create your password", + "url": "<%- evseDashboardCreatePasswordURL %>" + }], + "afterActionLines": [ + "Best Regards,", + "EV Admins." + ], + "footer": { + } + } +} diff --git a/src/assets/server/notification/email/it/verification-email-user-import.json b/src/assets/server/notification/email/it/verification-email-user-import.json new file mode 100644 index 0000000000..f542791354 --- /dev/null +++ b/src/assets/server/notification/email/it/verification-email-user-import.json @@ -0,0 +1,38 @@ +{ + "subject": "New account created", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "New account created", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Hi <%- (user.firstName?user.firstName:user.name) %>,", + "", + "An account has just been created on your behalf in <%- tenantName %> organisation.", + "", + "Click on the link below to complete the activation." + ], + "actions": [{ + "title": "Activate your Account", + "url": "<%- evseDashboardVerifyEmailURL %>" + }], + "afterActionLines": [ + "Best Regards,", + "EV Admins." + ], + "footer": { + } + } +} + diff --git a/src/assets/server/notification/email/pt/user-create-password.json b/src/assets/server/notification/email/pt/user-create-password.json new file mode 100644 index 0000000000..ed6d991520 --- /dev/null +++ b/src/assets/server/notification/email/pt/user-create-password.json @@ -0,0 +1,37 @@ +{ + "subject": "New account created", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "New account created", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Hi <%- (user.firstName?user.firstName:user.name) %>,", + "", + "An account has just been created on your behalf in <%- subdomain %> organisation.", + "Click on the link bellow to create your password.", + "You will have to log in at least once to this organisation to be able to charge your car." + ], + "actions": [{ + "title": "Create your password", + "url": "<%- evseDashboardCreatePasswordURL %>" + }], + "afterActionLines": [ + "Best Regards,", + "EV Admins." + ], + "footer": { + } + } +} diff --git a/src/assets/server/notification/email/pt/verification-email-user-import.json b/src/assets/server/notification/email/pt/verification-email-user-import.json new file mode 100644 index 0000000000..f542791354 --- /dev/null +++ b/src/assets/server/notification/email/pt/verification-email-user-import.json @@ -0,0 +1,38 @@ +{ + "subject": "New account created", + "tenant": "", + "baseURL": "<%- evseDashboardURL %>", + "body": { + "header": { + "title": "New account created", + "image": { + "left": { + "height": 60, + "url": "<%- tenantLogoURL %>" + }, + "right": { + "height": 50, + "url": "<%- evseDashboardURL %>/assets/img/info.png" + } + } + }, + "beforeActionLines": [ + "Hi <%- (user.firstName?user.firstName:user.name) %>,", + "", + "An account has just been created on your behalf in <%- tenantName %> organisation.", + "", + "Click on the link below to complete the activation." + ], + "actions": [{ + "title": "Activate your Account", + "url": "<%- evseDashboardVerifyEmailURL %>" + }], + "afterActionLines": [ + "Best Regards,", + "EV Admins." + ], + "footer": { + } + } +} + 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 75cb1523fc..a6dd62bd83 100644 --- a/src/assets/server/rest/v1/docs/e-mobility-oas.json +++ b/src/assets/server/rest/v1/docs/e-mobility-oas.json @@ -892,6 +892,57 @@ "$ref": "#/components/responses/BackendError" } } + }, + "delete": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Delete several Transactions", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "transactionIDs": { + "type": "array", + "items": { + "type": "number" + } + } + } + } + } + } + }, + "tags": [ + "Transactions" + ], + "responses": { + "200": { + "description": "Transaction deletion result", + "content": { + "application/json": { + "example": { + "status": "Success", + "inSuccess": 42, + "inError": 0 + } + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + } + } } }, "/api/transactions/{id}": { @@ -971,6 +1022,227 @@ "$ref": "#/components/responses/BackendError" } } + }, + "delete": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Delete a Transaction", + "parameters": [ + { + "$ref": "#/components/parameters/transactionID" + } + ], + "tags": [ + "Transactions" + ], + "responses": { + "200": { + "description": "Transaction deletion result", + "content": { + "application/json": { + "example": { + "status": "Success", + "inSuccess": 1, + "inError": 0 + } + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + } + } + } + }, + "/api/transactions/{id}/ocpi/cdr": { + "post": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Push Transaction CDR", + "parameters": [ + { + "$ref": "#/components/parameters/transactionID" + } + ], + "tags": [ + "Transactions" + ], + "responses": { + "200": { + "description": "Transaction CDR pushed successfully", + "content": { + "application/json": { + "example": { + "status": "Success" + } + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + }, + "580": { + "description": "The transaction belongs to an external organization" + }, + "581": { + "description": "The transaction has no OCPI or OICP session data" + }, + "582": { + "description": "The CDR of the transaction has already been pushed" + } + } + } + }, + "/api/transactions/{id}/ocpi/cdr/export": { + "get": { + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "$ref": "#/components/parameters/WithUser" + }, + { + "$ref": "#/components/parameters/WithCar" + }, + { + "$ref": "#/components/parameters/WithTag" + } + ], + "description": "Export Transaction CDR", + "tags": [ + "Transactions" + ], + "responses": { + "200": { + "description": "Exported Transaction CDR", + "content": { + "application/json": { + "example": { + "id": "###", + "start_date_time": "2020-06-30T14:08:50.000Z", + "stop_date_time": "2020-06-30T14:33:56.000Z", + "total_parking_time": 0, + "total_time": 1506, + "total_energy": 4.502, + "total_cost": 0.72, + "currency": "EUR", + "auth_id": "###", + "authorization_id": "###", + "auth_method": "AUTH_REQUEST", + "location": { + "id": "###", + "name": "###", + "address": "###", + "city": "###", + "postal_code": "###", + "country": "###", + "coordinates": { + "latitude": "###", + "longitude": "###" + }, + "type": "UNKNOWN", + "evses": [ + { + "uid": "###", + "evse_id": "###", + "status": "BLOCKED", + "capabilities": [ + "REMOTE_START_STOP_CAPABLE", + "RFID_READER" + ], + "connectors": [ + { + "id": "###", + "standard": "IEC_62196_T2", + "format": "SOCKET", + "voltage": 230, + "amperage": 96, + "power_type": "AC_3_PHASE", + "last_updated": "2020-06-30T14:08:47.621Z" + } + ], + "coordinates": { + "latitude": "###", + "longitude": "###" + }, + "last_updated": "2020-06-30T14:08:47.621Z" + } + ], + "last_updated": "2020-05-06T09:18:10.227Z" + }, + "charging_periods": [ + { + "start_date_time": "2020-06-30T14:08:50.000Z", + "dimensions": [ + { + "type": "ENERGY", + "volume": 0.108 + }, + { + "type": "MAX_CURRENT", + "volume": 96 + } + ] + }, + { + "start_date_time": "2020-06-30T14:10:50.000Z", + "dimensions": [ + { + "type": "ENERGY", + "volume": 0.182 + }, + { + "type": "MAX_CURRENT", + "volume": 96 + } + ] + } + ], + "last_updated": "2020-06-30T14:33:56.000Z" + } + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + }, + "580": { + "description": "The transaction belongs to an external organization" + }, + "581": { + "description": "The transaction has no OCPI or OICP session data" + }, + "582": { + "description": "The CDR of the transaction has already been pushed" + } + } } }, "/api/transactions/{id}/consumptions": { @@ -1081,6 +1353,320 @@ } } }, + "/api/transactions/{id}/consumptions/rebuild": { + "post": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Rebuild a Transaction consumptions", + "parameters": [ + { + "$ref": "#/components/parameters/transactionID" + } + ], + "tags": [ + "Transactions" + ], + "responses": { + "200": { + "description": "Consumptions rebuild result", + "content": { + "application/json": { + "example": { + "status": "Success", + "nbrOfConsumptions": 1 + } + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + } + } + } + }, + "/api/transactions/{id}/stop/soft": { + "post": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Stop a Transaction", + "parameters": [ + { + "$ref": "#/components/parameters/transactionID" + } + ], + "tags": [ + "Transactions" + ], + "responses": { + "200": { + "description": "Transaction stopped successfully", + "content": { + "application/json": { + "example": { + "status": "Success" + } + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + } + } + } + }, + "/api/transactions/action/refund": { + "post": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Refund Transactions", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "transactionIds": { + "type": "array", + "items": { + "type": "number" + } + } + } + } + } + } + }, + "tags": [ + "Transactions" + ], + "responses": { + "200": { + "description": "Transactions refund result", + "content": { + "application/json": { + "example": { + "status": "Success", + "inSuccess": 42, + "inError": 1 + } + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + }, + "552": { + "description": "No Refund valid connection found" + } + } + } + }, + "/api/transactions/action/assign": { + "post": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Assign Transactions to a user", + "parameters": [ + { + "in": "query", + "name": "TagID", + "description": "Tag ID", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "UserID", + "description": "User ID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "tags": [ + "Transactions" + ], + "responses": { + "200": { + "description": "Transactions successfully assigned", + "content": { + "application/json": { + "example": { + "status": "Success" + } + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + } + } + } + }, + "/api/transactions/action/export": { + "get": { + "security": [ + { + "bearerAuth": [] + } + ], + "description": "Export Transactions", + "parameters": [ + { + "$ref": "#/components/parameters/Issuer" + }, + { + "$ref": "#/components/parameters/Search" + }, + { + "$ref": "#/components/parameters/StartDateTime" + }, + { + "$ref": "#/components/parameters/EndDateTime" + }, + { + "$ref": "#/components/parameters/WithChargingStation" + }, + { + "$ref": "#/components/parameters/ChargingStationIDs" + }, + { + "$ref": "#/components/parameters/ConnectorIDs" + }, + { + "$ref": "#/components/parameters/WithCar" + }, + { + "$ref": "#/components/parameters/WithUser" + }, + { + "$ref": "#/components/parameters/UserIDs" + }, + { + "$ref": "#/components/parameters/TagIDs" + }, + { + "$ref": "#/components/parameters/VisualTagIDs" + }, + { + "$ref": "#/components/parameters/SiteIDs" + }, + { + "$ref": "#/components/parameters/WithSite" + }, + { + "$ref": "#/components/parameters/SiteAreaIDs" + }, + { + "$ref": "#/components/parameters/WithSiteArea" + }, + { + "$ref": "#/components/parameters/WithCompany" + }, + { + "$ref": "#/components/parameters/InactivityStatuses" + }, + { + "$ref": "#/components/parameters/TransactionStatistics" + }, + { + "$ref": "#/components/parameters/RefundStatus" + }, + { + "in": "query", + "name": "MinimalPrice", + "description": "Transactions minimal price", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "ReportIDs", + "description": "Pipe separated report IDs", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/Limit" + }, + { + "$ref": "#/components/parameters/Skip" + }, + { + "$ref": "#/components/parameters/SortFields" + }, + { + "$ref": "#/components/parameters/OnlyRecordCount" + }, + { + "$ref": "#/components/parameters/ProjectFields" + } + ], + "tags": [ + "Transactions" + ], + "responses": { + "200": { + "description": "Transactions successfully exported", + "content": { + "text/csv": { + "example": "id,chargingStationID,connectorID,companyName,siteName,siteAreaName,userID,user,tagID,visualTagID,tagDescription,timezone,startDate,startTime,endDate,endTime,totalConsumptionkWh,totalDurationMins,totalInactivityMins,price,priceUnit\n1155141205,\"###\",1,,,,\"\",\"\",\"###\",\"###\",\"###\",\"Europe/Paris\",\"2021-07-07\",\"16:21:59\",\"2021-07-07\",\"16:22:27\",0,0.46,0.46,0,\"EUR\"" + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/BackendError" + } + } + } + }, "/api/charging-stations": { "get": { "security": [ 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 46de46266c..ab90e727a1 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 @@ -11,6 +11,32 @@ }, "visualID": { "$ref": "tag.json#/definitions/visualID" + }, + "siteIDs": { + "type": "string", + "sanitize": "mongo" + }, + "importedData": { + "type": "object", + "properties": { + "autoActivateUserAtImport": { + "type": "boolean", + "sanitize": "mongo" + }, + "autoActivateTagAtImport": { + "type": "boolean", + "sanitize": "mongo" + } + } + }, + "email": { + "$ref": "user.json#/definitions/email" + }, + "name": { + "$ref": "user.json#/definitions/name" + }, + "firstName": { + "$ref": "user.json#/definitions/firstName" } }, "required": [ diff --git a/src/assets/server/rest/v1/schemas/tag/tags-get.json b/src/assets/server/rest/v1/schemas/tag/tags-get.json index 2802ccef22..960d9e3757 100644 --- a/src/assets/server/rest/v1/schemas/tag/tags-get.json +++ b/src/assets/server/rest/v1/schemas/tag/tags-get.json @@ -30,6 +30,10 @@ }, "ProjectFields": { "$ref": "common.json#/definitions/projectFields" + }, + "WithUser": { + "type": "boolean", + "sanitize": "mongo" } }, "required": [] diff --git a/src/assets/server/rest/v1/schemas/user/imported-user-create-req.json b/src/assets/server/rest/v1/schemas/user/imported-user-create-req.json index 6e0ea2e02e..fb72cbc65c 100644 --- a/src/assets/server/rest/v1/schemas/user/imported-user-create-req.json +++ b/src/assets/server/rest/v1/schemas/user/imported-user-create-req.json @@ -38,6 +38,19 @@ }, "notifications": { "$ref": "user.json#/definitions/notifications" + }, + "siteIDs": { + "type": "string", + "sanitize": "mongo" + }, + "importedData": { + "type": "object", + "properties": { + "autoActivateUserAtImport": { + "type": "boolean", + "sanitize": "mongo" + } + } } }, "required": [ 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 f7b8d43adb..cd1ef76412 100644 --- a/src/assets/server/rest/v1/schemas/user/users-get.json +++ b/src/assets/server/rest/v1/schemas/user/users-get.json @@ -6,10 +6,6 @@ "type": "boolean", "sanitize": "mongo" }, - "WithTag": { - "type": "boolean", - "sanitize": "mongo" - }, "SiteID": { "$ref": "common.json#/definitions/ids" }, diff --git a/src/async-task/tasks/BillTransactionAsyncTask.ts b/src/async-task/tasks/BillTransactionAsyncTask.ts index aec207b944..b60665af00 100644 --- a/src/async-task/tasks/BillTransactionAsyncTask.ts +++ b/src/async-task/tasks/BillTransactionAsyncTask.ts @@ -25,7 +25,7 @@ export default class BillTransactionAsyncTask extends AbstractAsyncTask { const lock = await LockingHelper.acquireBillUserLock(tenant.id, userID); if (lock) { try { - const transaction = await TransactionStorage.getTransaction(tenant.id, Number(transactionID), { withUser: true, withChargingStation: true }); + const transaction = await TransactionStorage.getTransaction(tenant, Number(transactionID), { withUser: true, withChargingStation: true }); if (!transaction) { throw new Error(`Unknown Transaction ID '${this.asyncTask.parameters.transactionID}'`); } @@ -43,7 +43,7 @@ export default class BillTransactionAsyncTask extends AbstractAsyncTask { transaction.billingData.stop = billingDataStop; transaction.billingData.lastUpdate = new Date(); // Save - await TransactionStorage.saveTransactionBillingData(tenant.id, transaction.id, transaction.billingData); + await TransactionStorage.saveTransactionBillingData(tenant, transaction.id, transaction.billingData); } finally { // Release the lock await LockingManager.release(lock); diff --git a/src/async-task/tasks/ImportHelper.ts b/src/async-task/tasks/ImportHelper.ts new file mode 100644 index 0000000000..b252618951 --- /dev/null +++ b/src/async-task/tasks/ImportHelper.ts @@ -0,0 +1,181 @@ +import Tag, { ImportedTag } from '../../types/Tag'; +import User, { ImportedUser, UserRole, UserStatus } from '../../types/User'; + +import Constants from '../../utils/Constants'; +import Logging from '../../utils/Logging'; +import NotificationHandler from '../../notification/NotificationHandler'; +import { ServerAction } from '../../types/Server'; +import Site from '../../types/Site'; +import SiteStorage from '../../storage/mongodb/SiteStorage'; +import TagStorage from '../../storage/mongodb/TagStorage'; +import Tenant from '../../types/Tenant'; +import TenantComponents from '../../types/TenantComponents'; +import UserStorage from '../../storage/mongodb/UserStorage'; +import Utils from '../../utils/Utils'; + +const MODULE_NAME = 'ImportHelper'; + +export default class ImportHelper { + public async processImportedUser(tenant: Tenant, importedUser: ImportedUser, existingSites: Map): Promise { + // Get User + let user = await UserStorage.getUserByEmail(tenant, importedUser.email); + // If one found lets update it else create new User + if (user) { + await this.updateUser(tenant, user, importedUser); + } else { + user = await this.createUser(tenant, importedUser); + } + if (Utils.isTenantComponentActive(tenant, TenantComponents.ORGANIZATION) && importedUser.siteIDs) { + await this.processSiteAssignment(tenant, user, importedUser, existingSites); + } + return user; + } + + public async processImportedTag(tenant: Tenant, importedTag: ImportedTag, existingSites: Map): Promise { + // Get Tag + let tag = await TagStorage.getTag(tenant.id, importedTag.id, { withNbrTransactions: true }); + // Try to get Tag with Visual ID + if (!tag && importedTag.visualID) { + tag = await TagStorage.getTagByVisualID(tenant.id, importedTag.visualID, { withNbrTransactions: true }); + } + // If one found lets update it else create new tag + if (tag) { + this.updateTag(tag, importedTag); + } else { + tag = this.createTag(importedTag); + } + // Save user if any and get the ID to assign current tag + if (importedTag.email && importedTag.name && importedTag.firstName) { + // Check & Import the User + const user = await this.processImportedUser(tenant, importedTag as ImportedUser, existingSites); + // Assign + tag.userID = user.id; + // Make this Tag default + await TagStorage.clearDefaultUserTag(tenant.id, user.id); + tag.default = true; + } + // Save the new Tag + await TagStorage.saveTag(tenant.id, tag); + return tag; + } + + private updateTag(tag: Tag, importedTag: ImportedTag): void { + // Check tag is already in use + if (!tag.issuer) { + throw new Error('Tag is not local to the organization'); + } + if (tag.userID) { + throw new Error('Tag is already assigned to a user'); + } + if (tag.active) { + throw new Error('Tag is already active'); + } + if (tag.transactionsCount > 0) { + throw new Error(`Tag is already used in ${tag.transactionsCount} transaction(s)`); + } + if (tag.id !== importedTag.id) { + throw new Error('Tag Visual ID is already assigned to another tag'); + } + // Update + tag.visualID = importedTag.visualID; + tag.active = importedTag.importedData?.autoActivateTagAtImport; + tag.description = importedTag.description; + tag.importedData = importedTag.importedData; + } + + private createTag(importedTag: ImportedTag): Tag { + // New Tag + return { + id: importedTag.id, + visualID: importedTag.visualID, + description: importedTag.description, + issuer: true, + active: importedTag.importedData?.autoActivateTagAtImport, + createdBy: { id: importedTag.importedBy }, + createdOn: importedTag.importedOn, + importedData: importedTag.importedData + }; + } + + private async updateUser(tenant: Tenant, user: User, importedUser: ImportedUser): Promise { + // Check user is already in use + if (!user.issuer) { + throw new Error('User is not local to the organization'); + } + // Update it + user.name = importedUser.name; + user.firstName = importedUser.firstName; + await UserStorage.saveUser(tenant, user); + } + + private async createUser(tenant: Tenant, importedUser: ImportedUser): Promise { + // New User + const newUser = UserStorage.createNewUser() as User; + // Set + newUser.firstName = importedUser.firstName; + newUser.name = importedUser.name; + newUser.email = importedUser.email; + newUser.createdBy = { id: importedUser.importedBy }; + newUser.createdOn = importedUser.importedOn; + newUser.importedData = importedUser.importedData; + // Save the new User + newUser.id = await UserStorage.saveUser(tenant, newUser); + await UserStorage.saveUserRole(tenant, newUser.id, UserRole.BASIC); + await UserStorage.saveUserStatus(tenant, newUser.id, UserStatus.PENDING); + await this.sendNotifications(tenant, newUser); + return newUser; + } + + private async processSiteAssignment(tenant: Tenant, user: User, importedUser: ImportedUser, existingSites: Map): Promise { + // If we never got the sites from db -> construct array of existing sites + if (existingSites.size === 0) { + // Init Site collections + const sites = await SiteStorage.getSites(tenant, {}, Constants.DB_PARAMS_MAX_LIMIT, ['id', 'name']); + for (const site of sites.result) { + existingSites.set(site.id, site); + } + } + const importedSiteIDs = importedUser.siteIDs.split('|'); + for (const importedSiteID of importedSiteIDs) { + const existingSite = existingSites.get(importedSiteID); + if (existingSite) { + // Assign Site + await UserStorage.addSiteToUser(tenant, user.id, importedSiteID); + } else { + // Site does not exist + await Logging.logError({ + tenantID: tenant.id, + action: ServerAction.USERS_IMPORT, + module: MODULE_NAME, method: 'executeAsyncTask', + user, + message: `Site ID '${importedSiteID}' does not exist` + }); + } + } + } + + private async sendNotifications(tenant: Tenant, user: User): Promise { + // Handle sending email for reseting password if user auto activated + // Init Password info + const resetHash = Utils.generateUUID(); + await UserStorage.saveUserPassword(tenant, user.id, { passwordResetHash: resetHash }); + // Generate new verificationToken + const verificationToken = Utils.generateToken(user.email); + // Save User Verification Account + await UserStorage.saveUserAccountVerification(tenant, user.id, { verificationToken }); + // Build account verif email with reset password embeded + const evseDashboardVerifyEmailURL = Utils.buildEvseURL(tenant.subdomain) + + '/verify-email?VerificationToken=' + verificationToken + '&Email=' + user.email + '&ResetToken=' + resetHash; + // Send activate account link + await NotificationHandler.sendVerificationEmailUserImport( + tenant.id, + Utils.generateUUID(), + user, + { + 'tenantName': tenant.name, + 'user': user, + 'evseDashboardURL': Utils.buildEvseURL(tenant.subdomain), + 'evseDashboardVerifyEmailURL': evseDashboardVerifyEmailURL + }); + } +} diff --git a/src/async-task/tasks/TagsImportAsyncTask.ts b/src/async-task/tasks/TagsImportAsyncTask.ts index 436112aa6e..fed19db83a 100644 --- a/src/async-task/tasks/TagsImportAsyncTask.ts +++ b/src/async-task/tasks/TagsImportAsyncTask.ts @@ -1,19 +1,19 @@ import { ActionsResponse, ImportStatus } from '../../types/GlobalType'; -import Tag, { ImportedTag } from '../../types/Tag'; -import User, { UserRole, UserStatus } from '../../types/User'; import AbstractAsyncTask from '../AsyncTask'; import Constants from '../../utils/Constants'; import { DataResult } from '../../types/DataResult'; import DbParams from '../../types/database/DbParams'; +import ImportHelper from './ImportHelper'; +import { ImportedTag } from '../../types/Tag'; import LockingHelper from '../../locking/LockingHelper'; import LockingManager from '../../locking/LockingManager'; import Logging from '../../utils/Logging'; import { ServerAction } from '../../types/Server'; +import Site from '../../types/Site'; +import SiteStorage from '../../storage/mongodb/SiteStorage'; 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 = 'TagsImportAsyncTask'; @@ -21,9 +21,18 @@ const MODULE_NAME = 'TagsImportAsyncTask'; export default class TagsImportAsyncTask extends AbstractAsyncTask { protected async executeAsyncTask(): Promise { const importTagsLock = await LockingHelper.acquireImportTagsLock(this.asyncTask.tenantID); + const importHelper = new ImportHelper(); + const existingSites: Map = new Map(); if (importTagsLock) { const tenant = await TenantStorage.getTenant(this.asyncTask.tenantID); try { + // If we never got the sites from db -> construct array of existing sites + if (existingSites.size === 0) { + const sites = await SiteStorage.getSites(tenant, {}, Constants.DB_PARAMS_MAX_LIMIT, ['id', 'name']); + for (const site of sites.result) { + existingSites.set(site.id, site); + } + } const dbParams: DbParams = { limit: Constants.IMPORT_PAGE_SIZE, skip: 0 }; let importedTags: DataResult; const result: ActionsResponse = { @@ -44,69 +53,28 @@ export default class TagsImportAsyncTask extends AbstractAsyncTask { do { // Get the imported tags importedTags = await TagStorage.getImportedTags(tenant.id, { status: ImportStatus.READY }, dbParams); - let tagToSave: Tag; for (const importedTag of importedTags.result) { try { - // Existing tags - let foundTag = await TagStorage.getTag(tenant.id, importedTag.id, { withNbrTransactions: true }); - foundTag = foundTag ? foundTag : await TagStorage.getTagByVisualID(tenant.id, importedTag.visualID); - if (foundTag) { - // Check tag is already in use - if (!foundTag.issuer) { - throw new Error('Tag is not local to the organization'); - } - if (foundTag.userID) { - throw new Error('Tag is already assigned to an user'); - } - if (foundTag.active) { - throw new Error('Tag is already active'); - } - if (foundTag.transactionsCount > 0) { - throw new Error(`Tag is already used in ${foundTag.transactionsCount} transaction(s)`); - } - if (foundTag.id !== importedTag.id) { - throw new Error('Tag VisualID is already assigned to another tag'); - } - tagToSave = { ...foundTag, ...importedTag }; - } else { - // New Tag - tagToSave = { - id: importedTag.id, - visualID: importedTag.visualID, - description: importedTag.description, - issuer: true, - active: false, - createdBy: { id: importedTag.importedBy }, - createdOn: importedTag.importedOn, - }; - } - // Save user if any and get the ID to assign tag - if (importedTag.email && importedTag.name && importedTag.firstName) { - await this.assignTag(tenant, importedTag, tagToSave); - } - // Save the new Tag - await TagStorage.saveTag(tenant.id, tagToSave); + // Check & Import the Tag (+ User if present) + await importHelper.processImportedTag(tenant, importedTag, existingSites); // Remove the imported Tag await TagStorage.deleteImportedTag(tenant.id, importedTag.id); result.inSuccess++; } catch (error) { - // Update the imported Tag + // Mark the imported Tag faulty with the reason importedTag.status = ImportStatus.ERROR; importedTag.errorDescription = error.message; result.inError++; - // Update it await TagStorage.saveImportedTag(tenant.id, importedTag); - // Log await Logging.logError({ tenantID: tenant.id, action: ServerAction.TAGS_IMPORT, module: MODULE_NAME, method: 'processTenant', - message: `Error when importing Tag ID '${importedTag.id}': ${error.message}`, - detailedMessages: { tag: importedTag, error: error.stack } + message: `Cannot import Tag ID '${importedTag.id}': ${error.message}`, + detailedMessages: { importedTag, error: error.stack } }); } } - // Log if (!Utils.isEmptyArray(importedTags.result) && (result.inError + result.inSuccess) > 0) { const intermediateDurationSecs = Math.round((new Date().getTime() - startTime) / 1000); await Logging.logDebug({ @@ -134,27 +102,4 @@ export default class TagsImportAsyncTask extends AbstractAsyncTask { } } } - - private async assignTag(tenant: Tenant, importedTag: ImportedTag, tag: Tag) { - // Save user if any and get the ID to assign tag - const foundUser = await UserStorage.getUserByEmail(tenant.id, importedTag.email); - if (!foundUser) { - const user = { - name: importedTag.name, - firstName: importedTag.firstName, - email: importedTag.email, - createdBy: { id: importedTag.importedBy }, - createdOn: Utils.convertToDate(importedTag.importedOn), - issuer: true, - status: UserStatus.PENDING, - role: UserRole.BASIC, - } as User; - const userID = await UserStorage.saveUser(tenant.id, user); - await UserStorage.saveUserStatus(tenant.id, userID, user.status); - await UserStorage.saveUserRole(tenant.id, userID, user.role); - tag.userID = userID; - } else { - tag.userID = foundUser.id; - } - } } diff --git a/src/async-task/tasks/UsersImportAsyncTask.ts b/src/async-task/tasks/UsersImportAsyncTask.ts index 761f83603e..e2bde7c551 100644 --- a/src/async-task/tasks/UsersImportAsyncTask.ts +++ b/src/async-task/tasks/UsersImportAsyncTask.ts @@ -1,14 +1,17 @@ import { ActionsResponse, ImportStatus } from '../../types/GlobalType'; -import User, { ImportedUser, UserRole, UserStatus } from '../../types/User'; import AbstractAsyncTask from '../AsyncTask'; import Constants from '../../utils/Constants'; import { DataResult } from '../../types/DataResult'; import DbParams from '../../types/database/DbParams'; +import ImportHelper from './ImportHelper'; +import { ImportedUser } from '../../types/User'; import LockingHelper from '../../locking/LockingHelper'; import LockingManager from '../../locking/LockingManager'; import Logging from '../../utils/Logging'; import { ServerAction } from '../../types/Server'; +import Site from '../../types/Site'; +import SiteStorage from '../../storage/mongodb/SiteStorage'; import TenantStorage from '../../storage/mongodb/TenantStorage'; import UserStorage from '../../storage/mongodb/UserStorage'; import Utils from '../../utils/Utils'; @@ -18,9 +21,17 @@ const MODULE_NAME = 'UsersImportAsyncTask'; export default class UsersImportAsyncTask extends AbstractAsyncTask { protected async executeAsyncTask(): Promise { const importUsersLock = await LockingHelper.acquireImportUsersLock(this.asyncTask.tenantID); + const importHelper = new ImportHelper(); + const existingSites: Map = new Map(); if (importUsersLock) { const tenant = await TenantStorage.getTenant(this.asyncTask.tenantID); try { + if (existingSites.size === 0) { + const sites = await SiteStorage.getSites(tenant, {}, Constants.DB_PARAMS_MAX_LIMIT, ['id', 'name']); + for (const site of sites.result) { + existingSites.set(site.id, site); + } + } const dbParams: DbParams = { limit: Constants.IMPORT_PAGE_SIZE, skip: 0 }; let importedUsers: DataResult; const result: ActionsResponse = { @@ -29,7 +40,7 @@ export default class UsersImportAsyncTask extends AbstractAsyncTask { }; const startTime = new Date().getTime(); // Get total number of Users to import - const totalUsersToImport = await UserStorage.getImportedUsersCount(tenant.id); + const totalUsersToImport = await UserStorage.getImportedUsersCount(tenant); if (totalUsersToImport > 0) { await Logging.logInfo({ tenantID: tenant.id, @@ -40,62 +51,30 @@ export default class UsersImportAsyncTask extends AbstractAsyncTask { } do { // Get the imported users - importedUsers = await UserStorage.getImportedUsers(tenant.id, { status: ImportStatus.READY }, dbParams); + importedUsers = await UserStorage.getImportedUsers(tenant, { status: ImportStatus.READY }, dbParams); for (const importedUser of importedUsers.result) { try { - // Existing Users - const foundUser = await UserStorage.getUserByEmail(tenant.id, importedUser.email); - if (foundUser) { - // Check tag is already in use - if (!foundUser.issuer) { - throw new Error('User is not local to the organization'); - } - if (foundUser.status !== UserStatus.PENDING) { - throw new Error('User account is already in use'); - } - // Update it - foundUser.name = importedUser.name; - foundUser.firstName = importedUser.firstName; - await UserStorage.saveUser(tenant.id, foundUser); - // Remove the imported User - await UserStorage.deleteImportedUser(tenant.id, importedUser.id); - result.inSuccess++; - continue; - } - // New User - const newUser = UserStorage.createNewUser() as User; - // Set - newUser.firstName = importedUser.firstName; - newUser.name = importedUser.name; - newUser.email = importedUser.email; - newUser.createdBy = { id: importedUser.importedBy }; - newUser.createdOn = importedUser.importedOn; - // Save the new User - newUser.id = await UserStorage.saveUser(tenant.id, newUser); - // Role need to be set separately - await UserStorage.saveUserRole(tenant.id, newUser.id, UserRole.BASIC); - // Status need to be set separately - await UserStorage.saveUserStatus(tenant.id, newUser.id, UserStatus.PENDING); - // Remove the imported User - await UserStorage.deleteImportedUser(tenant.id, importedUser.id); + // Check & Import the User + await importHelper.processImportedUser(tenant, importedUser, existingSites); + // Remove the imported User either it's found or not + await UserStorage.deleteImportedUser(tenant, importedUser.id); result.inSuccess++; } catch (error) { + // Mark the imported User faulty with the reason importedUser.status = ImportStatus.ERROR; importedUser.errorDescription = error.message; result.inError++; // Update it - await UserStorage.saveImportedUser(tenant.id, importedUser); - // Log + await UserStorage.saveImportedUser(tenant, importedUser); await Logging.logError({ tenantID: tenant.id, action: ServerAction.USERS_IMPORT, - module: MODULE_NAME, method: 'processTenant', - message: `Error when importing User with email '${importedUser.email}': ${error.message}`, - detailedMessages: { user: importedUser, error: error.stack } + module: MODULE_NAME, method: 'executeAsyncTask', + message: `Cannot import User with email '${importedUser.email}': ${error.message}`, + detailedMessages: { importedUser, error: error.stack } }); } } - // Log if (importedUsers.result.length > 0 && (result.inError + result.inSuccess) > 0) { const intermediateDurationSecs = Math.round((new Date().getTime() - startTime) / 1000); await Logging.logDebug({ diff --git a/src/authorization/Authorizations.ts b/src/authorization/Authorizations.ts index f24b27cce5..636d03a036 100644 --- a/src/authorization/Authorizations.ts +++ b/src/authorization/Authorizations.ts @@ -125,12 +125,12 @@ export default class Authorizations { return requestedSites.filter((site) => sites.has(site)); } - public static async buildUserToken(tenantID: string, user: User, tags: Tag[]): Promise { + public static async buildUserToken(tenant: Tenant, user: User, tags: Tag[]): Promise { const siteIDs = []; const siteAdminIDs = []; const siteOwnerIDs = []; // Get User's site - const sites = (await UserStorage.getUserSites(tenantID, { userIDs: [user.id] }, + const sites = (await UserStorage.getUserSites(tenant, { userIDs: [user.id] }, Constants.DB_PARAMS_MAX_LIMIT)).result; for (const siteUser of sites) { if (!Authorizations.isAdmin(user)) { @@ -147,8 +147,7 @@ export default class Authorizations { let activeComponents = []; let tenantName; let tenantSubdomain; - if (tenantID !== Constants.DEFAULT_TENANT) { - const tenant = await TenantStorage.getTenant(tenantID); + if (tenant.id !== Constants.DEFAULT_TENANT) { tenantName = tenant.name; tenantSubdomain = tenant.subdomain; tenantHashID = SessionHashService.buildTenantHashID(tenant); @@ -156,7 +155,7 @@ export default class Authorizations { } // Currency let currency = null; - const pricing = await SettingStorage.getPricingSettings(tenantID); + const pricing = await SettingStorage.getPricingSettings(tenant.id); if (pricing && pricing.type === PricingSettingsType.SIMPLE) { currency = pricing.simple.currency; } @@ -174,7 +173,7 @@ export default class Authorizations { locale: user.locale, language: Utils.getLanguageFromLocale(user.locale), currency: currency, - tenantID: tenantID, + tenantID: tenant.id, tenantName: tenantName, tenantSubdomain: tenantSubdomain, userHashID: SessionHashService.buildUserHashID(user), @@ -208,7 +207,7 @@ export default class Authorizations { alternateUser = result.user; alternateTag = result.tag; // Get User and Tag that started the Transaction - user = await UserStorage.getUserByTagId(tenant.id, transaction.tagID); + user = await UserStorage.getUserByTagId(tenant, transaction.tagID); tag = await TagStorage.getTag(tenant.id, transaction.tagID); } else { // Check User @@ -751,7 +750,7 @@ export default class Authorizations { foundSiteArea = false; } else if (!chargingStation.siteArea) { chargingStation.siteArea = await SiteAreaStorage.getSiteArea( - tenant.id, chargingStation.siteAreaID, { withSite: true }); + tenant, chargingStation.siteAreaID, { withSite: true }); if (!chargingStation.siteArea) { foundSiteArea = false; } @@ -790,7 +789,7 @@ export default class Authorizations { // Access Control is disabled? if (!chargingStation.siteArea.accessControl) { // No ACL: Always try to get the user - const user = await UserStorage.getUserByTagId(tenant.id, tagID); + const user = await UserStorage.getUserByTagId(tenant, tagID); const tag = await TagStorage.getTag(tenant.id, tagID); return { user, tag }; } @@ -896,7 +895,7 @@ export default class Authorizations { for (const authorization of authorizations.result) { if (authorization.authorizationId) { // Check Existing Transaction with the same Auth ID - const ocpiTransaction = await TransactionStorage.getOCPITransactionByAuthorizationID(tenant.id, authorization.authorizationId); + const ocpiTransaction = await TransactionStorage.getOCPITransactionByAuthorizationID(tenant, authorization.authorizationId); // OCPI Auth ID not used yet if (!ocpiTransaction) { authorizationID = authorization.authorizationId; @@ -938,7 +937,7 @@ export default class Authorizations { // Update Remote Authorizations if (remoteAuthorizationsUpdated) { await ChargingStationStorage.saveChargingStationRemoteAuthorizations( - tenant.id, chargingStation.id, chargingStation.remoteAuthorizations); + tenant, chargingStation.id, chargingStation.remoteAuthorizations); } } return authorizationID; @@ -947,7 +946,7 @@ export default class Authorizations { private static async checkAndGetAuthorizedUserFromTag(action: ServerAction, tenant: Tenant, chargingStation: ChargingStation, transaction: Transaction, tag: Tag, authAction: Action): Promise { // Get User - const user = await UserStorage.getUser(tenant.id, tag.user.id); + const user = await UserStorage.getUser(tenant, tag.user.id); // User status if (user.status !== UserStatus.ACTIVE) { throw new BackendError({ @@ -962,7 +961,7 @@ export default class Authorizations { // Check Auth if local User if (user.issuer && authAction) { // Build the JWT Token - const userToken = await Authorizations.buildUserToken(tenant.id, user, [tag]); + const userToken = await Authorizations.buildUserToken(tenant, user, [tag]); // Authorized? const context: AuthorizationContext = { user: transaction ? transaction.userID : null, @@ -1025,7 +1024,7 @@ export default class Authorizations { if (Utils.isTenantComponentActive(tenant, TenantComponents.OICP)) { // Check if user has remote authorization or the session is already running if (tagID === OICPDefaultTagId.RemoteIdentification || transaction?.oicpData?.session?.id) { - return UserStorage.getUserByEmail(tenant.id, Constants.OICP_VIRTUAL_USER_EMAIL); + return UserStorage.getUserByEmail(tenant, Constants.OICP_VIRTUAL_USER_EMAIL); } // Get the client const oicpClient = await OICPClientFactory.getAvailableOicpClient(tenant, OICPRole.CPO) as CpoOICPClient; @@ -1039,7 +1038,7 @@ export default class Authorizations { // Check the Tag and retrieve the authorization const response = await oicpClient.authorizeStart(tagID); if (response?.AuthorizationStatus === OICPAuthorizationStatus.Authorized) { - const virtualOICPUser = await UserStorage.getUserByEmail(tenant.id, Constants.OICP_VIRTUAL_USER_EMAIL); + const virtualOICPUser = await UserStorage.getUserByEmail(tenant, Constants.OICP_VIRTUAL_USER_EMAIL); virtualOICPUser.authorizationID = response.SessionID; return virtualOICPUser; } diff --git a/src/authorization/dynamic-data-source/SitesAdminDynamicAuthorizationDataSource.ts b/src/authorization/dynamic-data-source/SitesAdminDynamicAuthorizationDataSource.ts index 86cc7586cd..7cc3af5610 100644 --- a/src/authorization/dynamic-data-source/SitesAdminDynamicAuthorizationDataSource.ts +++ b/src/authorization/dynamic-data-source/SitesAdminDynamicAuthorizationDataSource.ts @@ -21,7 +21,7 @@ export default class SitesAdminDynamicAuthorizationDataSource private async getSitesAdminSiteIDs(): Promise { // Get the Site IDs of the Sites for which the user is Site Admin - const sites = await UserStorage.getUserSites(this.tenant.id, + const sites = await UserStorage.getUserSites(this.tenant, { userIDs: [this.userToken.id], siteAdmin: true diff --git a/src/authorization/dynamic-data-source/SitesOwnerDynamicAuthorizationDataSource.ts b/src/authorization/dynamic-data-source/SitesOwnerDynamicAuthorizationDataSource.ts index 3e903ce5fa..085b0e683c 100644 --- a/src/authorization/dynamic-data-source/SitesOwnerDynamicAuthorizationDataSource.ts +++ b/src/authorization/dynamic-data-source/SitesOwnerDynamicAuthorizationDataSource.ts @@ -21,7 +21,7 @@ export default class SitesOwnerDynamicAuthorizationDataSource private async getSitesOwnerSiteIDs(): Promise { // Get the Site IDs of the Sites for which the user is Site Owner - const sites = await UserStorage.getUserSites(this.tenant.id, + const sites = await UserStorage.getUserSites(this.tenant, { userIDs: [this.userToken.id], siteOwner: true diff --git a/src/client/ocpi/CpoOCPIClient.ts b/src/client/ocpi/CpoOCPIClient.ts index a599a20d38..f3f41ce8d2 100644 --- a/src/client/ocpi/CpoOCPIClient.ts +++ b/src/client/ocpi/CpoOCPIClient.ts @@ -121,7 +121,7 @@ export default class CpoOCPIClient extends OCPIClient { let emspUser = emspUsers.get(email); if (!emspUser) { // Get User from DB - emspUser = await UserStorage.getUserByEmail(this.tenant.id, email); + emspUser = await UserStorage.getUserByEmail(this.tenant, email); if (emspUser) { emspUsers.set(email, emspUser); } @@ -488,7 +488,7 @@ export default class CpoOCPIClient extends OCPIClient { }; // Perfs trace const startTime = new Date().getTime(); - const transactions = await TransactionStorage.getTransactions(this.tenant.id, { + const transactions = await TransactionStorage.getTransactions(this.tenant, { issuer: true, ocpiSessionChecked: false }, Constants.DB_PARAMS_MAX_LIMIT); @@ -588,7 +588,7 @@ export default class CpoOCPIClient extends OCPIClient { }; // Perfs trace const startTime = new Date().getTime(); - const transactions = await TransactionStorage.getTransactions(this.tenant.id, { + const transactions = await TransactionStorage.getTransactions(this.tenant, { issuer: true, ocpiCdrChecked: false }, Constants.DB_PARAMS_MAX_LIMIT); @@ -813,7 +813,7 @@ export default class CpoOCPIClient extends OCPIClient { } // Mark it as done (checked at least once) transaction.ocpiData.cdrCheckedOn = new Date(); - await TransactionStorage.saveTransactionOcpiData(this.tenant.id, transaction.id, transaction.ocpiData); + await TransactionStorage.saveTransactionOcpiData(this.tenant, transaction.id, transaction.ocpiData); // Check CDR const cdrsUrl = this.getEndpointUrl('cdrs', ServerAction.OCPI_CHECK_CDRS); const response = await this.axiosInstance.get( @@ -880,7 +880,7 @@ export default class CpoOCPIClient extends OCPIClient { } // Mark it as done (checked at least once) transaction.ocpiData.sessionCheckedOn = new Date(); - await TransactionStorage.saveTransactionOcpiData(this.tenant.id, transaction.id, transaction.ocpiData); + await TransactionStorage.saveTransactionOcpiData(this.tenant, transaction.id, transaction.ocpiData); // Check Session const sessionsUrl = `${this.getEndpointUrl('sessions', ServerAction.OCPI_CHECK_SESSIONS)}/${this.getLocalCountryCode(ServerAction.OCPI_CHECK_SESSIONS)}/${this.getLocalPartyID(ServerAction.OCPI_CHECK_SESSIONS)}/${transaction.ocpiData.session.id}`; const response = await this.axiosInstance.get( diff --git a/src/client/ocpi/EmspOCPIClient.ts b/src/client/ocpi/EmspOCPIClient.ts index 665587892a..e7f5fabce9 100644 --- a/src/client/ocpi/EmspOCPIClient.ts +++ b/src/client/ocpi/EmspOCPIClient.ts @@ -345,7 +345,7 @@ export default class EmspOCPIClient extends OCPIClient { } const locationName = site.name + Constants.OCPI_SEPARATOR + location.id; // Handle Site Area - const siteAreas = await SiteAreaStorage.getSiteAreas(this.tenant.id, + const siteAreas = await SiteAreaStorage.getSiteAreas(this.tenant, { siteIDs: [site.id], name: locationName, issuer: false, withSite: true }, Constants.DB_PARAMS_SINGLE_RECORD); let siteArea = !Utils.isEmptyArray(siteAreas.result) ? siteAreas.result[0] : null; @@ -371,7 +371,7 @@ export default class EmspOCPIClient extends OCPIClient { Utils.convertToFloat(location.coordinates.latitude) ]; } - siteArea.id = await SiteAreaStorage.saveSiteArea(this.tenant.id, siteArea, false); + siteArea.id = await SiteAreaStorage.saveSiteArea(this.tenant, siteArea, false); } if (!Utils.isEmptyArray(location.evses)) { for (const evse of location.evses) { @@ -386,10 +386,10 @@ export default class EmspOCPIClient extends OCPIClient { if (evse.status === OCPIEvseStatus.REMOVED) { // Get existing charging station const currentChargingStation = await ChargingStationStorage.getChargingStationByOcpiLocationUid( - this.tenant.id, location.id, evse.uid, ['id'] + this.tenant, location.id, evse.uid, ['id'] ); if (currentChargingStation) { - await ChargingStationStorage.deleteChargingStation(this.tenant.id, currentChargingStation.id); + await ChargingStationStorage.deleteChargingStation(this.tenant, currentChargingStation.id); await Logging.logDebug({ tenantID: this.tenant.id, action: ServerAction.OCPI_PULL_LOCATIONS, @@ -405,8 +405,8 @@ export default class EmspOCPIClient extends OCPIClient { chargingStation.companyID = siteArea.site?.companyID; chargingStation.siteID = siteArea.siteID; chargingStation.siteAreaID = siteArea.id; - await ChargingStationStorage.saveChargingStation(this.tenant.id, chargingStation); - await ChargingStationStorage.saveChargingStationOcpiData(this.tenant.id, chargingStation.id, chargingStation.ocpiData); + await ChargingStationStorage.saveChargingStation(this.tenant, chargingStation); + await ChargingStationStorage.saveChargingStationOcpiData(this.tenant, chargingStation.id, chargingStation.ocpiData); await Logging.logDebug({ tenantID: this.tenant.id, action: ServerAction.OCPI_PULL_LOCATIONS, @@ -508,7 +508,7 @@ export default class EmspOCPIClient extends OCPIClient { const commandUrl = this.getEndpointUrl('commands', ServerAction.OCPI_START_SESSION) + '/' + OCPICommandType.STOP_SESSION; const callbackUrl = this.getLocalEndpointUrl('commands') + '/' + OCPICommandType.STOP_SESSION; // Get transaction - const transaction = await TransactionStorage.getTransaction(this.tenant.id, transactionId); + const transaction = await TransactionStorage.getTransaction(this.tenant, transactionId); if (!transaction) { throw new BackendError({ action: ServerAction.OCPI_START_SESSION, diff --git a/src/client/oicp/CpoOICPClient.ts b/src/client/oicp/CpoOICPClient.ts index 24eae04e1d..7f2ec79e3f 100644 --- a/src/client/oicp/CpoOICPClient.ts +++ b/src/client/oicp/CpoOICPClient.ts @@ -209,7 +209,7 @@ export default class CpoOICPClient extends OICPClient { let currentChargingStationSkip = 0; do { // Get all charging stations from tenant - chargingStations = (await ChargingStationStorage.getChargingStations(this.tenant.id, + chargingStations = (await ChargingStationStorage.getChargingStations(this.tenant, { siteIDs: [site.id], public: true, withSiteArea: true }, { skip: currentChargingStationSkip, limit: Constants.DB_RECORD_COUNT_DEFAULT })).result; if (!Utils.isEmptyArray(chargingStations)) { // Convert (public) charging stations to OICP EVSEs @@ -357,7 +357,7 @@ export default class CpoOICPClient extends OICPClient { let currentChargingStationSkip = 0; do { // Get all charging stations from tenant - chargingStations = (await ChargingStationStorage.getChargingStations(this.tenant.id, + chargingStations = (await ChargingStationStorage.getChargingStations(this.tenant, { siteIDs: [site.id], public: true, withSiteArea: true }, { skip: currentChargingStationSkip, limit: Constants.DB_RECORD_COUNT_DEFAULT })).result; if (!Utils.isEmptyArray(chargingStations)) { // Convert (public) charging stations to OICP EVSE Statuses diff --git a/src/integration/asset/lacroix/LacroixAssetIntegration.ts b/src/integration/asset/lacroix/LacroixAssetIntegration.ts index 171a9e627b..82845f470d 100644 --- a/src/integration/asset/lacroix/LacroixAssetIntegration.ts +++ b/src/integration/asset/lacroix/LacroixAssetIntegration.ts @@ -127,7 +127,7 @@ export default class LacroixAssetIntegration extends AssetIntegration { - const newUsers = await UserStorage.getUsers(this.tenant.id, + const newUsers = await UserStorage.getUsers(this.tenant, { statuses: [UserStatus.ACTIVE], notSynchronizedBillingData: true @@ -450,7 +450,7 @@ export default abstract class BillingIntegration { await Promise.all(billingInvoice.sessions.map(async (session) => { const transactionID = session.transactionID; try { - const transaction = await TransactionStorage.getTransaction(this.tenant.id, Number(transactionID)); + const transaction = await TransactionStorage.getTransaction(this.tenant, Number(transactionID)); // Update Billing Data const stop: BillingDataTransactionStop = { status: BillingStatus.UNBILLED, @@ -464,7 +464,7 @@ export default abstract class BillingIntegration { stop }; // Save to clear billing data - await TransactionStorage.saveTransactionBillingData(this.tenant.id, transaction.id, transaction.billingData); + await TransactionStorage.saveTransactionBillingData(this.tenant, transaction.id, transaction.billingData); } catch (error) { await Logging.logError({ tenantID: this.tenant.id, @@ -507,7 +507,7 @@ export default abstract class BillingIntegration { private async _getUsersWithTestBillingData(): Promise { // Get the users where billingData.liveMode is set to false - const users = await UserStorage.getUsers(this.tenant.id, + const users = await UserStorage.getUsers(this.tenant, { statuses: [UserStatus.ACTIVE], withTestBillingData: true @@ -529,7 +529,7 @@ export default abstract class BillingIntegration { }); } // Let's remove the billingData field - await UserStorage.saveUserBillingData(this.tenant.id, user.id, null); + await UserStorage.saveUserBillingData(this.tenant, user.id, null); } private isInvoiceOutOfPeriodicOperationScope(invoice: BillingInvoice): boolean { diff --git a/src/integration/billing/stripe/StripeBillingIntegration.ts b/src/integration/billing/stripe/StripeBillingIntegration.ts index 4c5ddf35a7..2aa846d144 100644 --- a/src/integration/billing/stripe/StripeBillingIntegration.ts +++ b/src/integration/billing/stripe/StripeBillingIntegration.ts @@ -185,7 +185,7 @@ export default class StripeBillingIntegration extends BillingIntegration { // Check Stripe await this.checkConnection(); // Make sure to get fresh data - user = await UserStorage.getUser(this.tenant.id, user.id); + user = await UserStorage.getUser(this.tenant, user.id); const customerID: string = user?.billingData?.customerID; // returns true when the customerID is properly set! return !!customerID; @@ -196,7 +196,7 @@ export default class StripeBillingIntegration extends BillingIntegration { await this.checkConnection(); // Make sure the billing data has been provided if (!user.billingData) { - user = await UserStorage.getUser(this.tenant.id, user.id); + user = await UserStorage.getUser(this.tenant, user.id); } // Retrieve the STRIPE customer (if any) const customerID: string = user.billingData?.customerID; @@ -322,7 +322,7 @@ export default class StripeBillingIntegration extends BillingIntegration { }); } else if (checkUserExists) { // Let's make sure the userID is still valid - const user = await UserStorage.getUser(this.tenant.id, userID); + const user = await UserStorage.getUser(this.tenant, userID); if (!user) { throw new BackendError({ message: `Unexpected situation - the e-Mobility user does not exist - ${userID}`, @@ -455,13 +455,13 @@ export default class StripeBillingIntegration extends BillingIntegration { await Promise.all(billingInvoice.sessions.map(async (session) => { const transactionID = session.transactionID; try { - const transaction = await TransactionStorage.getTransaction(this.tenant.id, Number(transactionID)); + const transaction = await TransactionStorage.getTransaction(this.tenant, Number(transactionID)); // Update Billing Data transaction.billingData.stop.invoiceStatus = billingInvoice.status; transaction.billingData.stop.invoiceNumber = billingInvoice.number; transaction.billingData.lastUpdate = new Date(); // Save - await TransactionStorage.saveTransactionBillingData(this.tenant.id, transaction.id, transaction.billingData); + await TransactionStorage.saveTransactionBillingData(this.tenant, transaction.id, transaction.billingData); } catch (error) { // Catch stripe errors and send the information back to the client await Logging.logError({ @@ -1378,7 +1378,7 @@ export default class StripeBillingIntegration extends BillingIntegration { }; // Save the billing data user.billingData = billingData; - await UserStorage.saveUserBillingData(this.tenant.id, user.id, user.billingData); + await UserStorage.saveUserBillingData(this.tenant, user.id, user.billingData); // Let's return the corresponding Billing User return this.convertToBillingUser(customer, user); } @@ -1405,7 +1405,7 @@ export default class StripeBillingIntegration extends BillingIntegration { customer = await this.stripe.customers.update(customerID, updateParams); // Let's update the Billing Data of our customer user.billingData.lastChangedOn = new Date(); - await UserStorage.saveUserBillingData(this.tenant.id, user.id, user.billingData); + await UserStorage.saveUserBillingData(this.tenant, user.id, user.billingData); // Let's return the corresponding Billing User return this.convertToBillingUser(customer, user); } diff --git a/src/integration/charging-station-vendor/ChargingStationVendorIntegration.ts b/src/integration/charging-station-vendor/ChargingStationVendorIntegration.ts index 1fa3609b3f..c53b9ba026 100644 --- a/src/integration/charging-station-vendor/ChargingStationVendorIntegration.ts +++ b/src/integration/charging-station-vendor/ChargingStationVendorIntegration.ts @@ -117,7 +117,7 @@ export default abstract class ChargingStationVendorIntegration { // Set connector.amperageLimit = limitAmpsPerConnector; } - await ChargingStationStorage.saveChargingStationConnectors(tenant.id, chargingStation.id, chargingStation.connectors); + await ChargingStationStorage.saveChargingStationConnectors(tenant, chargingStation.id, chargingStation.connectors); } return result; } @@ -146,7 +146,7 @@ export default abstract class ChargingStationVendorIntegration { } } // Save - await ChargingStationStorage.saveChargingStationConnectors(tenant.id, chargingStation.id, chargingStation.connectors); + await ChargingStationStorage.saveChargingStationConnectors(tenant, chargingStation.id, chargingStation.connectors); } } } @@ -392,7 +392,7 @@ export default abstract class ChargingStationVendorIntegration { // Check first matching Charging Profile if (chargingStation.capabilities?.supportChargingProfiles) { // Get the current Charging Profiles - const chargingProfiles = (await ChargingStationStorage.getChargingProfiles(tenant.id, { + const chargingProfiles = (await ChargingStationStorage.getChargingProfiles(tenant, { chargingStationIDs: [chargingStation.id] }, Constants.DB_PARAMS_MAX_LIMIT)).result; // Check the TX Charging Profiles from the DB diff --git a/src/integration/pricing/convergent-charging b/src/integration/pricing/convergent-charging index 9a8285a718..da274a9fc0 160000 --- a/src/integration/pricing/convergent-charging +++ b/src/integration/pricing/convergent-charging @@ -1 +1 @@ -Subproject commit 9a8285a71850f61cb97052fb40f86d4a68d7e78b +Subproject commit da274a9fc0589456a050e26fb745045dab4b461c diff --git a/src/integration/refund/RefundIntegration.ts b/src/integration/refund/RefundIntegration.ts index 59bfa947ef..7bd0cd6ae3 100644 --- a/src/integration/refund/RefundIntegration.ts +++ b/src/integration/refund/RefundIntegration.ts @@ -17,7 +17,7 @@ export default abstract class RefundIntegration { public abstract canBeDeleted(transaction: Transaction): boolean; - public abstract updateRefundStatus(id: string, transaction: Transaction): Promise; + public abstract updateRefundStatus(tenant: Tenant, transaction: Transaction): Promise; public abstract createConnection(userID: string, data: unknown): Promise; diff --git a/src/integration/refund/concur b/src/integration/refund/concur index f27ef94664..353a264524 160000 --- a/src/integration/refund/concur +++ b/src/integration/refund/concur @@ -1 +1 @@ -Subproject commit f27ef94664d3c4e883b36707b74f8d4a8e177229 +Subproject commit 353a264524fd4136c9b83592ed23bb37d85e206a diff --git a/src/integration/refund/dummy/DummyRefundIntegration.ts b/src/integration/refund/dummy/DummyRefundIntegration.ts index 1ad26a4e15..e17da60463 100644 --- a/src/integration/refund/dummy/DummyRefundIntegration.ts +++ b/src/integration/refund/dummy/DummyRefundIntegration.ts @@ -19,7 +19,7 @@ export default class DummyRefundIntegration extends RefundIntegration { + public async updateRefundStatus(tenant: Tenant, transaction: Transaction): Promise { return null; } diff --git a/src/integration/smart-charging/SmartChargingIntegration.ts b/src/integration/smart-charging/SmartChargingIntegration.ts index e691eacb84..b62ae01a39 100644 --- a/src/integration/smart-charging/SmartChargingIntegration.ts +++ b/src/integration/smart-charging/SmartChargingIntegration.ts @@ -142,7 +142,7 @@ export default abstract class SmartChargingIntegration 0) { return adminUsers.result; @@ -443,6 +443,31 @@ export default class NotificationHandler { } } + static async sendVerificationEmailUserImport(tenantID: string, notificationID: string, user: User, + sourceData: VerificationEmailNotification): Promise { + if (tenantID !== Constants.DEFAULT_TENANT) { + // Get the Tenant + const tenant = await TenantStorage.getTenant(tenantID, { withLogo: true }); + sourceData.tenantLogoURL = tenant.logo; + // For each Sources + for (const notificationSource of NotificationHandler.notificationSources) { + // Active? + if (notificationSource.enabled) { + try { + // Save + await NotificationHandler.saveNotification( + tenant, notificationSource.channel, notificationID, ServerAction.VERIFICATION_EMAIL_USER_IMPORT, { user }); + // Send + await notificationSource.notificationTask.sendVerificationEmailUserImport( + sourceData, user, tenant, NotificationSeverity.INFO); + } catch (error) { + await Logging.logActionExceptionMessage(tenantID, ServerAction.VERIFICATION_EMAIL_USER_IMPORT, error); + } + } + } + } + } + public static async sendChargingStationStatusError(tenant: Tenant, notificationID: string, chargingStation: ChargingStation, sourceData: ChargingStationStatusErrorNotification): Promise { if (tenant.id !== Constants.DEFAULT_TENANT) { diff --git a/src/notification/NotificationTask.ts b/src/notification/NotificationTask.ts index c4f9cf260a..61b7d5b8dd 100644 --- a/src/notification/NotificationTask.ts +++ b/src/notification/NotificationTask.ts @@ -1,4 +1,4 @@ -import { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, SmtpErrorNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, VerificationEmailNotification } from '../types/UserNotifications'; +import { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, SmtpErrorNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserCreatePassword, VerificationEmailNotification } from '../types/UserNotifications'; import Tenant from '../types/Tenant'; import User from '../types/User'; @@ -36,4 +36,6 @@ export default interface NotificationTask { sendBillingNewInvoiceOpen(data: BillingNewInvoiceNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise; sendAccountVerificationNotification(data: AccountVerificationNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise; sendAdminAccountVerificationNotification(data: AdminAccountVerificationNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise; + sendUserCreatePassword(data: UserCreatePassword, user: User, tenant: Tenant, severity: NotificationSeverity): Promise; + sendVerificationEmailUserImport(data: VerificationEmailNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise; } diff --git a/src/notification/email/EMailNotificationTask.ts b/src/notification/email/EMailNotificationTask.ts index 8f334c6c7a..51b4f6740e 100644 --- a/src/notification/email/EMailNotificationTask.ts +++ b/src/notification/email/EMailNotificationTask.ts @@ -1,4 +1,4 @@ -import { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EmailNotificationMessage, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, SmtpErrorNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, VerificationEmailNotification } from '../../types/UserNotifications'; +import { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EmailNotificationMessage, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, SmtpErrorNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserCreatePassword, VerificationEmailNotification } from '../../types/UserNotifications'; import { Message, SMTPClient, SMTPError } from 'emailjs'; import BackendError from '../../exception/BackendError'; @@ -97,6 +97,10 @@ export default class EMailNotificationTask implements NotificationTask { return this.prepareAndSendEmail('verification-email', data, user, tenant, severity); } + public async sendVerificationEmailUserImport(data: VerificationEmailNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise { + return this.prepareAndSendEmail('verification-email-user-import', data, user, tenant, severity); + } + public async sendSmtpError(data: SmtpErrorNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise { return this.prepareAndSendEmail('smtp-error', data, user, tenant, severity, true); } @@ -174,6 +178,10 @@ export default class EMailNotificationTask implements NotificationTask { return this.prepareAndSendEmail('admin-account-verification-notification', data, user, tenant, severity); } + public async sendUserCreatePassword(data: UserCreatePassword, user: User, tenant: Tenant, severity: NotificationSeverity): Promise { + return this.prepareAndSendEmail('user-create-password', data, user, tenant, severity); + } + private async sendEmail(email: EmailNotificationMessage, data: any, tenant: Tenant, user: User, severity: NotificationSeverity, useSmtpClientBackup = false): Promise { // Email configuration sanity checks if (!this.smtpMainClientInstance) { diff --git a/src/notification/remote-push-notification/RemotePushNotificationTask.ts b/src/notification/remote-push-notification/RemotePushNotificationTask.ts index acc3a70214..41253453a5 100644 --- a/src/notification/remote-push-notification/RemotePushNotificationTask.ts +++ b/src/notification/remote-push-notification/RemotePushNotificationTask.ts @@ -274,6 +274,11 @@ export default class RemotePushNotificationTask implements NotificationTask { return Promise.resolve(); } + public async sendVerificationEmailUserImport(data: VerificationEmailNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise { + // Nothing to send + return Promise.resolve(); + } + public async sendSmtpError(data: SmtpErrorNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise { // Set the locale const i18nManager = I18nManager.getInstanceForLocale(user.locale); @@ -434,6 +439,11 @@ export default class RemotePushNotificationTask implements NotificationTask { return Promise.resolve(); } + public async sendUserCreatePassword(): Promise { + // Nothing to send + return Promise.resolve(); + } + private async sendRemotePushNotificationToUser(tenant: Tenant, notificationType: UserNotificationType, title: string, body: string, user: User, data?: Record, severity?: NotificationSeverity): Promise { // Checks diff --git a/src/scheduler/tasks/CheckAndComputeSmartChargingTask.ts b/src/scheduler/tasks/CheckAndComputeSmartChargingTask.ts index d9693d23fd..9e2e4674f7 100644 --- a/src/scheduler/tasks/CheckAndComputeSmartChargingTask.ts +++ b/src/scheduler/tasks/CheckAndComputeSmartChargingTask.ts @@ -17,7 +17,7 @@ export default class CheckAndComputeSmartChargingTask extends SchedulerTask { if (Utils.isTenantComponentActive(tenant, TenantComponents.ORGANIZATION) && Utils.isTenantComponentActive(tenant, TenantComponents.SMART_CHARGING)) { // Get all site areas - const siteAreas = await SiteAreaStorage.getSiteAreas(tenant.id, + const siteAreas = await SiteAreaStorage.getSiteAreas(tenant, { smartCharging: true }, Constants.DB_PARAMS_MAX_LIMIT); // Get Site Area diff --git a/src/scheduler/tasks/CheckChargingStationTemplateTask.ts b/src/scheduler/tasks/CheckChargingStationTemplateTask.ts index b84b849474..0074ed0efd 100644 --- a/src/scheduler/tasks/CheckChargingStationTemplateTask.ts +++ b/src/scheduler/tasks/CheckChargingStationTemplateTask.ts @@ -50,7 +50,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask { return; } // Get the charging stations - const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, { + const chargingStations = await ChargingStationStorage.getChargingStations(tenant, { issuer: true, withSiteArea: true }, Constants.DB_PARAMS_MAX_LIMIT); // Update diff --git a/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts b/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts index ca56c7eb8e..bf26df0346 100644 --- a/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts +++ b/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts @@ -24,10 +24,10 @@ export default class CheckOfflineChargingStationsTask extends SchedulerTask { try { // Compute the date some minutes ago const offlineSince = moment().subtract(Configuration.getChargingStationConfig().maxLastSeenIntervalSecs, 'seconds').toDate(); - const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, { + const chargingStations = await ChargingStationStorage.getChargingStations(tenant, { issuer: true, withSiteArea: true, offlineSince }, Constants.DB_PARAMS_MAX_LIMIT); - if (chargingStations.count > 0) { + if (!Utils.isEmptyArray(chargingStations.result)) { for (let i = chargingStations.result.length - 1; i >= 0; i--) { const chargingStation = chargingStations.result[i]; let ocppHeartbeatConfiguration: OCPPGetConfigurationCommandResult; @@ -50,7 +50,7 @@ export default class CheckOfflineChargingStationsTask extends SchedulerTask { detailedMessages: { ocppHeartbeatConfiguration } }); // Update lastSeen - await ChargingStationStorage.saveChargingStationLastSeen(tenant.id, chargingStation.id, + await ChargingStationStorage.saveChargingStationLastSeen(tenant, chargingStation.id, { lastSeen: new Date() } ); // Remove charging station from notification @@ -63,7 +63,7 @@ export default class CheckOfflineChargingStationsTask extends SchedulerTask { } // Notify users with the rest of the Charging Stations if (chargingStations.result.length > 0) { - const chargingStationIDs: string = chargingStations.result.map((chargingStation) => chargingStation.id).join(', '); + const chargingStationIDs = chargingStations.result.map((chargingStation) => chargingStation.id).join(', '); // Send notification await NotificationHandler.sendOfflineChargingStations( tenant, { diff --git a/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts b/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts index 2ee13e9c3e..7852d9783e 100644 --- a/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts +++ b/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts @@ -21,7 +21,7 @@ export default class CheckPreparingSessionNotStartedTask extends SchedulerTask { if (await LockingManager.acquire(sessionNotStartedLock)) { try { // Get Charging Stations - const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, { + const chargingStations = await ChargingStationStorage.getChargingStations(tenant, { 'statusChangedBefore': moment().subtract(config.preparingStatusMaxMins, 'minutes').toDate(), 'connectorStatuses': [ChargePointStatus.PREPARING], withSiteArea: true diff --git a/src/scheduler/tasks/CheckSessionNotStartedAfterAuthorizeTask.ts b/src/scheduler/tasks/CheckSessionNotStartedAfterAuthorizeTask.ts index 0dee9e0463..7bd32368a1 100644 --- a/src/scheduler/tasks/CheckSessionNotStartedAfterAuthorizeTask.ts +++ b/src/scheduler/tasks/CheckSessionNotStartedAfterAuthorizeTask.ts @@ -17,7 +17,7 @@ export default class CheckSessionNotStartedAfterAuthorizeTask extends SchedulerT if (await LockingManager.acquire(sessionNotStartedLock)) { try { // Get notification - const notificationTransactionNotStarted = await TransactionStorage.getNotStartedTransactions(tenant.id, { + const notificationTransactionNotStarted = await TransactionStorage.getNotStartedTransactions(tenant, { 'checkPastAuthorizeMins': config.checkPastAuthorizeMins, 'sessionShouldBeStartedAfterMins': config.sessionShouldBeStartedAfterMins }); diff --git a/src/scheduler/tasks/CheckUserAccountInactivityTask.ts b/src/scheduler/tasks/CheckUserAccountInactivityTask.ts index 701489d8bc..2586ccc54f 100644 --- a/src/scheduler/tasks/CheckUserAccountInactivityTask.ts +++ b/src/scheduler/tasks/CheckUserAccountInactivityTask.ts @@ -26,7 +26,7 @@ export default class CheckUserAccountInactivityTask extends SchedulerTask { noLoginSince: someMonthsAgo }; // Get Users - const users = await UserStorage.getUsers(tenant.id, params, Constants.DB_PARAMS_MAX_LIMIT); + const users = await UserStorage.getUsers(tenant, params, Constants.DB_PARAMS_MAX_LIMIT); for (const user of users.result) { // Notification moment.locale(user.locale); diff --git a/src/scheduler/tasks/SynchronizeRefundTransactionsTask.ts b/src/scheduler/tasks/SynchronizeRefundTransactionsTask.ts index 4cf580f993..e4205517ac 100644 --- a/src/scheduler/tasks/SynchronizeRefundTransactionsTask.ts +++ b/src/scheduler/tasks/SynchronizeRefundTransactionsTask.ts @@ -41,7 +41,7 @@ export default class SynchronizeRefundTransactionsTask extends SchedulerTask { if (await LockingManager.acquire(refundLock)) { try { // Get the 'Submitted' transactions - const transactions = await TransactionStorage.getTransactions(tenant.id, + const transactions = await TransactionStorage.getTransactions(tenant, { 'refundStatus': [RefundStatus.SUBMITTED] }, { ...Constants.DB_PARAMS_MAX_LIMIT, sort: { 'userID': 1, 'refundData.reportId': 1 } }); // Check @@ -62,7 +62,7 @@ export default class SynchronizeRefundTransactionsTask extends SchedulerTask { for (const transaction of transactions.result) { try { // Update Transaction - const updatedAction = await refundConnector.updateRefundStatus(tenant.id, transaction); + const updatedAction = await refundConnector.updateRefundStatus(tenant, transaction); switch (updatedAction) { case RefundStatus.CANCELLED: actionsDone.cancelled++; diff --git a/src/scheduler/tasks/ocpi/OCPIPushCdrsTask.ts b/src/scheduler/tasks/ocpi/OCPIPushCdrsTask.ts index 700436bd01..2580b0b0c1 100644 --- a/src/scheduler/tasks/ocpi/OCPIPushCdrsTask.ts +++ b/src/scheduler/tasks/ocpi/OCPIPushCdrsTask.ts @@ -53,7 +53,7 @@ export default class OCPIPushCdrsTask extends SchedulerTask { if (ocpiTransactionLock) { try { // Get Transaction - const transaction = await TransactionStorage.getTransaction(tenant.id, transactionMDB._id, { withUser: true }); + const transaction = await TransactionStorage.getTransaction(tenant, transactionMDB._id, { withUser: true }); if (!transaction) { await Logging.logError({ tenantID: tenant.id, @@ -73,7 +73,7 @@ export default class OCPIPushCdrsTask extends SchedulerTask { continue; } // Get Charging Station - const chargingStation = await ChargingStationStorage.getChargingStation(tenant.id, transaction.chargeBoxID); + const chargingStation = await ChargingStationStorage.getChargingStation(tenant, transaction.chargeBoxID); if (!chargingStation) { await Logging.logError({ tenantID: tenant.id, @@ -97,7 +97,7 @@ export default class OCPIPushCdrsTask extends SchedulerTask { // Roaming await OCPPUtils.processTransactionRoaming(tenant, transaction, chargingStation, tag, TransactionAction.END); // Save - await TransactionStorage.saveTransactionOcpiData(tenant.id, transaction.id, transaction.ocpiData); + await TransactionStorage.saveTransactionOcpiData(tenant, transaction.id, transaction.ocpiData); // Ok await Logging.logInfo({ tenantID: tenant.id, 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 25a9c39f9d..3fefec9e45 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 @@ -430,7 +430,7 @@ export default class OCPIUtilsService { // Build evses array const evses: OCPIEvse[] = []; // Convert charging stations to evse(s) - const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, + const chargingStations = await ChargingStationStorage.getChargingStations(tenant, { ...dbFilters, siteIDs: [ siteID ], public: true, issuer: true, withSiteArea: true }, dbParams ?? Constants.DB_PARAMS_MAX_LIMIT, [ 'id', 'chargePoints', 'connectors', 'coordinates', 'lastSeen', 'siteAreaID', 'siteID' ]); @@ -448,7 +448,7 @@ export default class OCPIUtilsService { chargingStationEvses.push(...OCPIUtilsService.convertChargingStation2MultipleEvses(tenant, chargingStation, null, options)); } // Always update OCPI data - await ChargingStationStorage.saveChargingStationOcpiData(tenant.id, chargingStation.id, { evses: chargingStationEvses }); + await ChargingStationStorage.saveChargingStationOcpiData(tenant, chargingStation.id, { evses: chargingStationEvses }); evses.push(...chargingStationEvses); } return evses; @@ -481,9 +481,9 @@ export default class OCPIUtilsService { if (!session.kwh) { session.kwh = 0; } - let transaction: Transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant.id, session.id); + let transaction: Transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant, session.id); if (!transaction) { - const user = await UserStorage.getUser(tenant.id, session.auth_id); + const user = await UserStorage.getUser(tenant, session.auth_id); if (!user) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -495,7 +495,7 @@ export default class OCPIUtilsService { }); } const evse = session.location.evses[0]; - const chargingStation = await ChargingStationStorage.getChargingStationByOcpiEvseID(tenant.id, evse.evse_id); + const chargingStation = await ChargingStationStorage.getChargingStationByOcpiEvseID(tenant, evse.evse_id); if (!chargingStation) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -590,7 +590,7 @@ export default class OCPIUtilsService { userID: transaction.userID }; } - await TransactionStorage.saveTransaction(tenant.id, transaction); + await TransactionStorage.saveTransaction(tenant, transaction); await this.updateConnector(tenant, transaction); } @@ -605,7 +605,7 @@ export default class OCPIUtilsService { ocpiError: OCPIStatusCode.CODE_2001_INVALID_PARAMETER_ERROR }); } - const transaction: Transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant.id, cdr.id); + const transaction: Transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant, cdr.id); if (!transaction) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -653,7 +653,7 @@ export default class OCPIUtilsService { transaction.ocpiData = {}; } transaction.ocpiData.cdr = cdr; - await TransactionStorage.saveTransaction(tenant.id, transaction); + await TransactionStorage.saveTransaction(tenant, transaction); await this.updateConnector(tenant, transaction); } @@ -729,9 +729,9 @@ export default class OCPIUtilsService { locale: Utils.getLocaleFromLanguage(token.language), } as User; // Save User - emspUser.id = await UserStorage.saveUser(tenant.id, emspUser); - await UserStorage.saveUserRole(tenant.id, emspUser.id, UserRole.BASIC); - await UserStorage.saveUserStatus(tenant.id, emspUser.id, UserStatus.ACTIVE); + emspUser.id = await UserStorage.saveUser(tenant, emspUser); + await UserStorage.saveUserRole(tenant, emspUser.id, UserRole.BASIC); + await UserStorage.saveUserStatus(tenant, emspUser.id, UserStatus.ACTIVE); const tagToSave = { id: token.uid, issuer: false, @@ -1061,7 +1061,7 @@ export default class OCPIUtilsService { } private static async updateConnector(tenant: Tenant, transaction: Transaction): Promise { - const chargingStation = await ChargingStationStorage.getChargingStation(tenant.id, transaction.chargeBoxID); + const chargingStation = await ChargingStationStorage.getChargingStation(tenant, transaction.chargeBoxID); if (chargingStation && chargingStation.connectors) { for (const connector of chargingStation.connectors) { if (connector.connectorId === transaction.connectorId && connector.currentTransactionID === 0 || connector.currentTransactionID === transaction.id) { @@ -1086,7 +1086,7 @@ export default class OCPIUtilsService { connector.currentInstantWatts = 0; connector.currentInactivityStatus = null; } - await ChargingStationStorage.saveChargingStationConnectors(tenant.id, chargingStation.id, chargingStation.connectors); + await ChargingStationStorage.saveChargingStationConnectors(tenant, chargingStation.id, chargingStation.connectors); } } } diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOCdrsEndpoint.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOCdrsEndpoint.ts index 9d45eadb54..3c1d08e916 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOCdrsEndpoint.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOCdrsEndpoint.ts @@ -48,7 +48,7 @@ export default class CPOCdrsEndpoint extends AbstractEndpoint { private async getAllCdrs(tenant: Tenant, limit: number, skip: number, dateFrom?: Date, dateTo?: Date): Promise> { // Get all transactions - const transactions = await TransactionStorage.getTransactions(tenant.id, + const transactions = await TransactionStorage.getTransactions(tenant, { issuer: true, ocpiCdrDateFrom: dateFrom, ocpiCdrDateTo: dateTo, ocpiCdrChecked: true }, { limit, skip }, ['ocpiData'] ); diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOCommandsEndpoint.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOCommandsEndpoint.ts index 0b321eec95..f184e8b3f4 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOCommandsEndpoint.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOCommandsEndpoint.ts @@ -108,7 +108,7 @@ export default class CPOCommandsEndpoint extends AbstractEndpoint { } // Get the Charging Station const chargingStation = await ChargingStationStorage.getChargingStationByOcpiLocationUid( - tenant.id, startSession.location_id, startSession.evse_uid); + tenant, startSession.location_id, startSession.evse_uid); if (!chargingStation) { await Logging.logError({ tenantID: tenant.id, @@ -188,7 +188,7 @@ export default class CPOCommandsEndpoint extends AbstractEndpoint { } // Save Auth await ChargingStationStorage.saveChargingStationRemoteAuthorizations( - tenant.id, chargingStation.id, chargingStation.remoteAuthorizations); + tenant, chargingStation.id, chargingStation.remoteAuthorizations); // Called Async as the response to the eMSP is sent asynchronously and this request has to finish before the command returns void this.remoteStartTransaction(tenant, chargingStation, connector, startSession, ocpiEndpoint).catch(() => { }); // Ok @@ -208,7 +208,7 @@ export default class CPOCommandsEndpoint extends AbstractEndpoint { ocpiError: OCPIStatusCode.CODE_2001_INVALID_PARAMETER_ERROR }); } - const transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant.id, stopSession.session_id); + const transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant, stopSession.session_id); if (!transaction) { await Logging.logError({ tenantID: tenant.id, @@ -241,7 +241,7 @@ export default class CPOCommandsEndpoint extends AbstractEndpoint { }); return this.buildOCPIResponse(OCPICommandResponseType.REJECTED); } - const chargingStation = await ChargingStationStorage.getChargingStation(tenant.id, transaction.chargeBoxID); + const chargingStation = await ChargingStationStorage.getChargingStation(tenant, transaction.chargeBoxID); if (!chargingStation) { await Logging.logError({ tenantID: tenant.id, diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOSessionsEndpoint.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOSessionsEndpoint.ts index 495deb457d..99efaf8494 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOSessionsEndpoint.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOSessionsEndpoint.ts @@ -72,7 +72,7 @@ export default class CPOSessionsEndpoint extends AbstractEndpoint { // Result const sessions: OCPISession[] = []; // Get all transactions - const transactions = await TransactionStorage.getTransactions(tenant.id, + const transactions = await TransactionStorage.getTransactions(tenant, { issuer: true, ocpiSessionDateFrom: dateFrom, ocpiSessionDateTo: dateTo }, { limit, skip }, ['ocpiData']); for (const transaction of transactions.result) { diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPCdrsEndpoint.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPCdrsEndpoint.ts index 73e38090cb..685df31431 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPCdrsEndpoint.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPCdrsEndpoint.ts @@ -48,7 +48,7 @@ export default class EMSPCdrsEndpoint extends AbstractEndpoint { ocpiError: OCPIStatusCode.CODE_2001_INVALID_PARAMETER_ERROR }); } - const transaction: Transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant.id, id); + const transaction: Transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant, id); if (!transaction) { throw new AppError({ source: Constants.CENTRAL_SERVER, 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 16668f736b..c0152bb9e0 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 @@ -74,7 +74,7 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint { } if (evseUID) { const chargingStation = await ChargingStationStorage.getChargingStationByOcpiLocationUid( - tenant.id, locationID, evseUID); + tenant, locationID, evseUID); if (!chargingStation) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -223,7 +223,7 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint { (chargingStationEvse) => chargingStationEvse.uid === chargingStationEvse.uid); if (evse.status) { if (evse.status === OCPIEvseStatus.REMOVED) { - await ChargingStationStorage.deleteChargingStation(tenant.id, chargingStation.id); + await ChargingStationStorage.deleteChargingStation(tenant, chargingStation.id); return; } foundChargingStationEvse.status = evse.status; @@ -260,7 +260,7 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint { chargingStation.connectors = patchedChargingStation.connectors; chargingStation.maximumPower = patchedChargingStation.maximumPower; } - await ChargingStationStorage.saveChargingStation(tenant.id, chargingStation); + await ChargingStationStorage.saveChargingStation(tenant, chargingStation); } private async patchEvseConnector(tenant: Tenant, chargingStation: ChargingStation, ocpiConnector: Partial) { @@ -289,16 +289,16 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint { if (ocpiConnector.standard) { foundConnector.type = OCPIUtilsService.convertOCPIConnectorType2ConnectorType(ocpiConnector.standard); } - await ChargingStationStorage.saveChargingStationConnectors(tenant.id, chargingStation.id, chargingStation.connectors); + await ChargingStationStorage.saveChargingStationConnectors(tenant, chargingStation.id, chargingStation.connectors); } private async updateEvse(tenant: Tenant, evse: OCPIEvse, location: OCPILocation) { if (evse.status === OCPIEvseStatus.REMOVED) { const chargingStation = await ChargingStationStorage.getChargingStationByOcpiLocationUid( - tenant.id, location.id, evse.uid); + tenant, location.id, evse.uid); if (chargingStation) { // Delete - await ChargingStationStorage.deleteChargingStation(tenant.id, chargingStation.id); + await ChargingStationStorage.deleteChargingStation(tenant, chargingStation.id); await Logging.logInfo({ tenantID: tenant.id, action: ServerAction.OCPI_PATCH_LOCATION, @@ -311,7 +311,7 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint { } else { // Create/Update const chargingStation = OCPIUtilsService.convertEvseToChargingStation(evse, location); - await ChargingStationStorage.saveChargingStation(tenant.id, chargingStation); + await ChargingStationStorage.saveChargingStation(tenant, chargingStation); await Logging.logDebug({ tenantID: tenant.id, action: ServerAction.OCPI_PATCH_LOCATION, @@ -325,7 +325,7 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint { private async updateConnector(tenant: Tenant, evse: OCPIEvse, evseConnector: OCPIConnector, location: OCPILocation) { const chargingStation = await ChargingStationStorage.getChargingStationByOcpiLocationUid( - tenant.id, location.id, evse.uid); + tenant, location.id, evse.uid); if (!chargingStation) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -359,7 +359,7 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint { type: OCPIUtilsService.convertOCPIConnectorType2ConnectorType(evseConnector.standard), }); } - await ChargingStationStorage.saveChargingStationConnectors(tenant.id, chargingStation.id, chargingStation.connectors); + await ChargingStationStorage.saveChargingStationConnectors(tenant, chargingStation.id, chargingStation.connectors); } } diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPSessionsEndpoint.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPSessionsEndpoint.ts index 54aaa4e16e..80a4afd6a4 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPSessionsEndpoint.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPSessionsEndpoint.ts @@ -53,7 +53,7 @@ export default class EMSPSessionsEndpoint extends AbstractEndpoint { ocpiError: OCPIStatusCode.CODE_2001_INVALID_PARAMETER_ERROR }); } - const transaction: Transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant.id, sessionId); + const transaction: Transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant, sessionId); if (!transaction) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -120,7 +120,7 @@ export default class EMSPSessionsEndpoint extends AbstractEndpoint { ocpiError: OCPIStatusCode.CODE_2001_INVALID_PARAMETER_ERROR }); } - const transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant.id, sessionId); + const transaction = await TransactionStorage.getOCPITransactionBySessionID(tenant, sessionId); if (!transaction) { throw new AppError({ source: Constants.CENTRAL_SERVER, diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPTokensEndpoint.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPTokensEndpoint.ts index 9fc263926a..c58dfba5de 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPTokensEndpoint.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPTokensEndpoint.ts @@ -104,7 +104,7 @@ export default class EMSPTokensEndpoint extends AbstractEndpoint { }); } const chargingStation = await ChargingStationStorage.getChargingStationByOcpiEvseID( - tenant.id, locationReference.evse_uids[0]); + tenant, locationReference.evse_uids[0]); if (!chargingStation) { throw new AppError({ source: Constants.CENTRAL_SERVER, diff --git a/src/server/ocpp/json/JsonRestWSConnection.ts b/src/server/ocpp/json/JsonRestWSConnection.ts index d985d9dcae..be96274d83 100644 --- a/src/server/ocpp/json/JsonRestWSConnection.ts +++ b/src/server/ocpp/json/JsonRestWSConnection.ts @@ -62,7 +62,7 @@ export default class JsonRestWSConnection extends WSConnection { public async handleRequest(messageId: string, commandName: ServerAction, commandPayload: Record | string): Promise { // Get the Charging Station - const chargingStation = await ChargingStationStorage.getChargingStation(this.getTenantID(), this.getChargingStationID()); + const chargingStation = await ChargingStationStorage.getChargingStation(this.getTenant(), this.getChargingStationID()); if (!chargingStation) { throw new BackendError({ source: this.getChargingStationID(), diff --git a/src/server/ocpp/json/JsonWSConnection.ts b/src/server/ocpp/json/JsonWSConnection.ts index 0dfd293cf5..85a40fec86 100644 --- a/src/server/ocpp/json/JsonWSConnection.ts +++ b/src/server/ocpp/json/JsonWSConnection.ts @@ -31,6 +31,7 @@ export default class JsonWSConnection extends WSConnection { constructor(wsConnection: WebSocket, req: http.IncomingMessage, wsServer: JsonCentralSystemServer) { // Call super super(wsConnection, req, wsServer); + let backendError: BackendError; // Check Protocol (required field of OCPP spec) switch (wsConnection.protocol) { // OCPP 1.6? @@ -42,13 +43,22 @@ export default class JsonWSConnection extends WSConnection { break; // Not Found default: - // Error - throw new BackendError({ + backendError = new BackendError({ source: this.getChargingStationID(), module: MODULE_NAME, method: 'constructor', - message: `Protocol ${wsConnection.protocol} not supported` + message: wsConnection.protocol ? + `Web Socket Protocol '${wsConnection.protocol}' not supported` : 'Web Socket Protocol is mandatory' }); + // Log in the right Tenants + void Logging.logException( + backendError, + ServerAction.WS_JSON_CONNECTION_ERROR, + this.getChargingStationID(), + MODULE_NAME, 'constructor', + this.getTenantID() + ); + throw backendError; } this.isConnectionAlive = true; // Handle Socket ping @@ -75,15 +85,15 @@ export default class JsonWSConnection extends WSConnection { } }; // Update the Charging Station - const chargingStation = await ChargingStationStorage.getChargingStation(this.getTenantID(), this.getChargingStationID(), {}, ['id']); + const chargingStation = await ChargingStationStorage.getChargingStation(this.getTenant(), this.getChargingStationID(), {}, ['id']); if (chargingStation) { // Update Last Seen - await ChargingStationStorage.saveChargingStationLastSeen(this.getTenantID(), + await ChargingStationStorage.saveChargingStationLastSeen(this.getTenant(), chargingStation.id, { lastSeen: new Date() }); // Update CF Instance if (Configuration.isCloudFoundry()) { await ChargingStationStorage.saveChargingStationCFApplicationIDAndInstanceIndex( - this.getTenantID(), chargingStation.id, Configuration.getCFApplicationIDAndInstanceIndex()); + this.getTenant(), chargingStation.id, Configuration.getCFApplicationIDAndInstanceIndex()); } // Must have a valid Token } else { @@ -148,7 +158,7 @@ export default class JsonWSConnection extends WSConnection { } public async handleRequest(messageId: string, commandName: ServerAction, commandPayload: Record | string): Promise { - await Logging.logChargingStationServerReceiveAction(MODULE_NAME, this.getTenantID(), this.getChargingStationID(), commandName, commandPayload); + await Logging.logChargingStationServerReceiveAction(Constants.MODULE_JSON_OCPP_SERVER_16, this.getTenantID(), this.getChargingStationID(), commandName, commandPayload); const methodName = `handle${commandName}`; // Check if method exist in the service if (typeof this.chargingStationService[methodName] === 'function') { @@ -158,7 +168,7 @@ export default class JsonWSConnection extends WSConnection { // Call it const result = await this.chargingStationService[methodName](this.headers, commandPayload); // Log - await Logging.logChargingStationServerRespondAction(MODULE_NAME, this.getTenantID(), this.getChargingStationID(), commandName, result); + await Logging.logChargingStationServerRespondAction(Constants.MODULE_JSON_OCPP_SERVER_16, this.getTenantID(), this.getChargingStationID(), commandName, result); // Send Response await this.sendMessage(messageId, result, OCPPMessageType.CALL_RESULT_MESSAGE, commandName); } else { @@ -192,10 +202,10 @@ export default class JsonWSConnection extends WSConnection { if (!this.lastSeen || (Date.now() - this.lastSeen.getTime()) > Constants.LAST_SEEN_UPDATE_INTERVAL_MILLIS) { // Update last seen this.lastSeen = new Date(); - const chargingStation = await ChargingStationStorage.getChargingStation(this.getTenantID(), + const chargingStation = await ChargingStationStorage.getChargingStation(this.getTenant(), this.getChargingStationID(), { issuer: true }, ['id']); if (chargingStation) { - await ChargingStationStorage.saveChargingStationLastSeen(this.getTenantID(), this.getChargingStationID(), + await ChargingStationStorage.saveChargingStationLastSeen(this.getTenant(), this.getChargingStationID(), { lastSeen: this.lastSeen }); } } diff --git a/src/server/ocpp/json/WSConnection.ts b/src/server/ocpp/json/WSConnection.ts index 44f2194ba5..e722e9c9bd 100644 --- a/src/server/ocpp/json/WSConnection.ts +++ b/src/server/ocpp/json/WSConnection.ts @@ -28,7 +28,6 @@ export default abstract class WSConnection { private clientIP: string | string[]; private wsConnection: WebSocket; private requests: { [id: string]: OCPPRequest }; - private validTenant: boolean; constructor(wsConnection: WebSocket, req: http.IncomingMessage, wsServer: JsonCentralSystemServer) { // Init @@ -44,7 +43,6 @@ export default abstract class WSConnection { message: `WS connection opening attempts with URL: '${req.url}'`, }); // Default - this.validTenant = false; this.requests = {}; // Check URL: remove starting and trailing '/' if (this.url.endsWith('/')) { @@ -120,7 +118,6 @@ export default abstract class WSConnection { try { // Check Tenant? this.tenant = await DatabaseUtils.checkTenant(this.tenantID); - this.validTenant = true; } catch (error) { // Custom Error await Logging.logException(error, ServerAction.WS_CONNECTION, this.getChargingStationID(), 'WSConnection', 'initialize', this.tenantID); @@ -328,11 +325,7 @@ export default abstract class WSConnection { } public getTenantID(): string { - if (this.isTenantValid()) { - return this.tenantID; - } - // No: go to the master tenant - return Constants.DEFAULT_TENANT; + return this.tenantID; } public getTenant(): Tenant { @@ -347,10 +340,6 @@ export default abstract class WSConnection { return `${this.getTenantID()}~${this.getChargingStationID()}`; } - public isTenantValid(): boolean { - return this.validTenant; - } - public isWSConnectionOpen(): boolean { return this.getConnectionStatus() === OPEN; } diff --git a/src/server/ocpp/services/OCPPService.ts b/src/server/ocpp/services/OCPPService.ts index 92c9556027..10198d027b 100644 --- a/src/server/ocpp/services/OCPPService.ts +++ b/src/server/ocpp/services/OCPPService.ts @@ -82,7 +82,7 @@ export default class OCPPService { }); } // Get Charging Station - let chargingStation = await ChargingStationStorage.getChargingStation(tenant.id, headers.chargeBoxIdentity); + let chargingStation = await ChargingStationStorage.getChargingStation(tenant, headers.chargeBoxIdentity); if (!chargingStation) { // Create Charging Station chargingStation = await this.checkAndCreateChargingStation(tenant, bootNotification, headers); @@ -94,12 +94,12 @@ export default class OCPPService { this.enrichChargingStation(chargingStation, headers, bootNotification); // Clear Firmware Status if (chargingStation.firmwareUpdateStatus) { - await ChargingStationStorage.saveChargingStationFirmwareStatus(tenant.id, chargingStation.id, null); + await ChargingStationStorage.saveChargingStationFirmwareStatus(tenant, chargingStation.id, null); } // Apply Charging Station Template const templateUpdateResult = await this.applyChargingStationTemplate(tenant, chargingStation); // Save Charging Station - await ChargingStationStorage.saveChargingStation(tenant.id, chargingStation); + await ChargingStationStorage.saveChargingStation(tenant, chargingStation); // Save Boot Notification await OCPPStorage.saveBootNotification(tenant, bootNotification); // Send Notification (Async) @@ -151,7 +151,7 @@ export default class OCPPService { timezone: Utils.getTimezone(chargingStation.coordinates) }; // Save Charging Station lastSeen date - await ChargingStationStorage.saveChargingStationLastSeen(tenant.id, chargingStation.id, { + await ChargingStationStorage.saveChargingStationLastSeen(tenant, chargingStation.id, { lastSeen: chargingStation.lastSeen, currentIPAddress: chargingStation.currentIPAddress, }); @@ -269,13 +269,13 @@ export default class OCPPService { // Roaming await OCPPUtils.processTransactionRoaming(tenant, transaction, chargingStation, transaction.tag, TransactionAction.UPDATE); // Save Transaction - await TransactionStorage.saveTransaction(tenant.id, transaction); + await TransactionStorage.saveTransaction(tenant, transaction); // Update Charging Station await this.updateChargingStationWithTransaction(tenant, chargingStation, transaction); // Handle End Of charge await this.checkNotificationEndOfCharge(tenant, chargingStation, transaction); // Save Charging Station - await ChargingStationStorage.saveChargingStation(tenant.id, chargingStation); + await ChargingStationStorage.saveChargingStation(tenant, chargingStation); // First Meter Value -> Trigger Smart Charging to adjust the limit if (transaction.numberOfMeterValues === 1 && transaction.phasesUsed) { // Yes: Trigger Smart Charging @@ -391,7 +391,7 @@ export default class OCPPService { // Enrich this.enrichOCPPRequest(chargingStation, firmwareStatusNotification); // Save the status to Charging Station - await ChargingStationStorage.saveChargingStationFirmwareStatus(tenant.id, chargingStation.id, firmwareStatusNotification.status); + await ChargingStationStorage.saveChargingStationFirmwareStatus(tenant, chargingStation.id, firmwareStatusNotification.status); // Save it await OCPPStorage.saveFirmwareStatusNotification(tenant, firmwareStatusNotification); // Log @@ -446,11 +446,11 @@ export default class OCPPService { // Roaming await OCPPUtils.processTransactionRoaming(tenant, newTransaction, chargingStation, tag, TransactionAction.START); // Save it - await TransactionStorage.saveTransaction(tenant.id, newTransaction); + await TransactionStorage.saveTransaction(tenant, newTransaction); // Clean up await this.updateChargingStationConnectorWithTransaction(tenant, newTransaction, chargingStation, user); // Save - await ChargingStationStorage.saveChargingStation(tenant.id, chargingStation); + await ChargingStationStorage.saveChargingStation(tenant, chargingStation); // Notify this.notifyStartTransaction(tenant, newTransaction, chargingStation, user); // Log @@ -551,7 +551,7 @@ export default class OCPPService { // Free the connector OCPPUtils.clearChargingStationConnector(chargingStation, transaction.connectorId); // Save Charging Station - await ChargingStationStorage.saveChargingStation(tenant.id, chargingStation); + await ChargingStationStorage.saveChargingStation(tenant, chargingStation); // Soft Stop this.checkSoftStopTransaction(transaction, stopTransaction, isSoftStop); // Transaction End has already been received? @@ -565,7 +565,7 @@ export default class OCPPService { // Roaming await OCPPUtils.processTransactionRoaming(tenant, transaction, chargingStation, transaction.tag, TransactionAction.STOP); // Save the transaction - await TransactionStorage.saveTransaction(tenant.id, transaction); + await TransactionStorage.saveTransaction(tenant, transaction); // Notify User this.notifyStopTransaction(tenant, chargingStation, transaction, user, alternateUser); // Recompute the Smart Charging Plan @@ -633,7 +633,7 @@ export default class OCPPService { alternateUser = authorizedUsers.alternateUser; } else { // Get the User - user = await UserStorage.getUserByTagId(tenant.id, tagId); + user = await UserStorage.getUserByTagId(tenant, tagId); } // Already Stopped? if (transaction.stop) { @@ -675,7 +675,7 @@ export default class OCPPService { } private async deleteAllTransactionTxProfile(tenant: Tenant, transaction: Transaction) { - const chargingProfiles = await ChargingStationStorage.getChargingProfiles(tenant.id, { + const chargingProfiles = await ChargingStationStorage.getChargingProfiles(tenant, { chargingStationIDs: [transaction.chargeBoxID], connectorID: transaction.connectorId, profilePurposeType: ChargingProfilePurposeType.TX_PROFILE, @@ -728,9 +728,9 @@ export default class OCPPService { connector1?.connectorId - connector2?.connectorId); } // Save Charging Station - await ChargingStationStorage.saveChargingStationConnectors(tenant.id, chargingStation.id, + await ChargingStationStorage.saveChargingStationConnectors(tenant, chargingStation.id, chargingStation.connectors, chargingStation.backupConnectors); - await ChargingStationStorage.saveChargingStationLastSeen(tenant.id, chargingStation.id, { lastSeen: new Date() }); + await ChargingStationStorage.saveChargingStationLastSeen(tenant, chargingStation.id, { lastSeen: new Date() }); // Process Smart Charging await this.processSmartChargingStatusNotification(tenant, chargingStation, connector); // Log @@ -834,7 +834,7 @@ export default class OCPPService { if (statusNotification.status === ChargePointStatus.AVAILABLE) { // Get the last transaction const lastTransaction = await TransactionStorage.getLastTransactionFromChargingStation( - tenant.id, chargingStation.id, connector.connectorId, { withChargingStation: true, withUser: true, withTag: true }); + tenant, chargingStation.id, connector.connectorId, { withChargingStation: true, withUser: true, withTag: true }); // Transaction completed if (lastTransaction?.stop) { // Check Inactivity @@ -902,7 +902,7 @@ export default class OCPPService { await this.checkAndSendOICPTransactionCdr(tenant, lastTransaction, chargingStation, lastTransaction.tag); } // Save - await TransactionStorage.saveTransaction(tenant.id, lastTransaction); + await TransactionStorage.saveTransaction(tenant, lastTransaction); } else if (!Utils.isNullOrUndefined(lastTransaction)) { await Logging.logWarning({ tenantID: tenant.id, @@ -1426,7 +1426,7 @@ export default class OCPPService { let activeTransaction: Transaction, lastCheckedTransactionID: number; do { // Check if the charging station has already a transaction - activeTransaction = await TransactionStorage.getActiveTransaction(tenant.id, chargingStation.id, connectorId); + activeTransaction = await TransactionStorage.getActiveTransaction(tenant, chargingStation.id, connectorId); // Exists already? if (activeTransaction) { // Avoid infinite Loop @@ -1445,7 +1445,7 @@ export default class OCPPService { message: `${Utils.buildConnectorInfo(activeTransaction.connectorId, activeTransaction.id)} Transaction with no consumption has been deleted` }); // Delete - await TransactionStorage.deleteTransaction(tenant.id, activeTransaction.id); + await TransactionStorage.deleteTransaction(tenant, activeTransaction.id); // Clear connector OCPPUtils.clearChargingStationConnector(chargingStation, activeTransaction.connectorId); } else { @@ -1596,7 +1596,7 @@ export default class OCPPService { // Smart Charging must be active if (Utils.isTenantComponentActive(tenant, TenantComponents.SMART_CHARGING)) { // Get Site Area - const siteArea = await SiteAreaStorage.getSiteArea(tenant.id, chargingStation.siteAreaID); + const siteArea = await SiteAreaStorage.getSiteArea(tenant, chargingStation.siteAreaID); if (siteArea && siteArea.smartCharging) { const siteAreaLock = await LockingHelper.acquireSiteAreaSmartChargingLock(tenant.id, siteArea, 30); if (siteAreaLock) { @@ -1657,7 +1657,7 @@ export default class OCPPService { transaction.carCatalogID = car?.carCatalogID; } // Clear - await UserStorage.saveUserLastSelectedCarID(tenant.id, user.id, null); + await UserStorage.saveUserLastSelectedCarID(tenant, user.id, null); } } @@ -1685,7 +1685,7 @@ export default class OCPPService { private async createTransaction(tenant: Tenant, startTransaction: OCPPStartTransactionRequestExtended): Promise { return { - id: await TransactionStorage.findAvailableID(tenant.id), + id: await TransactionStorage.findAvailableID(tenant), issuer: true, chargeBoxID: startTransaction.chargeBoxID, tagID: startTransaction.idTag, @@ -1754,7 +1754,7 @@ export default class OCPPService { newChargingStation.registrationStatus = RegistrationStatus.ACCEPTED; // Assign to Site Area if (token.siteAreaID) { - const siteArea = await SiteAreaStorage.getSiteArea(tenant.id, token.siteAreaID, { withSite: true }); + const siteArea = await SiteAreaStorage.getSiteArea(tenant, token.siteAreaID, { withSite: true }); if (siteArea) { newChargingStation.companyID = siteArea.site?.companyID; newChargingStation.siteID = siteArea.siteID; @@ -1985,7 +1985,7 @@ export default class OCPPService { detailedMessages: { headers, meterValues } }); } - const transaction = await TransactionStorage.getTransaction(tenant.id, meterValues.transactionId, { withUser: true, withTag: true }); + const transaction = await TransactionStorage.getTransaction(tenant, meterValues.transactionId, { withUser: true, withTag: true }); if (!transaction) { // Try a Remote Stop the Transaction if (meterValues.transactionId) { @@ -2051,7 +2051,7 @@ export default class OCPPService { private async getTransactionFromStopTransaction(tenant: Tenant, chargingStation: ChargingStation, headers: OCPPHeader, stopTransaction: OCPPStopTransactionRequestExtended): Promise { - const transaction = await TransactionStorage.getTransaction(tenant.id, stopTransaction.transactionId, { withUser: true, withTag: true }); + const transaction = await TransactionStorage.getTransaction(tenant, stopTransaction.transactionId, { withUser: true, withTag: true }); if (!transaction) { throw new BackendError({ source: chargingStation.id, diff --git a/src/server/ocpp/soap/services/SoapCentralSystemService12.ts b/src/server/ocpp/soap/services/SoapCentralSystemService12.ts index a4d0e21068..eb36483a48 100644 --- a/src/server/ocpp/soap/services/SoapCentralSystemService12.ts +++ b/src/server/ocpp/soap/services/SoapCentralSystemService12.ts @@ -7,7 +7,7 @@ import { ServerAction } from '../../../../types/Server'; import Utils from '../../../../utils/Utils'; import global from '../../../../types/GlobalType'; -const MODULE_NAME = 'SoapCentralSystemService12'; +const MODULE_NAME = Constants.MODULE_SOAP_OCPP_SERVER_12; export default { /* Services */ CentralSystemService: { /* Ports */ diff --git a/src/server/ocpp/soap/services/SoapCentralSystemService15.ts b/src/server/ocpp/soap/services/SoapCentralSystemService15.ts index b919693981..d558f882b0 100644 --- a/src/server/ocpp/soap/services/SoapCentralSystemService15.ts +++ b/src/server/ocpp/soap/services/SoapCentralSystemService15.ts @@ -7,7 +7,7 @@ import { ServerAction } from '../../../../types/Server'; import Utils from '../../../../utils/Utils'; import global from '../../../../types/GlobalType'; -const MODULE_NAME = 'SoapCentralSystemService15'; +const MODULE_NAME = Constants.MODULE_SOAP_OCPP_SERVER_15; export default { /* Services */ CentralSystemService: { /* Ports */ diff --git a/src/server/ocpp/soap/services/SoapCentralSystemService16.ts b/src/server/ocpp/soap/services/SoapCentralSystemService16.ts index 96d1eda636..5a462039dd 100644 --- a/src/server/ocpp/soap/services/SoapCentralSystemService16.ts +++ b/src/server/ocpp/soap/services/SoapCentralSystemService16.ts @@ -7,7 +7,7 @@ import { ServerAction } from '../../../../types/Server'; import Utils from '../../../../utils/Utils'; import global from '../../../../types/GlobalType'; -const MODULE_NAME = 'SoapCentralSystemService16'; +const MODULE_NAME = Constants.MODULE_SOAP_OCPP_SERVER_16; export default { /* Services */ CentralSystemService: { /* Ports */ diff --git a/src/server/ocpp/utils/OCPPUtils.ts b/src/server/ocpp/utils/OCPPUtils.ts index d2a5232ea6..9c30922dcb 100644 --- a/src/server/ocpp/utils/OCPPUtils.ts +++ b/src/server/ocpp/utils/OCPPUtils.ts @@ -64,7 +64,7 @@ export default class OCPPUtils { }); } // Get the Token - const token = await RegistrationTokenStorage.getRegistrationToken(tenant.id, tokenID); + const token = await RegistrationTokenStorage.getRegistrationToken(tenant, tokenID); if (!token) { throw new BackendError({ source: chargingStationID, @@ -537,7 +537,7 @@ export default class OCPPUtils { transaction.roundedPrice = Utils.truncTo(transaction.price, 2); transaction.stop.price = transaction.currentCumulatedPrice; transaction.stop.roundedPrice = Utils.truncTo(transaction.currentCumulatedPrice, 2); - await TransactionStorage.saveTransaction(tenant.id, transaction); + await TransactionStorage.saveTransaction(tenant, transaction); } public static async rebuildTransactionConsumptions(tenant: Tenant, transaction: Transaction): Promise { @@ -564,7 +564,7 @@ export default class OCPPUtils { transactionSimplePricePerkWh = Utils.roundTo(transaction.stop.price / (transaction.stop.totalConsumptionWh / 1000), 2); } // Get the Charging Station - const chargingStation = await ChargingStationStorage.getChargingStation(tenant.id, + const chargingStation = await ChargingStationStorage.getChargingStation(tenant, transaction.chargeBoxID, { includeDeleted: true }); if (!chargingStation) { throw new BackendError({ @@ -665,7 +665,7 @@ export default class OCPPUtils { // Build extra inactivity consumption const consumptionCreated = await OCPPUtils.buildExtraConsumptionInactivity(tenant, transaction); // Save - await TransactionStorage.saveTransaction(tenant.id, transaction); + await TransactionStorage.saveTransaction(tenant, transaction); return consumptions.length + (consumptionCreated ? 1 : 0); } @@ -1056,7 +1056,7 @@ export default class OCPPUtils { consumption.limitSiteAreaSource = SiteAreaLimitSource.SITE_AREA; } else { // Compute it for Charging Stations - const chargingStationsOfSiteArea = await ChargingStationStorage.getChargingStations(tenant.id, + const chargingStationsOfSiteArea = await ChargingStationStorage.getChargingStations(tenant, { siteAreaIDs: [siteArea.id], withSiteArea: true }, Constants.DB_PARAMS_MAX_LIMIT); for (const chargingStationOfSiteArea of chargingStationsOfSiteArea.result) { if (Utils.objectHasProperty(chargingStationOfSiteArea, 'connectors')) { @@ -1070,7 +1070,7 @@ export default class OCPPUtils { // Save Site Area max consumption if (siteArea) { siteArea.maximumPower = consumption.limitSiteAreaWatts; - await SiteAreaStorage.saveSiteArea(tenant.id, siteArea); + await SiteAreaStorage.saveSiteArea(tenant, siteArea); } } consumption.smartChargingActive = siteArea.smartCharging; @@ -1250,7 +1250,7 @@ export default class OCPPUtils { sectionsUpdated.push('OCPP'); } // Save - await ChargingStationStorage.saveChargingStation(tenant.id, chargingStation); + await ChargingStationStorage.saveChargingStation(tenant, chargingStation); await Logging.logInfo({ tenantID: tenant.id, source: chargingStation.id, @@ -1303,7 +1303,7 @@ export default class OCPPUtils { inSuccess: 0 }; for (const chargingStation of siteArea.chargingStations) { - const chargingProfiles = await ChargingStationStorage.getChargingProfiles(tenant.id, { + const chargingProfiles = await ChargingStationStorage.getChargingProfiles(tenant, { chargingStationIDs: [chargingStation.id], profilePurposeType: params.profilePurposeType, transactionId: params.transactionId @@ -1330,7 +1330,7 @@ export default class OCPPUtils { public static async clearAndDeleteChargingProfile(tenant: Tenant, chargingProfile: ChargingProfile): Promise { // Get charging station - const chargingStation = await ChargingStationStorage.getChargingStation(tenant.id, chargingProfile.chargingStationID); + const chargingStation = await ChargingStationStorage.getChargingStation(tenant, chargingProfile.chargingStationID); // Check if Charging Profile is supported if (!chargingStation.capabilities?.supportChargingProfiles) { throw new BackendError({ @@ -1369,7 +1369,7 @@ export default class OCPPUtils { throw error; } // Delete from database - await ChargingStationStorage.deleteChargingProfile(tenant.id, chargingProfile.id); + await ChargingStationStorage.deleteChargingProfile(tenant, chargingProfile.id); await Logging.logInfo({ tenantID: tenant.id, source: chargingStation.id, @@ -1407,7 +1407,7 @@ export default class OCPPUtils { public static async setAndSaveChargingProfile(tenant: Tenant, chargingProfile: ChargingProfile): Promise { // Get charging station - const chargingStation = await ChargingStationStorage.getChargingStation(tenant.id, chargingProfile.chargingStationID); + const chargingStation = await ChargingStationStorage.getChargingStation(tenant, chargingProfile.chargingStationID); if (!chargingStation) { throw new BackendError({ source: chargingProfile.chargingStationID, @@ -1453,7 +1453,7 @@ export default class OCPPUtils { }); } // Save - const chargingProfileID = await ChargingStationStorage.saveChargingProfile(tenant.id, chargingProfile); + const chargingProfileID = await ChargingStationStorage.saveChargingProfile(tenant, chargingProfile); await Logging.logInfo({ tenantID: tenant.id, source: chargingStation.id, @@ -1549,7 +1549,7 @@ export default class OCPPUtils { let chargingStation: ChargingStation; try { chargingStation = await ChargingStationStorage.getChargingStation( - ocppHeader.tenantID, ocppHeader.chargeBoxIdentity); + tenant, ocppHeader.chargeBoxIdentity); if (!chargingStation) { throw new BackendError({ source: ocppHeader.chargeBoxIdentity, @@ -1599,7 +1599,7 @@ export default class OCPPUtils { timestamp: new Date() }; // Get saved OCPP configuration from DB - const ocppParametersFromDB = await ChargingStationStorage.getOcppParameters(tenant.id, chargingStation.id); + const ocppParametersFromDB = await ChargingStationStorage.getOcppParameters(tenant, chargingStation.id); // Charging Station configuration not found if (!chargingStationOcppParameters.configuration) { if (ocppParametersFromDB.count === 0) { @@ -1621,7 +1621,7 @@ export default class OCPPUtils { } } // Save configuration - await ChargingStationStorage.saveOcppParameters(tenant.id, chargingStationOcppParameters); + await ChargingStationStorage.saveOcppParameters(tenant, chargingStationOcppParameters); // Ok await Logging.logInfo({ tenantID: tenant.id, @@ -1646,7 +1646,7 @@ export default class OCPPUtils { let rebootRequired = false; // Get current OCPP parameters in DB let currentOcppParameters: OcppParameter[]; - const ocppParametersFromDB = await ChargingStationStorage.getOcppParameters(tenant.id, chargingStation.id); + const ocppParametersFromDB = await ChargingStationStorage.getOcppParameters(tenant, chargingStation.id); if (ocppParametersFromDB.count > 0) { currentOcppParameters = ocppParametersFromDB.result; } @@ -2188,7 +2188,7 @@ export default class OCPPUtils { private static async setConnectorPhaseAssignment(tenant: Tenant, chargingStation: ChargingStation, connector: Connector, nrOfPhases?: number): Promise { const csNumberOfPhases = nrOfPhases ?? Utils.getNumberOfConnectedPhases(chargingStation, null, connector.connectorId); if (chargingStation.siteAreaID) { - const siteArea = await SiteAreaStorage.getSiteArea(tenant.id, chargingStation.siteAreaID); + const siteArea = await SiteAreaStorage.getSiteArea(tenant, chargingStation.siteAreaID); // Phase Assignment to Grid has to be handled only for Site Area with 3 phases if (siteArea.numberOfPhases === 3) { switch (csNumberOfPhases) { diff --git a/src/server/oicp/OICPUtils.ts b/src/server/oicp/OICPUtils.ts index 803547f06f..c1920903bb 100644 --- a/src/server/oicp/OICPUtils.ts +++ b/src/server/oicp/OICPUtils.ts @@ -56,7 +56,7 @@ export default class OICPUtils { if (chargingStation.issuer && chargingStation.public) { const chargingStationEvses = OICPUtils.convertChargingStation2MultipleEvses(site, chargingStation.siteArea, chargingStation, options); // Update the Charging Station's OICP Data - await ChargingStationStorage.saveChargingStationOicpData(tenant.id, chargingStation.id, { + await ChargingStationStorage.saveChargingStationOicpData(tenant, chargingStation.id, { evses: chargingStationEvses }); evses.push(...chargingStationEvses); @@ -130,7 +130,7 @@ export default class OICPUtils { public static async getChargingStationConnectorFromEvseID(tenant: Tenant, evseID: OICPEvseID): Promise<{ chargingStation: ChargingStation, connector: Connector }> { const evseIDComponents = RoamingUtils.getEvseIdComponents(evseID); - const chargingStation = await ChargingStationStorage.getChargingStationByOicpEvseID(tenant.id, evseID); + const chargingStation = await ChargingStationStorage.getChargingStationByOicpEvseID(tenant, evseID); let foundConnector: Connector; if (chargingStation) { for (const connector of chargingStation.connectors) { @@ -213,7 +213,7 @@ export default class OICPUtils { // Get the first non used Authorization OICP ID / Session ID for (const authorization of authorizations.result) { if (authorization.authorizationId) { - const oicpTransaction = await TransactionStorage.getOICPTransactionBySessionID(tenant.id, authorization.authorizationId); + const oicpTransaction = await TransactionStorage.getOICPTransactionBySessionID(tenant, authorization.authorizationId); // OICP SessionID not used yet if (!oicpTransaction) { sessionId = authorization.authorizationId; @@ -228,7 +228,7 @@ export default class OICPUtils { } } - public static async createOICPVirtualUser(tenantID: string): Promise { + public static async createOICPVirtualUser(tenant: Tenant): Promise { // Create the virtual OICP user const newVirtualOICPUser = UserStorage.createNewUser() as User; newVirtualOICPUser.email = Constants.OICP_VIRTUAL_USER_EMAIL; @@ -238,9 +238,9 @@ export default class OICPUtils { newVirtualOICPUser.status = UserStatus.ACTIVE; newVirtualOICPUser.notificationsActive = false; // Save User - newVirtualOICPUser.id = await UserStorage.saveUser(tenantID, newVirtualOICPUser); + newVirtualOICPUser.id = await UserStorage.saveUser(tenant, newVirtualOICPUser); // Save User Status - await UserStorage.saveUserStatus(tenantID, newVirtualOICPUser.id, UserStatus.ACTIVE); + await UserStorage.saveUserStatus(tenant, newVirtualOICPUser.id, UserStatus.ACTIVE); } private static convertConnector2OICPChargingFacility(chargingStation: ChargingStation, connector: Connector): OICPChargingFacility { diff --git a/src/server/oicp/oicp-services-impl/oicp-2.3.0/CPORemoteAuthorizationsEndpoint.ts b/src/server/oicp/oicp-services-impl/oicp-2.3.0/CPORemoteAuthorizationsEndpoint.ts index 3b2c3daec1..6a15032548 100644 --- a/src/server/oicp/oicp-services-impl/oicp-2.3.0/CPORemoteAuthorizationsEndpoint.ts +++ b/src/server/oicp/oicp-services-impl/oicp-2.3.0/CPORemoteAuthorizationsEndpoint.ts @@ -136,7 +136,7 @@ export default class CPORemoteAuthorizationsEndpoint extends AbstractEndpoint { } // Save Auth await ChargingStationStorage.saveChargingStationRemoteAuthorizations( - tenant.id, chargingStation.id, chargingStation.remoteAuthorizations); + tenant, chargingStation.id, chargingStation.remoteAuthorizations); // Start the transaction const result = await this.remoteStartTransaction(tenant, chargingStation, connector, authorizeRemoteStart); if (result?.status === OCPPRemoteStartStopStatus.ACCEPTED) { @@ -152,7 +152,7 @@ export default class CPORemoteAuthorizationsEndpoint extends AbstractEndpoint { const session = {} as Partial; session.id = authorizeRemoteStop.SessionID; session.providerID = authorizeRemoteStop.ProviderID; - const transaction = await TransactionStorage.getOICPTransactionBySessionID(tenant.id, authorizeRemoteStop.SessionID); + const transaction = await TransactionStorage.getOICPTransactionBySessionID(tenant, authorizeRemoteStop.SessionID); if (!transaction) { await Logging.logError({ tenantID: tenant.id, @@ -180,7 +180,7 @@ export default class CPORemoteAuthorizationsEndpoint extends AbstractEndpoint { }); return OICPUtils.noSuccess(session, `Transaction with OICP Transaction ID '${authorizeRemoteStop.SessionID}' is already stopped`); } - const chargingStation = await ChargingStationStorage.getChargingStation(tenant.id, transaction.chargeBoxID); + const chargingStation = await ChargingStationStorage.getChargingStation(tenant, transaction.chargeBoxID); if (!chargingStation) { await Logging.logError({ tenantID: tenant.id, diff --git a/src/server/rest/v1/router/api/TransactionRouter.ts b/src/server/rest/v1/router/api/TransactionRouter.ts index 85c8c0fb4c..a3cc2eeac6 100644 --- a/src/server/rest/v1/router/api/TransactionRouter.ts +++ b/src/server/rest/v1/router/api/TransactionRouter.ts @@ -16,6 +16,15 @@ export default class TransactionRouter { this.buildRouteTransactions(); this.buildRouteTransaction(); this.buildRouteTransactionConsumption(); + this.buildRouteDeleteTransaction(); + this.buildRouteDeleteTransactions(); + this.buildRoutePushTransactionCDR(); + this.buildRouteExportTransactionCDR(); + this.buildRouteRebuildTransactionConsumption(); + this.buildRouteTransactionSoftStop(); + this.buildRouteTransactionsRefund(); + this.buildRouteTransactionsAssignUser(); + this.buildRouteTransactionsExport(); return this.router; } @@ -32,10 +41,69 @@ export default class TransactionRouter { }); } + protected buildRouteDeleteTransactions(): void { + this.router.delete(`/${ServerRoute.REST_TRANSACTIONS}`, async (req: Request, res: Response, next: NextFunction) => { + await RouterUtils.handleServerAction(TransactionService.handleDeleteTransactions.bind(this), ServerAction.TRANSACTIONS_DELETE, req, res, next); + }); + } + + protected buildRouteDeleteTransaction(): void { + this.router.delete(`/${ServerRoute.REST_TRANSACTION}`, async (req: Request, res: Response, next: NextFunction) => { + req.query.ID = req.params.id; + await RouterUtils.handleServerAction(TransactionService.handleDeleteTransaction.bind(this), ServerAction.TRANSACTION_DELETE, req, res, next); + }); + } + + protected buildRoutePushTransactionCDR(): void { + this.router.post(`/${ServerRoute.REST_TRANSACTION_CDR}`, async (req: Request, res: Response, next: NextFunction) => { + req.body.transactionId = req.params.id; + await RouterUtils.handleServerAction(TransactionService.handlePushTransactionCdr.bind(this), ServerAction.OCPI_PUSH_CDRS, req, res, next); + }); + } + + protected buildRouteExportTransactionCDR(): void { + this.router.get(`/${ServerRoute.REST_TRANSACTION_CDR_EXPORT}`, async (req: Request, res: Response, next: NextFunction) => { + req.query.ID = req.params.id; + await RouterUtils.handleServerAction(TransactionService.handleExportTransactionOcpiCdr.bind(this), ServerAction.TRANSACTION_OCPI_CDR_EXPORT, req, res, next); + }); + } + protected buildRouteTransactionConsumption(): void { this.router.get(`/${ServerRoute.REST_TRANSACTIONS_CONSUMPTION}`, async (req: Request, res: Response, next: NextFunction) => { req.query.TransactionId = req.params.id; await RouterUtils.handleServerAction(TransactionService.handleGetTransactionConsumption.bind(this), ServerAction.TRANSACTION_CONSUMPTION, req, res, next); }); } + + protected buildRouteRebuildTransactionConsumption(): void { + this.router.post(`/${ServerRoute.REST_TRANSACTION_CONSUMPTIONS_REBUILD}`, async (req: Request, res: Response, next: NextFunction) => { + req.query.ID = req.params.id; + await RouterUtils.handleServerAction(TransactionService.handleRebuildTransactionConsumptions.bind(this), ServerAction.REBUILD_TRANSACTION_CONSUMPTIONS, req, res, next); + }); + } + + protected buildRouteTransactionSoftStop(): void { + this.router.put(`/${ServerRoute.REST_TRANSACTION_SOFT_STOP}`, async (req: Request, res: Response, next: NextFunction) => { + req.body.ID = req.params.id; + await RouterUtils.handleServerAction(TransactionService.handleTransactionSoftStop.bind(this), ServerAction.TRANSACTION_SOFT_STOP, req, res, next); + }); + } + + protected buildRouteTransactionsRefund(): void { + this.router.post(`/${ServerRoute.REST_TRANSACTIONS_REFUND}`, async (req: Request, res: Response, next: NextFunction) => { + await RouterUtils.handleServerAction(TransactionService.handleRefundTransactions.bind(this), ServerAction.TRANSACTIONS_REFUND, req, res, next); + }); + } + + protected buildRouteTransactionsAssignUser(): void { + this.router.put(`/${ServerRoute.REST_TRANSACTIONS_ASSIGN_USER}`, async (req: Request, res: Response, next: NextFunction) => { + await RouterUtils.handleServerAction(TransactionService.handleAssignTransactionsToUser.bind(this), ServerAction.ASSIGN_TRANSACTIONS_TO_USER, req, res, next); + }); + } + + protected buildRouteTransactionsExport(): void { + this.router.get(`/${ServerRoute.REST_TRANSACTIONS_EXPORT}`, async (req: Request, res: Response, next: NextFunction) => { + await RouterUtils.handleServerAction(TransactionService.handleExportTransactions.bind(this), ServerAction.TRANSACTIONS_EXPORT, req, res, next); + }); + } } diff --git a/src/server/rest/v1/service/AssetService.ts b/src/server/rest/v1/service/AssetService.ts index 16fb97398d..81a2457bc8 100644 --- a/src/server/rest/v1/service/AssetService.ts +++ b/src/server/rest/v1/service/AssetService.ts @@ -527,7 +527,7 @@ export default class AssetService { // Check Site Area let siteArea: SiteArea = null; if (filteredRequest.siteAreaID) { - siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, filteredRequest.siteAreaID); + siteArea = await SiteAreaStorage.getSiteArea(req.tenant, filteredRequest.siteAreaID); UtilsService.assertObjectExists(action, siteArea, `Site Area ID '${filteredRequest.siteAreaID}' does not exist`, MODULE_NAME, 'handleCreateAsset', req.user); } @@ -585,7 +585,7 @@ export default class AssetService { // Check Site Area let siteArea: SiteArea = null; if (filteredRequest.siteAreaID) { - siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, filteredRequest.siteAreaID); + siteArea = await SiteAreaStorage.getSiteArea(req.tenant, filteredRequest.siteAreaID); UtilsService.assertObjectExists(action, siteArea, `Site Area ID '${filteredRequest.siteAreaID}' does not exist`, MODULE_NAME, 'handleUpdateAsset', req.user); } diff --git a/src/server/rest/v1/service/AuthService.ts b/src/server/rest/v1/service/AuthService.ts index 8588bbe0b8..a340219019 100644 --- a/src/server/rest/v1/service/AuthService.ts +++ b/src/server/rest/v1/service/AuthService.ts @@ -69,8 +69,8 @@ export default class AuthService { // Filter const filteredRequest = AuthValidator.getInstance().validateAuthSignIn(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, @@ -80,8 +80,8 @@ export default class AuthService { action: action }); } - req.user = { tenantID }; - const user = await UserStorage.getUserByEmail(tenantID, filteredRequest.email); + req.user = { tenantID: tenant.id }; + const user = await UserStorage.getUserByEmail(tenant, filteredRequest.email); UtilsService.assertObjectExists(action, user, `User with email '${filteredRequest.email}' does not exist`, MODULE_NAME, 'handleLogIn', req.user); // Check if the number of trials is reached @@ -98,14 +98,14 @@ export default class AuthService { message: 'User has been unlocked after a period of time can try to login again' }); // Save User Status - await UserStorage.saveUserStatus(req.user.tenantID, user.id, UserStatus.ACTIVE); + await UserStorage.saveUserStatus(req.tenant, user.id, UserStatus.ACTIVE); // Init User Password - await UserStorage.saveUserPassword(req.user.tenantID, user.id, + await UserStorage.saveUserPassword(req.tenant, user.id, { passwordWrongNbrTrials: 0, passwordBlockedUntil: null, passwordResetHash: null }); // Read user again - const updatedUser = await UserStorage.getUser(tenantID, user.id); + const updatedUser = await UserStorage.getUser(tenant, user.id); // Check user - await AuthService.checkUserLogin(action, tenantID, updatedUser, filteredRequest, req, res, next); + await AuthService.checkUserLogin(action, tenant, updatedUser, filteredRequest, req, res, next); } else { // Return data throw new AppError({ @@ -121,11 +121,11 @@ export default class AuthService { user.passwordWrongNbrTrials = 0; user.passwordBlockedUntil = null; // Check user - await AuthService.checkUserLogin(action, tenantID, user, filteredRequest, req, res, next); + await AuthService.checkUserLogin(action, tenant, user, filteredRequest, req, res, next); } } else { // Nbr trials OK: Check user - await AuthService.checkUserLogin(action, tenantID, user, filteredRequest, req, res, next); + await AuthService.checkUserLogin(action, tenant, user, filteredRequest, req, res, next); } } @@ -172,7 +172,7 @@ export default class AuthService { // Check Mandatory field UtilsService.checkIfUserValid(filteredRequest as User, null, req); // Check email - const user = await UserStorage.getUserByEmail(tenant.id, filteredRequest.email); + const user = await UserStorage.getUserByEmail(tenant, filteredRequest.email); if (user) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -192,17 +192,17 @@ export default class AuthService { newUser.locale = filteredRequest.locale; newUser.createdOn = new Date(); const verificationToken = Utils.generateToken(filteredRequest.email); - const endUserLicenseAgreement = await UserStorage.getEndUserLicenseAgreement(tenant.id, Utils.getLanguageFromLocale(newUser.locale)); + const endUserLicenseAgreement = await UserStorage.getEndUserLicenseAgreement(tenant, Utils.getLanguageFromLocale(newUser.locale)); // Save User - newUser.id = await UserStorage.saveUser(tenant.id, newUser); + newUser.id = await UserStorage.saveUser(tenant, newUser); // Save User Status if (tenant.id === Constants.DEFAULT_TENANT) { - await UserStorage.saveUserRole(tenant.id, newUser.id, UserRole.SUPER_ADMIN); + await UserStorage.saveUserRole(tenant, newUser.id, UserRole.SUPER_ADMIN); } else { - await UserStorage.saveUserRole(tenant.id, newUser.id, UserRole.BASIC); + await UserStorage.saveUserRole(tenant, newUser.id, UserRole.BASIC); } // Save User Status - await UserStorage.saveUserStatus(tenant.id, newUser.id, UserStatus.PENDING); + await UserStorage.saveUserStatus(tenant, newUser.id, UserStatus.PENDING); // Get the i18n translation class const i18nManager = I18nManager.getInstanceForLocale(newUser.locale); const tag: Tag = { @@ -217,7 +217,7 @@ export default class AuthService { }; await TagStorage.saveTag(req.user.tenantID, tag); // Save User password - await UserStorage.saveUserPassword(tenant.id, newUser.id, + await UserStorage.saveUserPassword(tenant, newUser.id, { password: newPasswordHashed, passwordWrongNbrTrials: 0, @@ -225,9 +225,9 @@ export default class AuthService { passwordBlockedUntil: null }); // Save User Account Verification - await UserStorage.saveUserAccountVerification(tenant.id, newUser.id, { verificationToken }); + await UserStorage.saveUserAccountVerification(tenant, newUser.id, { verificationToken }); // Save User EULA - await UserStorage.saveUserEULA(tenant.id, newUser.id, + await UserStorage.saveUserEULA(tenant, newUser.id, { eulaAcceptedOn: new Date(), eulaAcceptedVersion: endUserLicenseAgreement.version, @@ -241,7 +241,7 @@ export default class AuthService { if (sites.count > 0) { const siteIDs = sites.result.map((site) => site.id); if (siteIDs && siteIDs.length > 0) { - await UserStorage.addSitesToUser(tenant.id, newUser.id, siteIDs); + await UserStorage.addSitesToUser(tenant, newUser.id, siteIDs); } } // Log @@ -308,12 +308,12 @@ export default class AuthService { }); } // Generate a new password - const user = await UserStorage.getUserByEmail(tenant.id, filteredRequest.email); + const user = await UserStorage.getUserByEmail(tenant, 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(tenant.id, user.id, { passwordResetHash: resetHash }); + await UserStorage.saveUserPassword(tenant, user.id, { passwordResetHash: resetHash }); // Log await Logging.logSecurityInfo({ tenantID: tenant.id, @@ -340,15 +340,16 @@ export default class AuthService { next(); } - public static async resetUserPassword(tenantID: string, filteredRequest: Partial, action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { + public static async resetUserPassword(tenant: Tenant, filteredRequest: Partial, + action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { // Get the user - const user = await UserStorage.getUserByPasswordResetHash(tenantID, filteredRequest.hash); + const user = await UserStorage.getUserByPasswordResetHash(tenant, filteredRequest.hash); UtilsService.assertObjectExists(action, user, `User with password reset hash '${filteredRequest.hash}' does not exist`, MODULE_NAME, 'handleUserPasswordReset', req.user); // Hash it const newHashedPassword = await Utils.hashPasswordBcrypt(filteredRequest.password); // Save new password - await UserStorage.saveUserPassword(tenantID, user.id, + await UserStorage.saveUserPassword(tenant, user.id, { password: newHashedPassword, passwordWrongNbrTrials: 0, @@ -358,11 +359,11 @@ export default class AuthService { ); // Unlock if (user.status === UserStatus.LOCKED) { - await UserStorage.saveUserStatus(tenantID, user.id, UserStatus.ACTIVE); + await UserStorage.saveUserStatus(tenant, user.id, UserStatus.ACTIVE); } // Log await Logging.logSecurityInfo({ - tenantID: tenantID, + tenantID: tenant.id, user: user, action: action, module: MODULE_NAME, method: 'handleUserPasswordReset', @@ -390,7 +391,7 @@ export default class AuthService { // Check hash if (filteredRequest.hash) { // Send the new password - await AuthService.resetUserPassword(tenant.id, filteredRequest, action, req, res, next); + await AuthService.resetUserPassword(tenant, filteredRequest, action, req, res, next); } else { // Send Confirmation Email for requesting a new password await AuthService.checkAndSendResetPasswordConfirmationEmail(tenant, filteredRequest, action, req, res, next); @@ -401,8 +402,8 @@ export default class AuthService { // Filter const filteredRequest = AuthValidator.getInstance().validateAuthCheckEula(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.GENERAL_ERROR, @@ -412,7 +413,7 @@ export default class AuthService { }); } // Get User - const user = await UserStorage.getUserByEmail(tenantID, filteredRequest.Email); + const user = await UserStorage.getUserByEmail(tenant, filteredRequest.Email); if (!user) { // Do not return error, only reject it res.json({ eulaAccepted: false }); @@ -420,7 +421,7 @@ export default class AuthService { return; } // Get last Eula version - const endUserLicenseAgreement = await UserStorage.getEndUserLicenseAgreement(tenantID, Utils.getLanguageFromLocale(user.locale)); + const endUserLicenseAgreement = await UserStorage.getEndUserLicenseAgreement(tenant, Utils.getLanguageFromLocale(user.locale)); if (user.eulaAcceptedHash === endUserLicenseAgreement.hash) { // Check if version matches res.json({ eulaAccepted: true }); @@ -436,7 +437,7 @@ export default class AuthService { // Filter const filteredRequest = AuthValidator.getInstance().validateAuthEula(req.query); // Get it - const endUserLicenseAgreement = await UserStorage.getEndUserLicenseAgreement(Constants.DEFAULT_TENANT, filteredRequest.Language); + const endUserLicenseAgreement = await UserStorage.getEndUserLicenseAgreement(Constants.DEFAULT_TENANT_OBJECT, filteredRequest.Language); res.json(endUserLicenseAgreement); next(); } @@ -478,7 +479,7 @@ export default class AuthService { }); } // Check email - const user = await UserStorage.getUserByEmail(tenant.id, filteredRequest.Email); + const user = await UserStorage.getUserByEmail(tenant, 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 @@ -503,10 +504,18 @@ export default class AuthService { message: 'Wrong Verification Token' }); } - const userSettings = await SettingStorage.getUserSettings(tenant.id); - const userStatus = userSettings.user.autoActivateAccountAfterValidation ? UserStatus.ACTIVE : UserStatus.INACTIVE; // Save User Status - await UserStorage.saveUserStatus(tenant.id, user.id, userStatus); + let userStatus: UserStatus; + // When it's user creation case we take the user settings + if (!user.importedData) { + const userSettings = await SettingStorage.getUserSettings(tenant.id); + userStatus = userSettings.user.autoActivateAccountAfterValidation ? UserStatus.ACTIVE : UserStatus.INACTIVE; + } else { + // When it's user import case we take checkbox param saved to db + userStatus = user.importedData.autoActivateUserAtImport ? UserStatus.ACTIVE : UserStatus.INACTIVE; + } + // Save User Status + await UserStorage.saveUserStatus(tenant, user.id, userStatus); // For integration with billing const billingImpl = await BillingFactory.getBillingImpl(tenant); if (billingImpl) { @@ -531,7 +540,7 @@ export default class AuthService { } } // Save User Verification Account - await UserStorage.saveUserAccountVerification(tenant.id, user.id, + await UserStorage.saveUserAccountVerification(tenant, user.id, { verificationToken: null, verifiedAt: new Date() }); // Log await Logging.logSecurityInfo({ @@ -606,7 +615,7 @@ export default class AuthService { }); } // Is valid email? - const user = await UserStorage.getUserByEmail(tenant.id, filteredRequest.email); + const user = await UserStorage.getUserByEmail(tenant, 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 @@ -630,7 +639,7 @@ export default class AuthService { verificationToken = Utils.generateToken(filteredRequest.email); user.verificationToken = verificationToken; // Save User Verification Account - await UserStorage.saveUserAccountVerification(tenant.id, user.id, { verificationToken }); + await UserStorage.saveUserAccountVerification(tenant, user.id, { verificationToken }); } else { // Get existing verificationToken verificationToken = user.verificationToken; @@ -669,7 +678,7 @@ export default class AuthService { res.status(StatusCodes.OK).send({}); } - public static async userLoginWrongPassword(action: ServerAction, tenantID: string, user: User, req: Request, res: Response, next: NextFunction): Promise { + public static async userLoginWrongPassword(action: ServerAction, tenant: Tenant, user: User, req: Request, res: Response, next: NextFunction): Promise { // Add wrong trial + 1 if (isNaN(user.passwordWrongNbrTrials)) { user.passwordWrongNbrTrials = 0; @@ -679,9 +688,9 @@ export default class AuthService { if (passwordWrongNbrTrials >= _centralSystemRestConfig.passwordWrongNumberOfTrial) { // Too many attempts, lock user // Save User Status - await UserStorage.saveUserStatus(tenantID, user.id, UserStatus.LOCKED); + await UserStorage.saveUserStatus(tenant, user.id, UserStatus.LOCKED); // Save User Blocked Date - await UserStorage.saveUserPassword(tenantID, user.id, + await UserStorage.saveUserPassword(tenant, user.id, { passwordWrongNbrTrials, passwordBlockedUntil: moment().add(_centralSystemRestConfig.passwordBlockedWaitTimeMin, 'm').toDate() @@ -698,7 +707,7 @@ export default class AuthService { }); } else { // Save User Nbr Password Trials - await UserStorage.saveUserPassword(tenantID, user.id, { passwordWrongNbrTrials }); + await UserStorage.saveUserPassword(tenant, user.id, { passwordWrongNbrTrials }); // Log throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -712,10 +721,10 @@ export default class AuthService { } } - public static async userLoginSucceeded(action: ServerAction, tenantID: string, user: User, req: Request, res: Response, next: NextFunction): Promise { + public static async userLoginSucceeded(action: ServerAction, tenant: Tenant, user: User, req: Request, res: Response, next: NextFunction): Promise { // Password / Login OK await Logging.logSecurityInfo({ - tenantID: tenantID, + tenantID: tenant.id, user: user, module: MODULE_NAME, method: 'checkUserLogin', action: action, message: 'User logged in successfully' @@ -723,8 +732,8 @@ export default class AuthService { // Set Eula Info on Login Only if (action === ServerAction.LOGIN) { // Save EULA - const endUserLicenseAgreement = await UserStorage.getEndUserLicenseAgreement(tenantID, Utils.getLanguageFromLocale(user.locale)); - await UserStorage.saveUserEULA(tenantID, user.id, + const endUserLicenseAgreement = await UserStorage.getEndUserLicenseAgreement(tenant, Utils.getLanguageFromLocale(user.locale)); + await UserStorage.saveUserEULA(tenant, user.id, { eulaAcceptedOn: new Date(), eulaAcceptedVersion: endUserLicenseAgreement.version, @@ -733,12 +742,12 @@ export default class AuthService { ); } // Reset wrong number of trial - await UserStorage.saveUserPassword(tenantID, user.id, + await UserStorage.saveUserPassword(tenant, user.id, { passwordWrongNbrTrials: 0, passwordBlockedUntil: null, passwordResetHash: null }); // Get the tags (limited) to avoid an overweighted token - const tags = await TagStorage.getTags(tenantID, { userIDs: [user.id] }, Constants.DB_PARAMS_DEFAULT_RECORD); + const tags = await TagStorage.getTags(tenant.id, { userIDs: [user.id] }, Constants.DB_PARAMS_DEFAULT_RECORD); // Yes: build token - const payload = await Authorizations.buildUserToken(tenantID, user, tags.result); + const payload = await Authorizations.buildUserToken(tenant, user, tags.result); // Build token let token: string; // Role Demo? @@ -777,7 +786,7 @@ export default class AuthService { return (tenant ? tenant : null); } - public static async checkUserLogin(action: ServerAction, tenantID: string, user: User, + public static async checkUserLogin(action: ServerAction, tenant: Tenant, user: User, filteredRequest: Partial, req: Request, res: Response, next: NextFunction): Promise { // User Found? if (!user) { @@ -837,10 +846,10 @@ export default class AuthService { }); } // Login OK - await AuthService.userLoginSucceeded(action, tenantID, user, req, res, next); + await AuthService.userLoginSucceeded(action, tenant, user, req, res, next); } else { // Login KO - await AuthService.userLoginWrongPassword(action, tenantID, user, req, res, next); + await AuthService.userLoginWrongPassword(action, tenant, user, req, res, next); } } } diff --git a/src/server/rest/v1/service/AuthorizationService.ts b/src/server/rest/v1/service/AuthorizationService.ts index fee2c6db72..ed629a0b1d 100644 --- a/src/server/rest/v1/service/AuthorizationService.ts +++ b/src/server/rest/v1/service/AuthorizationService.ts @@ -171,7 +171,7 @@ export default class AuthorizationService { // Not an Admin? if (userToken.role !== UserRole.ADMIN) { // Get Site IDs for which user is admin from db - const siteAdminSiteIDs = await AuthorizationService.getSiteAdminSiteIDs(tenant.id, userToken); + const siteAdminSiteIDs = await AuthorizationService.getSiteAdminSiteIDs(tenant, userToken); // Check Site if (!Utils.isEmptyArray(siteAdminSiteIDs) && siteAdminSiteIDs.includes(siteArea.siteID)) { // Site Authorized, now check Assets @@ -580,9 +580,9 @@ export default class AuthorizationService { return authorizationFilters; } - public static async getSiteAdminSiteIDs(tenantID: string, userToken: UserToken): Promise { + public static async getSiteAdminSiteIDs(tenant: Tenant, userToken: UserToken): Promise { // Get the Sites where the user is Site Admin - const userSites = await UserStorage.getUserSites(tenantID, + const userSites = await UserStorage.getUserSites(tenant, { userIDs: [userToken.id], siteAdmin: true @@ -592,9 +592,9 @@ export default class AuthorizationService { return userSites.result.map((userSite) => userSite.siteID); } - private static async getSiteOwnerSiteIDs(tenantID: string, userToken: UserToken): Promise { + private static async getSiteOwnerSiteIDs(tenant: Tenant, userToken: UserToken): Promise { // Get the Sites where the user is Site Owner - const userSites = await UserStorage.getUserSites(tenantID, + const userSites = await UserStorage.getUserSites(tenant, { userIDs: [userToken.id], siteOwner: true @@ -660,8 +660,8 @@ export default class AuthorizationService { if (userToken.role !== UserRole.ADMIN && userToken.role !== UserRole.SUPER_ADMIN) { if (Utils.isTenantComponentActive(tenant, TenantComponents.ORGANIZATION)) { // Get Site IDs from Site Admin & Site Owner flag - const siteAdminSiteIDs = await AuthorizationService.getSiteAdminSiteIDs(tenant.id, userToken); - const siteOwnerSiteIDs = await AuthorizationService.getSiteOwnerSiteIDs(tenant.id, userToken); + const siteAdminSiteIDs = await AuthorizationService.getSiteAdminSiteIDs(tenant, userToken); + const siteOwnerSiteIDs = await AuthorizationService.getSiteOwnerSiteIDs(tenant, userToken); const allSites = _.uniq([...siteAdminSiteIDs, ...siteOwnerSiteIDs]); if (!Utils.isEmptyArray(allSites)) { // Force the filters diff --git a/src/server/rest/v1/service/BillingService.ts b/src/server/rest/v1/service/BillingService.ts index a65d08dad1..a176494506 100644 --- a/src/server/rest/v1/service/BillingService.ts +++ b/src/server/rest/v1/service/BillingService.ts @@ -204,7 +204,7 @@ export default class BillingService { }); } // Get user - const userToSynchronize = await UserStorage.getUser(req.user.tenantID, filteredRequest.id); + const userToSynchronize = await UserStorage.getUser(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, userToSynchronize, `User ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handleSynchronizeUser', req.user); // Get the lock @@ -257,7 +257,7 @@ export default class BillingService { }); } // Get user - const user = await UserStorage.getUser(req.user.tenantID, filteredRequest.id); + const user = await UserStorage.getUser(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, user, `User ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handleSynchronizeUser', req.user); // Get the User lock @@ -407,7 +407,7 @@ export default class BillingService { let user: User; if (!Authorizations.isAdmin(req.user)) { // Get the User - user = await UserStorage.getUser(req.user.tenantID, req.user.id); + user = await UserStorage.getUser(req.tenant, req.user.id); UtilsService.assertObjectExists(action, user, `User ID '${req.user.id}' does not exist`, MODULE_NAME, 'handleSynchronizeUserInvoices', req.user); } @@ -477,7 +477,7 @@ export default class BillingService { } const filteredRequest = BillingSecurity.filterForceSynchronizeUserInvoicesRequest(req.body); // Get the User - const user = await UserStorage.getUser(req.user.tenantID, filteredRequest.userID); + const user = await UserStorage.getUser(req.tenant, filteredRequest.userID); UtilsService.assertObjectExists(action, user, `User ID '${filteredRequest.userID}' does not exist`, MODULE_NAME, 'handleForceSynchronizeUserInvoices', req.user); // Get the Invoice lock @@ -536,7 +536,7 @@ export default class BillingService { }); } // Get user - ACHTUNG user !== req.user - const user: User = await UserStorage.getUser(req.user.tenantID, filteredRequest.userID); + const user: User = await UserStorage.getUser(req.tenant, filteredRequest.userID); UtilsService.assertObjectExists(action, user, `User ID '${filteredRequest.userID}' does not exist`, MODULE_NAME, 'handleSetupPaymentMethod', req.user); // Invoke the billing implementation @@ -576,7 +576,7 @@ export default class BillingService { }); } // Get user - ACHTUNG user !== req.user - const user: User = await UserStorage.getUser(req.user.tenantID, filteredRequest.userID); + const user: User = await UserStorage.getUser(req.tenant, filteredRequest.userID); UtilsService.assertObjectExists(action, user, `User ID '${filteredRequest.userID}' does not exist`, MODULE_NAME, 'handleBillingGetPaymentMethods', req.user); // Invoke the billing implementation @@ -624,7 +624,7 @@ export default class BillingService { }); } const userID = filteredRequest.userID; - const user: User = await UserStorage.getUser(req.user.tenantID, userID); + const user: User = await UserStorage.getUser(req.tenant, userID); UtilsService.assertObjectExists(action, user, `User ID '${userID}' does not exist`, MODULE_NAME, 'handleBillingDeletePaymentMethod', req.user); // Invoke the billing implementation diff --git a/src/server/rest/v1/service/ChargingStationService.ts b/src/server/rest/v1/service/ChargingStationService.ts index bed6816320..f195bae57e 100644 --- a/src/server/rest/v1/service/ChargingStationService.ts +++ b/src/server/rest/v1/service/ChargingStationService.ts @@ -55,7 +55,7 @@ export default class ChargingStationService { // Filter const filteredRequest = ChargingStationValidator.getInstance().validateChargingStationUpdateParametersReq({ ...req.params, ...req.body }); // Check the Charging Station - const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, filteredRequest.id); + const chargingStation = await ChargingStationStorage.getChargingStation(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${filteredRequest.id}' does not exist.`, MODULE_NAME, 'handleUpdateChargingStationParams', req.user); // OCPI Charging Station @@ -72,7 +72,7 @@ export default class ChargingStationService { let siteArea: SiteArea = null; // Check the Site Area if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION) && filteredRequest.siteAreaID) { - siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, filteredRequest.siteAreaID, { withSite: true }); + siteArea = await SiteAreaStorage.getSiteArea(req.tenant, filteredRequest.siteAreaID, { withSite: true }); UtilsService.assertObjectExists(action, siteArea, `Site Area ID '${filteredRequest.siteAreaID}' does not exist.`, MODULE_NAME, 'handleUpdateChargingStationParams', req.user); } @@ -302,7 +302,7 @@ export default class ChargingStationService { chargingStation.lastChangedBy = { 'id': req.user.id }; chargingStation.lastChangedOn = new Date(); // Update - await ChargingStationStorage.saveChargingStation(req.user.tenantID, chargingStation); + await ChargingStationStorage.saveChargingStation(req.tenant, chargingStation); // Reboot the Charging Station to reapply the templates if (resetAndApplyTemplate) { try { @@ -350,7 +350,7 @@ export default class ChargingStationService { }); } // Charging Station - const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, filteredRequest.chargingStationID); + const chargingStation = await ChargingStationStorage.getChargingStation(req.tenant, filteredRequest.chargingStationID); UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${filteredRequest.chargingStationID}' does not exist.`, MODULE_NAME, 'handleChargingStationLimitPower', req.user); // Charge Point @@ -372,7 +372,7 @@ export default class ChargingStationService { let siteID = null; if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION)) { // Get the Site Area - const siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, chargingStation.siteAreaID); + const siteArea = await SiteAreaStorage.getSiteArea(req.tenant, chargingStation.siteAreaID); siteID = siteArea ? siteArea.siteID : null; } // Check Auth @@ -409,7 +409,7 @@ export default class ChargingStationService { }); } // Check Charging Profile - const chargingProfiles = await ChargingStationStorage.getChargingProfiles(req.user.tenantID, + const chargingProfiles = await ChargingStationStorage.getChargingProfiles(req.tenant, { chargingStationIDs: [chargingStation.id], connectorID: 0 }, Constants.DB_PARAMS_MAX_LIMIT); const updatedChargingProfiles: ChargingProfile[] = Utils.cloneObject(chargingProfiles.result); @@ -517,7 +517,7 @@ export default class ChargingStationService { projectFields = projectFields.filter((projectField) => httpProjectFields.includes(projectField)); } // Get the profiles - const chargingProfiles = await ChargingStationStorage.getChargingProfiles(req.user.tenantID, + const chargingProfiles = await ChargingStationStorage.getChargingProfiles(req.tenant, { search: filteredRequest.Search, chargingStationIDs: filteredRequest.ChargingStationID ? filteredRequest.ChargingStationID.split('|') : null, @@ -547,7 +547,7 @@ export default class ChargingStationService { const filteredRequest = ChargingStationValidator.getInstance().validateSmartChargingTriggerReq(req.query); UtilsService.assertIdIsProvided(action, filteredRequest.SiteAreaID, MODULE_NAME, 'handleTriggerSmartCharging', req.user); // Get Site Area - const siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, filteredRequest.SiteAreaID); + const siteArea = await SiteAreaStorage.getSiteArea(req.tenant, filteredRequest.SiteAreaID); UtilsService.assertObjectExists(action, siteArea, `Site Area ID '${filteredRequest.SiteAreaID}' does not exist`, MODULE_NAME, 'handleTriggerSmartCharging', req.user); // Check auth @@ -614,7 +614,7 @@ export default class ChargingStationService { // Check ConnectorID UtilsService.assertIdIsProvided(action, filteredRequest.ConnectorID, MODULE_NAME, 'handleGenerateQrCodeForConnector', req.user); // Get the Charging Station` - const chargingStation: ChargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, filteredRequest.ChargingStationID); + const chargingStation: ChargingStation = await ChargingStationStorage.getChargingStation(req.tenant, filteredRequest.ChargingStationID); // Found ChargingStation ? UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${filteredRequest.ChargingStationID}' does not exist`, MODULE_NAME, 'handleGetChargingStationOcppParameters', req.user); @@ -647,7 +647,7 @@ export default class ChargingStationService { // Filter const filteredRequest = ChargingStationValidator.getInstance().validateChargingProfileUpdateReq({ ...req.params, ...req.body }); // Check for existing charging profile - const chargingProfile = await ChargingStationStorage.getChargingProfile(req.tenant.id, filteredRequest.id); + const chargingProfile = await ChargingStationStorage.getChargingProfile(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, chargingProfile, `Charging Profile ID '${filteredRequest.id}' does not exist.`, MODULE_NAME, 'handleUpdateChargingProfile', req.user); const chargingProfileID = await ChargingStationService.setAndSaveChargingProfile(filteredRequest, action, req); @@ -659,11 +659,11 @@ export default class ChargingStationService { // Check existence const chargingProfileID = ChargingStationValidator.getInstance().validateChargingProfileDeleteReq(req.query).ID; // Get Profile - const chargingProfile = await ChargingStationStorage.getChargingProfile(req.user.tenantID, chargingProfileID); + const chargingProfile = await ChargingStationStorage.getChargingProfile(req.tenant, chargingProfileID); UtilsService.assertObjectExists(action, chargingProfile, `Charging Profile ID '${chargingProfileID}' does not exist.`, MODULE_NAME, 'handleDeleteChargingProfile', req.user); // Get Charging Station - const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, chargingProfile.chargingStationID); + const chargingStation = await ChargingStationStorage.getChargingStation(req.tenant, chargingProfile.chargingStationID); UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${chargingProfile.chargingStationID}' does not exist.`, MODULE_NAME, 'handleDeleteChargingProfile', req.user); // OCPI Charging Station @@ -681,7 +681,7 @@ export default class ChargingStationService { let siteID = null; if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION)) { // Get the Site Area - const siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, chargingStation.siteAreaID); + const siteArea = await SiteAreaStorage.getSiteArea(req.tenant, chargingStation.siteAreaID); siteID = siteArea ? siteArea.siteID : null; } // Check Auth @@ -720,7 +720,7 @@ export default class ChargingStationService { // Check UtilsService.assertIdIsProvided(action, filteredRequest.ChargingStationID, MODULE_NAME, 'handleGetChargingStationOcppParameters', req.user); // Get the Charging Station` - const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, filteredRequest.ChargingStationID); + const chargingStation = await ChargingStationStorage.getChargingStation(req.tenant, filteredRequest.ChargingStationID); // Found? UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${filteredRequest.ChargingStationID}' does not exist`, MODULE_NAME, 'handleGetChargingStationOcppParameters', req.user); @@ -735,7 +735,7 @@ export default class ChargingStationService { }); } // Get the Parameters - const parameters = await ChargingStationStorage.getOcppParameters(req.user.tenantID, chargingStation.id); + const parameters = await ChargingStationStorage.getOcppParameters(req.tenant, chargingStation.id); // Return the result res.json(parameters); next(); @@ -756,7 +756,7 @@ export default class ChargingStationService { }); } // Get the Charging Station - const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, filteredRequest.chargingStationID); + const chargingStation = await ChargingStationStorage.getChargingStation(req.tenant, filteredRequest.chargingStationID); // Found? UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${filteredRequest.chargingStationID}' does not exist`, MODULE_NAME, 'handleRequestChargingStationOcppParameters', req.user); @@ -776,7 +776,7 @@ export default class ChargingStationService { UtilsService.assertIdIsProvided(action, chargingStationID, MODULE_NAME, 'handleDeleteChargingStation', req.user); // Get - const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, chargingStationID); + const chargingStation = await ChargingStationStorage.getChargingStation(req.tenant, chargingStationID); UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${chargingStationID}' does not exist`, MODULE_NAME, 'handleDeleteChargingStation', req.user); // OCPI Charging Station @@ -793,7 +793,7 @@ export default class ChargingStationService { let siteID = null; if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION)) { // Get the Site Area - const siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, chargingStation.siteAreaID); + const siteArea = await SiteAreaStorage.getSiteArea(req.tenant, chargingStation.siteAreaID); siteID = siteArea ? siteArea.siteID : null; } // Check auth @@ -820,7 +820,7 @@ export default class ChargingStationService { } for (const connector of chargingStation.connectors) { if (connector && connector.currentTransactionID) { - const transaction = await TransactionStorage.getTransaction(req.user.tenantID, connector.currentTransactionID); + const transaction = await TransactionStorage.getTransaction(req.tenant, connector.currentTransactionID); if (transaction && !transaction.stop) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -873,16 +873,16 @@ export default class ChargingStationService { // Set as deleted chargingStation.deleted = true; // Check if charging station has had transactions - const transactions = await TransactionStorage.getTransactions(req.user.tenantID, + const transactions = await TransactionStorage.getTransactions(req.tenant, { chargeBoxIDs: [chargingStation.id] }, Constants.DB_PARAMS_SINGLE_RECORD, ['id']); if (!Utils.isEmptyArray(transactions.result)) { // Delete logically - await ChargingStationStorage.saveChargingStation(req.tenant.id, chargingStation); + await ChargingStationStorage.saveChargingStation(req.tenant, chargingStation); // Delete Charging Profiles - await ChargingStationStorage.deleteChargingProfiles(req.tenant.id, chargingStation.id); + await ChargingStationStorage.deleteChargingProfiles(req.tenant, chargingStation.id); } else { // Delete physically - await ChargingStationStorage.deleteChargingStation(req.user.tenantID, chargingStation.id); + await ChargingStationStorage.deleteChargingStation(req.tenant, chargingStation.id); } await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -946,7 +946,7 @@ export default class ChargingStationService { res.attachment('exported-ocpp-params.csv'); let writeHeader = true; for (const chargingStation of chargingStations.result) { - const ocppParameters = await ChargingStationStorage.getOcppParameters(req.user.tenantID, chargingStation.id); + const ocppParameters = await ChargingStationStorage.getOcppParameters(req.tenant, chargingStation.id); // Get OCPP Params const dataToExport = ChargingStationService.convertOCPPParamsToCSV({ params: ocppParameters.result, @@ -1027,7 +1027,7 @@ export default class ChargingStationService { projectFields = projectFields.filter((projectField) => projectHttpFields.includes(projectField)); } // Get Charging Stations - const chargingStations = await ChargingStationStorage.getChargingStationsInError(req.user.tenantID, + const chargingStations = await ChargingStationStorage.getChargingStationsInError(req.tenant, { search: filteredRequest.Search, siteIDs: Authorizations.getAuthorizedSiteIDs(req.user, filteredRequest.SiteID ? filteredRequest.SiteID.split('|') : null), @@ -1267,7 +1267,7 @@ export default class ChargingStationService { return { count: 0, result: [] }; } // Get Charging Stations - const chargingStations = await ChargingStationStorage.getChargingStations(req.user.tenantID, + const chargingStations = await ChargingStationStorage.getChargingStations(req.tenant, { search: filteredRequest.Search, withNoSiteArea: filteredRequest.WithNoSiteArea, @@ -1508,7 +1508,7 @@ export default class ChargingStationService { // Custom param? if (params.custom) { // Get OCPP Parameters from DB - const ocppParametersFromDB = await ChargingStationStorage.getOcppParameters(tenant.id, chargingStation.id); + const ocppParametersFromDB = await ChargingStationStorage.getOcppParameters(tenant, chargingStation.id); // Set new structure const chargingStationOcppParameters: ChargingStationOcppParameters = { id: chargingStation.id, @@ -1523,7 +1523,7 @@ export default class ChargingStationService { // Save config if (foundOcppParam.custom) { // Save - await ChargingStationStorage.saveOcppParameters(tenant.id, chargingStationOcppParameters); + await ChargingStationStorage.saveOcppParameters(tenant, chargingStationOcppParameters); } else { // Not a custom param: refresh the whole OCPP Parameters await OCPPUtils.requestAndSaveChargingStationOcppParameters(tenant, chargingStation); @@ -1532,7 +1532,7 @@ export default class ChargingStationService { // Add custom param chargingStationOcppParameters.configuration.push(params); // Save - await ChargingStationStorage.saveOcppParameters(tenant.id, chargingStationOcppParameters); + await ChargingStationStorage.saveOcppParameters(tenant, chargingStationOcppParameters); } } else { // Refresh the whole OCPP Parameters @@ -1640,7 +1640,7 @@ export default class ChargingStationService { private static async setAndSaveChargingProfile(filteredRequest: ChargingProfile, action: ServerAction, req: Request): Promise { // Check existence - const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, filteredRequest.chargingStationID); + const chargingStation = await ChargingStationStorage.getChargingStation(req.tenant, filteredRequest.chargingStationID); UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${filteredRequest.chargingStationID}' does not exist.`, MODULE_NAME, 'setAndSaveChargingProfile', req.user); // OCPI Charging Station @@ -1662,7 +1662,7 @@ export default class ChargingStationService { let siteID = null; if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION)) { // Get the Site Area - const siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, chargingStation.siteAreaID); + const siteArea = await SiteAreaStorage.getSiteArea(req.tenant, chargingStation.siteAreaID); siteID = siteArea ? siteArea.siteID : null; } // Check Auth @@ -1802,7 +1802,7 @@ export default class ChargingStationService { if (Utils.isComponentActiveFromToken(req.user, TenantComponents.CAR)) { if (result?.status === OCPPRemoteStartStopStatus.ACCEPTED) { if (filteredRequest.carID && filteredRequest.carID !== user.lastSelectedCarID) { - await UserStorage.saveUserLastSelectedCarID(req.user.tenantID, user.id, filteredRequest.carID); + await UserStorage.saveUserLastSelectedCarID(req.tenant, user.id, filteredRequest.carID); } } } @@ -1825,7 +1825,7 @@ export default class ChargingStationService { } // Get Transaction const transaction = await TransactionStorage.getTransaction( - req.user.tenantID, filteredRequest.args.transactionId, { withUser: true }); + req.tenant, filteredRequest.args.transactionId, { withUser: true }); UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.args.transactionId as string}' does not exist`, MODULE_NAME, 'handleAction', req.user); // Add connector ID @@ -1855,7 +1855,7 @@ export default class ChargingStationService { userID: req.user.id }; // Save Transaction - await TransactionStorage.saveTransaction(req.user.tenantID, transaction); + await TransactionStorage.saveTransaction(req.tenant, transaction); // Ok: Execute it return ChargingStationService.executeChargingStationCommand( req.tenant, req.user, chargingStation, action, command, filteredRequest.args); diff --git a/src/server/rest/v1/service/LoggingService.ts b/src/server/rest/v1/service/LoggingService.ts index fa7a4bda92..f6c7e86399 100644 --- a/src/server/rest/v1/service/LoggingService.ts +++ b/src/server/rest/v1/service/LoggingService.ts @@ -105,7 +105,7 @@ export default class LoggingService { // Add filter for Site Admins if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION) && Authorizations.isSiteAdmin(req.user)) { // Optimization: Retrieve Charging Stations to get the logs only for the Site Admin user - const chargingStations = await ChargingStationStorage.getChargingStations(req.user.tenantID, + const chargingStations = await ChargingStationStorage.getChargingStations(req.tenant, { siteIDs: req.user.sitesAdmin, withSiteArea: true }, Constants.DB_PARAMS_MAX_LIMIT); // Check if Charging Station is already filtered if (chargingStations.count === 0) { diff --git a/src/server/rest/v1/service/NotificationService.ts b/src/server/rest/v1/service/NotificationService.ts index 12c7c36160..317ff72df1 100644 --- a/src/server/rest/v1/service/NotificationService.ts +++ b/src/server/rest/v1/service/NotificationService.ts @@ -69,7 +69,7 @@ export default class NotificationService { // Save mobile number if (filteredRequest.mobile && user.mobile !== filteredRequest.mobile) { user.mobile = filteredRequest.mobile; - await UserStorage.saveUserMobilePhone(req.user.tenantID, user.id, { mobile: filteredRequest.mobile }); + await UserStorage.saveUserMobilePhone(req.tenant, user.id, { mobile: filteredRequest.mobile }); } // Set const endUserErrorNotification: EndUserErrorNotification = { diff --git a/src/server/rest/v1/service/RegistrationTokenService.ts b/src/server/rest/v1/service/RegistrationTokenService.ts index 527bbd18f3..4865b83e01 100644 --- a/src/server/rest/v1/service/RegistrationTokenService.ts +++ b/src/server/rest/v1/service/RegistrationTokenService.ts @@ -27,7 +27,7 @@ export default class RegistrationTokenService { // Check Auth if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION) && filteredRequest.siteAreaID) { // Get the Site Area - const siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, filteredRequest.siteAreaID); + const siteArea = await SiteAreaStorage.getSiteArea(req.tenant, filteredRequest.siteAreaID); UtilsService.assertObjectExists(action, siteArea, `Site Area ID '${filteredRequest.siteAreaID}' does not exist`, MODULE_NAME, 'handleCreateRegistrationToken', req.user); if (!await Authorizations.canCreateRegistrationToken(req.user, siteArea.siteID)) { @@ -79,7 +79,7 @@ export default class RegistrationTokenService { createdOn: new Date() }; // Save - registrationToken.id = await RegistrationTokenStorage.saveRegistrationToken(req.user.tenantID, registrationToken); + registrationToken.id = await RegistrationTokenStorage.saveRegistrationToken(req.tenant, registrationToken); // Build OCPP URLs registrationToken.ocpp15SOAPUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_15, OCPPProtocol.SOAP, registrationToken.id); registrationToken.ocpp16SOAPUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_16, OCPPProtocol.SOAP, registrationToken.id); @@ -107,13 +107,13 @@ export default class RegistrationTokenService { }); } // Get Token - const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.user.tenantID, filteredRequest.id); + const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, registrationToken, `Token ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handleUpdateRegistrationToken', req.user); if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION)) { // Check Site Area if it's provided if (filteredRequest.siteAreaID) { - const siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, filteredRequest.siteAreaID); + const siteArea = await SiteAreaStorage.getSiteArea(req.tenant, filteredRequest.siteAreaID); UtilsService.assertObjectExists(action, siteArea, `Site Area ID '${filteredRequest.siteAreaID}' does not exist`, MODULE_NAME, 'handleUpdateRegistrationToken', req.user); } @@ -136,7 +136,7 @@ export default class RegistrationTokenService { registrationToken.lastChangedOn = new Date(); registrationToken.revocationDate = null; // Save - registrationToken.id = await RegistrationTokenStorage.saveRegistrationToken(req.user.tenantID, registrationToken); + registrationToken.id = await RegistrationTokenStorage.saveRegistrationToken(req.tenant, registrationToken); // Build OCPP URLs registrationToken.ocpp15SOAPUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_15, OCPPProtocol.SOAP, registrationToken.id); registrationToken.ocpp16SOAPUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_16, OCPPProtocol.SOAP, registrationToken.id); @@ -153,7 +153,7 @@ export default class RegistrationTokenService { const tokenID = RegistrationTokenSecurity.filterRegistrationTokenByIDRequest(req.query); UtilsService.assertIdIsProvided(action, tokenID, MODULE_NAME, 'handleDeleteRegistrationToken', req.user); // Get Token - const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.user.tenantID, tokenID); + const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.tenant, tokenID); UtilsService.assertObjectExists(action, registrationToken, `Registration Token ID '${tokenID}' does not exist`, MODULE_NAME, 'handleDeleteRegistrationToken', req.user); // Check auth @@ -167,7 +167,7 @@ export default class RegistrationTokenService { }); } // Delete - await RegistrationTokenStorage.deleteRegistrationToken(req.user.tenantID, tokenID); + await RegistrationTokenStorage.deleteRegistrationToken(req.tenant, tokenID); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -185,7 +185,7 @@ export default class RegistrationTokenService { const tokenID = RegistrationTokenSecurity.filterRegistrationTokenByIDRequest(req.query); UtilsService.assertIdIsProvided(action, tokenID, MODULE_NAME, 'handleDeleteRegistrationToken', req.user); // Get Token - const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.user.tenantID, tokenID); + const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.tenant, tokenID); UtilsService.assertObjectExists(action, registrationToken, `Registration Token ID '${tokenID}' does not exist`, MODULE_NAME, 'handleRevokeRegistrationToken', req.user); // Check auth @@ -212,7 +212,7 @@ export default class RegistrationTokenService { registrationToken.revocationDate = new Date(); registrationToken.lastChangedBy = { 'id': req.user.id }; registrationToken.lastChangedOn = new Date(); - await RegistrationTokenStorage.saveRegistrationToken(req.user.tenantID, registrationToken); + await RegistrationTokenStorage.saveRegistrationToken(req.tenant, registrationToken); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -244,7 +244,7 @@ export default class RegistrationTokenService { userProject = [ 'createdBy.name', 'createdBy.firstName', 'lastChangedBy.name', 'lastChangedBy.firstName' ]; } // Get the tokens - const registrationTokens = await RegistrationTokenStorage.getRegistrationTokens(req.user.tenantID, + const registrationTokens = await RegistrationTokenStorage.getRegistrationTokens(req.tenant, { siteAreaID: filteredRequest.SiteAreaID, siteIDs: Authorizations.getAuthorizedSiteAdminIDs(req.user, null), @@ -283,7 +283,7 @@ export default class RegistrationTokenService { userProject = [ 'createdBy.name', 'createdBy.firstName', 'lastChangedBy.name', 'lastChangedBy.firstName' ]; } // Get the token - const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.user.tenantID, + const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.tenant, filteredRequest, [ 'id', 'status', 'description', 'createdOn', 'lastChangedOn', 'expirationDate', 'revocationDate', diff --git a/src/server/rest/v1/service/SessionHashService.ts b/src/server/rest/v1/service/SessionHashService.ts index 0ab463ae91..06bf43c259 100644 --- a/src/server/rest/v1/service/SessionHashService.ts +++ b/src/server/rest/v1/service/SessionHashService.ts @@ -30,7 +30,7 @@ export default class SessionHashService { tenant = await TenantStorage.getTenant(tenantID); } // Get User - const user = await UserStorage.getUser(tenantID, userID); + const user = await UserStorage.getUser(tenant, userID); // User or Tenant no longer exists if (!tenant || !user) { return true; diff --git a/src/server/rest/v1/service/SiteAreaService.ts b/src/server/rest/v1/service/SiteAreaService.ts index a0818a5dcb..35f104b1e6 100644 --- a/src/server/rest/v1/service/SiteAreaService.ts +++ b/src/server/rest/v1/service/SiteAreaService.ts @@ -20,6 +20,7 @@ import SiteAreaSecurity from './security/SiteAreaSecurity'; import SiteAreaStorage from '../../../../storage/mongodb/SiteAreaStorage'; import SmartChargingFactory from '../../../../integration/smart-charging/SmartChargingFactory'; import TenantComponents from '../../../../types/TenantComponents'; +import TenantStorage from '../../../../storage/mongodb/TenantStorage'; import Utils from '../../../../utils/Utils'; import UtilsService from './UtilsService'; import moment from 'moment'; @@ -42,9 +43,9 @@ export default class SiteAreaService { req.tenant, req.user, siteArea, filteredRequest.assetIDs, action); // Save if (action === ServerAction.ADD_ASSET_TO_SITE_AREA) { - await SiteAreaStorage.addAssetsToSiteArea(req.user.tenantID, siteArea, assets.map((asset) => asset.id)); + await SiteAreaStorage.addAssetsToSiteArea(req.tenant, siteArea, assets.map((asset) => asset.id)); } else { - await SiteAreaStorage.removeAssetsFromSiteArea(req.user.tenantID, filteredRequest.siteAreaID, assets.map((asset) => asset.id)); + await SiteAreaStorage.removeAssetsFromSiteArea(req.tenant, filteredRequest.siteAreaID, assets.map((asset) => asset.id)); } // Log await Logging.logSecurityInfo({ @@ -95,10 +96,10 @@ export default class SiteAreaService { // Save if (action === ServerAction.ADD_CHARGING_STATIONS_TO_SITE_AREA) { await SiteAreaStorage.addChargingStationsToSiteArea( - req.user.tenantID, siteArea, chargingStations.map((chargingStation) => chargingStation.id)); + req.tenant, siteArea, chargingStations.map((chargingStation) => chargingStation.id)); } else { await SiteAreaStorage.removeChargingStationsFromSiteArea( - req.user.tenantID, filteredRequest.siteAreaID, chargingStations.map((chargingStation) => chargingStation.id)); + req.tenant, filteredRequest.siteAreaID, chargingStations.map((chargingStation) => chargingStation.id)); } // Log await Logging.logSecurityInfo({ @@ -124,7 +125,7 @@ export default class SiteAreaService { const siteArea = await UtilsService.checkAndGetSiteAreaAuthorization( req.tenant, req.user, siteAreaID, Action.DELETE, action); // Delete - await SiteAreaStorage.deleteSiteArea(req.user.tenantID, siteArea.id); + await SiteAreaStorage.deleteSiteArea(req.tenant, siteArea.id); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -163,7 +164,8 @@ export default class SiteAreaService { // Check mandatory fields UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetSiteAreaImage', req.user); // Get it - const siteAreaImage = await SiteAreaStorage.getSiteAreaImage(filteredRequest.TenantID, filteredRequest.ID); + const siteAreaImage = await SiteAreaStorage.getSiteAreaImage( + await TenantStorage.getTenant(filteredRequest.TenantID), filteredRequest.ID); // Return if (siteAreaImage?.image) { let header = 'image'; @@ -196,7 +198,7 @@ export default class SiteAreaService { return; } // Get the SiteAreas - const siteAreas = await SiteAreaStorage.getSiteAreas(req.user.tenantID, + const siteAreas = await SiteAreaStorage.getSiteAreas(req.tenant, { issuer: filteredRequest.Issuer, search: filteredRequest.Search, @@ -300,7 +302,7 @@ export default class SiteAreaService { createdOn: new Date() } as SiteArea; // Save - newSiteArea.id = await SiteAreaStorage.saveSiteArea(req.user.tenantID, newSiteArea, true); + newSiteArea.id = await SiteAreaStorage.saveSiteArea(req.tenant, newSiteArea, true); await Logging.logSecurityInfo({ tenantID: req.user.tenantID, user: req.user, module: MODULE_NAME, method: 'handleCreateSiteArea', @@ -367,7 +369,7 @@ export default class SiteAreaService { siteArea.lastChangedBy = { 'id': req.user.id }; siteArea.lastChangedOn = new Date(); // Save - await SiteAreaStorage.saveSiteArea(req.user.tenantID, siteArea, Utils.objectHasProperty(filteredRequest, 'image')); + await SiteAreaStorage.saveSiteArea(req.tenant, siteArea, Utils.objectHasProperty(filteredRequest, 'image')); // Retrigger Smart Charging if (filteredRequest.smartCharging) { // FIXME: the lock acquisition can wait for 30s before timeout and the whole code execution timeout at 3s diff --git a/src/server/rest/v1/service/StatisticService.ts b/src/server/rest/v1/service/StatisticService.ts index 6a92f53c59..dcd08247af 100644 --- a/src/server/rest/v1/service/StatisticService.ts +++ b/src/server/rest/v1/service/StatisticService.ts @@ -39,7 +39,7 @@ export default class StatisticService { const filter = StatisticService.buildFilter(filteredRequest, req.user); // Get Stats const transactionStats = await StatisticsStorage.getChargingStationStats( - req.user.tenantID, filter, StatsGroupBy.CONSUMPTION); + req.tenant, filter, StatsGroupBy.CONSUMPTION); // Convert const transactions = StatisticService.convertToGraphData( transactionStats, StatsDataCategory.CHARGING_STATION); @@ -69,7 +69,7 @@ export default class StatisticService { const filter = StatisticService.buildFilter(filteredRequest, req.user); // Get Stats const transactionStats = await StatisticsStorage.getChargingStationStats( - req.user.tenantID, filter, StatsGroupBy.USAGE); + req.tenant, filter, StatsGroupBy.USAGE); // Convert const transactions = StatisticService.convertToGraphData( transactionStats, StatsDataCategory.CHARGING_STATION); @@ -99,7 +99,7 @@ export default class StatisticService { const filter = StatisticService.buildFilter(filteredRequest, req.user); // Get Stats const transactionStats = await StatisticsStorage.getChargingStationStats( - req.user.tenantID, filter, StatsGroupBy.INACTIVITY); + req.tenant, filter, StatsGroupBy.INACTIVITY); // Convert const transactions = StatisticService.convertToGraphData( transactionStats, StatsDataCategory.CHARGING_STATION); @@ -129,7 +129,7 @@ export default class StatisticService { const filter = StatisticService.buildFilter(filteredRequest, req.user); // Get Stats const transactionStats = await StatisticsStorage.getChargingStationStats( - req.user.tenantID, filter, StatsGroupBy.TRANSACTIONS); + req.tenant, filter, StatsGroupBy.TRANSACTIONS); // Convert const transactions = StatisticService.convertToGraphData( transactionStats, StatsDataCategory.CHARGING_STATION); @@ -159,7 +159,7 @@ export default class StatisticService { const filter = StatisticService.buildFilter(filteredRequest, req.user); // Get Stats const transactionStats = await StatisticsStorage.getChargingStationStats( - req.user.tenantID, filter, StatsGroupBy.PRICING); + req.tenant, filter, StatsGroupBy.PRICING); // Convert const transactions = StatisticService.convertToGraphData( transactionStats, StatsDataCategory.CHARGING_STATION); @@ -189,7 +189,7 @@ export default class StatisticService { const filter = StatisticService.buildFilter(filteredRequest, req.user); // Get Stats const transactionStats = await StatisticsStorage.getUserStats( - req.user.tenantID, filter, StatsGroupBy.CONSUMPTION); + req.tenant, filter, StatsGroupBy.CONSUMPTION); // Convert const transactions = StatisticService.convertToGraphData( transactionStats, StatsDataCategory.USER); @@ -219,7 +219,7 @@ export default class StatisticService { const filter = StatisticService.buildFilter(filteredRequest, req.user); // Get Stats const transactionStats = await StatisticsStorage.getUserStats( - req.user.tenantID, filter, StatsGroupBy.USAGE); + req.tenant, filter, StatsGroupBy.USAGE); // Convert const transactions = StatisticService.convertToGraphData( transactionStats, StatsDataCategory.USER); @@ -249,7 +249,7 @@ export default class StatisticService { const filter = StatisticService.buildFilter(filteredRequest, req.user); // Get Stats const transactionStats = await StatisticsStorage.getUserStats( - req.user.tenantID, filter, StatsGroupBy.INACTIVITY); + req.tenant, filter, StatsGroupBy.INACTIVITY); // Convert const transactions = StatisticService.convertToGraphData( transactionStats, StatsDataCategory.USER); @@ -279,7 +279,7 @@ export default class StatisticService { const filter = StatisticService.buildFilter(filteredRequest, req.user); // Get Stats const transactionStats = await StatisticsStorage.getUserStats( - req.user.tenantID, filter, StatsGroupBy.TRANSACTIONS); + req.tenant, filter, StatsGroupBy.TRANSACTIONS); // Convert const transactions = StatisticService.convertToGraphData( transactionStats, StatsDataCategory.USER); @@ -309,7 +309,7 @@ export default class StatisticService { const filter = StatisticService.buildFilter(filteredRequest, req.user); // Get Stats const transactionStats = await StatisticsStorage.getUserStats( - req.user.tenantID, filter, StatsGroupBy.PRICING); + req.tenant, filter, StatsGroupBy.PRICING); // Convert const transactions = StatisticService.convertToGraphData( transactionStats, StatsDataCategory.USER); @@ -361,9 +361,9 @@ export default class StatisticService { // Query data let transactionStats: ChargingStationStats[] | UserStats[]; if (filteredRequest.DataCategory === StatsDataCategory.CHARGING_STATION) { - transactionStats = await StatisticsStorage.getChargingStationStats(req.user.tenantID, filter, groupBy); + transactionStats = await StatisticsStorage.getChargingStationStats(req.tenant, filter, groupBy); } else { - transactionStats = await StatisticsStorage.getUserStats(req.user.tenantID, filter, groupBy); + transactionStats = await StatisticsStorage.getUserStats(req.tenant, filter, groupBy); } // Set the attachement name res.attachment('exported-' + filteredRequest.DataType.toLowerCase() + '-statistics.csv'); diff --git a/src/server/rest/v1/service/TagService.ts b/src/server/rest/v1/service/TagService.ts index 5e00a32ecc..39a3a7ba76 100644 --- a/src/server/rest/v1/service/TagService.ts +++ b/src/server/rest/v1/service/TagService.ts @@ -34,6 +34,7 @@ import TransactionStorage from '../../../../storage/mongodb/TransactionStorage'; import UserToken from '../../../../types/UserToken'; import UserValidator from '../validator/UserValidator'; import Utils from '../../../../utils/Utils'; +import UtilsSecurity from './security/UtilsSecurity'; import UtilsService from './UtilsService'; import csvToJson from 'csvtojson/v2'; @@ -116,7 +117,7 @@ export default class TagService { }); } // Check if Tag has been already used - const transactions = await TransactionStorage.getTransactions(req.user.tenantID, + const transactions = await TransactionStorage.getTransactions(req.tenant, { tagIDs: [filteredRequest.id.toUpperCase()], hasUserID: true }, Constants.DB_PARAMS_SINGLE_RECORD, ['id']); if (!Utils.isEmptyArray(transactions.result)) { throw new AppError({ @@ -336,6 +337,10 @@ export default class TagService { // Set default value tag.importedBy = importedBy; tag.importedOn = importedOn; + tag.importedData = { + 'autoActivateUserAtImport' : UtilsSecurity.filterBoolean(req.headers.autoactivateuseratimport), + 'autoActivateTagAtImport' : UtilsSecurity.filterBoolean(req.headers.autoactivatetagatimport) + }; // Import const importSuccess = await TagService.processTag(action, req, tag, tagsToBeImported); if (!importSuccess) { @@ -622,32 +627,39 @@ export default class TagService { const newImportedTag: ImportedTag = { id: importedTag.id.toUpperCase(), visualID: importedTag.visualID, - description: importedTag.description ? importedTag.description : `Tag ID '${importedTag.id}'` + description: importedTag.description ? importedTag.description : `Tag ID '${importedTag.id}'`, + importedData: importedTag.importedData }; - if (importedTag.name && importedTag.firstName && importedTag.email) { - newImportedTag.name = importedTag.name.toUpperCase(); - newImportedTag.firstName = importedTag.firstName; - newImportedTag.email = importedTag.email; - } // Validate Tag data TagValidator.getInstance().validateImportedTagCreation(newImportedTag); // Set properties newImportedTag.importedBy = importedTag.importedBy; newImportedTag.importedOn = importedTag.importedOn; newImportedTag.status = ImportStatus.READY; - try { - UserValidator.getInstance().validateImportedUserCreation(newImportedTag as ImportedUser); - } catch (error) { - await Logging.logWarning({ - tenantID: req.user.tenantID, - module: MODULE_NAME, method: 'processTag', - action: action, - message: `User cannot be imported tag ${newImportedTag.id}`, - detailedMessages: { tag: newImportedTag, error: error.stack } - }); + let tagToImport = newImportedTag; + // handle user part + if (importedTag.name && importedTag.firstName && importedTag.email) { + const newImportedUser: ImportedUser = { + name: importedTag.name.toUpperCase(), + firstName: importedTag.firstName, + email: importedTag.email, + siteIDs: importedTag.siteIDs + }; + try { + UserValidator.getInstance().validateImportedUserCreation(newImportedUser); + tagToImport = { ...tagToImport, ...newImportedUser as ImportedTag }; + } catch (error) { + await Logging.logWarning({ + tenantID: req.user.tenantID, + module: MODULE_NAME, method: 'processTag', + action: action, + message: `User cannot be imported with tag ${newImportedTag.id}`, + detailedMessages: { tag: newImportedTag, error: error.message, stack: error.stack } + }); + } } // Save it later on - tagsToBeImported.push(newImportedTag); + tagsToBeImported.push(tagToImport); return true; } catch (error) { await Logging.logError({ diff --git a/src/server/rest/v1/service/TenantService.ts b/src/server/rest/v1/service/TenantService.ts index 5f09c29c23..90a72c9cfe 100644 --- a/src/server/rest/v1/service/TenantService.ts +++ b/src/server/rest/v1/service/TenantService.ts @@ -270,25 +270,27 @@ export default class TenantService { tenantUser.name = filteredRequest.name; tenantUser.firstName = 'Admin'; tenantUser.email = filteredRequest.email; + // Get Tenant + const tenant = await TenantStorage.getTenant(filteredRequest.id); // Save User - tenantUser.id = await UserStorage.saveUser(filteredRequest.id, tenantUser); + tenantUser.id = await UserStorage.saveUser(tenant, tenantUser); // Save User Role - await UserStorage.saveUserRole(filteredRequest.id, tenantUser.id, UserRole.ADMIN); + await UserStorage.saveUserRole(tenant, tenantUser.id, UserRole.ADMIN); // Save User Status - await UserStorage.saveUserStatus(filteredRequest.id, tenantUser.id, tenantUser.status); + await UserStorage.saveUserStatus(tenant, tenantUser.id, tenantUser.status); // Save User Account Verification const verificationToken = Utils.generateToken(filteredRequest.email); - await UserStorage.saveUserAccountVerification(filteredRequest.id, tenantUser.id, { verificationToken }); + await UserStorage.saveUserAccountVerification(tenant, tenantUser.id, { verificationToken }); const resetHash = Utils.generateUUID(); // Init Password info - await UserStorage.saveUserPassword(filteredRequest.id, tenantUser.id, { passwordResetHash: resetHash }); + await UserStorage.saveUserPassword(tenant, tenantUser.id, { passwordResetHash: resetHash }); // Send activation link const evseDashboardVerifyEmailURL = Utils.buildEvseURL(filteredRequest.subdomain) + '/verify-email?VerificationToken=' + verificationToken + '&Email=' + tenantUser.email + '&ResetToken=' + resetHash; // Send Register User (Async) NotificationHandler.sendNewRegisteredUser( - await TenantStorage.getTenant(filteredRequest.id), + tenant, Utils.generateUUID(), tenantUser, { @@ -344,7 +346,7 @@ export default class TenantService { if (filteredRequest.components && filteredRequest.components.smartCharging && tenant.components && tenant.components.smartCharging && !filteredRequest.components.smartCharging.active && tenant.components.smartCharging.active) { - const siteAreas = await SiteAreaStorage.getSiteAreas(filteredRequest.id, { smartCharging: true }, Constants.DB_PARAMS_MAX_LIMIT); + const siteAreas = await SiteAreaStorage.getSiteAreas(tenant, { smartCharging: true }, Constants.DB_PARAMS_MAX_LIMIT); if (siteAreas.count !== 0) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -432,17 +434,17 @@ export default class TenantService { // OICP if (tenant.components && tenant.components.oicp) { // Virtual user needed for unknown roaming user - const virtualOICPUser = await UserStorage.getUserByEmail(tenant.id, Constants.OICP_VIRTUAL_USER_EMAIL); + const virtualOICPUser = await UserStorage.getUserByEmail(tenant, Constants.OICP_VIRTUAL_USER_EMAIL); // Activate or deactivate virtual user depending on the oicp component status if (tenant.components.oicp.active) { // Create OICP user if (!virtualOICPUser) { - await OICPUtils.createOICPVirtualUser(tenant.id); + await OICPUtils.createOICPVirtualUser(tenant); } } else if (virtualOICPUser) { // Clean up user if (virtualOICPUser) { - await UserStorage.deleteUser(tenant.id, virtualOICPUser.id); + await UserStorage.deleteUser(tenant, virtualOICPUser.id); } // Delete Endpoints if component is inactive const oicpEndpoints = await OICPEndpointStorage.getOicpEndpoints(tenant, { role: OICPRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); diff --git a/src/server/rest/v1/service/TransactionService.ts b/src/server/rest/v1/service/TransactionService.ts index f2ca21da1c..9a35d5d78e 100644 --- a/src/server/rest/v1/service/TransactionService.ts +++ b/src/server/rest/v1/service/TransactionService.ts @@ -100,7 +100,7 @@ export default class TransactionService { } const transactionsToRefund: Transaction[] = []; for (const transactionId of filteredRequest.transactionIds) { - const transaction = await TransactionStorage.getTransaction(req.user.tenantID, transactionId, { withUser: true }); + const transaction = await TransactionStorage.getTransaction(req.tenant, transactionId, { withUser: true }); if (!transaction) { await Logging.logError({ tenantID: req.user.tenantID, @@ -136,7 +136,7 @@ export default class TransactionService { transactionsToRefund.push(transaction); } // Get Transaction User - const user: User = await UserStorage.getUser(req.user.tenantID, req.user.id); + const user: User = await UserStorage.getUser(req.tenant, req.user.id); UtilsService.assertObjectExists(action, user, `User ID '${req.user.id}' does not exist`, MODULE_NAME, 'handleRefundTransactions', req.user); const refundConnector = await RefundFactory.getRefundImpl(req.tenant); @@ -192,11 +192,11 @@ export default class TransactionService { }); } // Check Transaction - const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.transactionId, { withUser: true, withTag: true }); + const transaction = await TransactionStorage.getTransaction(req.tenant, filteredRequest.transactionId, { withUser: true, withTag: true }); UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.transactionId}' does not exist`, MODULE_NAME, 'handlePushTransactionCdr', req.user); // Check Charging Station - const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, transaction.chargeBoxID); + const chargingStation = await ChargingStationStorage.getChargingStation(req.tenant, transaction.chargeBoxID); UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${transaction.chargeBoxID}' does not exist`, MODULE_NAME, 'handlePushTransactionCdr', req.user); // Check Issuer @@ -238,7 +238,7 @@ export default class TransactionService { // Roaming await OCPPUtils.processTransactionRoaming(req.tenant, transaction, chargingStation, transaction.tag, TransactionAction.END); // Save - await TransactionStorage.saveTransactionOcpiData(req.user.tenantID, transaction.id, transaction.ocpiData); + await TransactionStorage.saveTransactionOcpiData(req.tenant, transaction.id, transaction.ocpiData); // Ok await Logging.logInfo({ tenantID: req.user.tenantID, @@ -274,7 +274,7 @@ export default class TransactionService { // Post CDR await OCPPUtils.processOICPTransaction(req.tenant, transaction, chargingStation, TransactionAction.END); // Save - await TransactionStorage.saveTransactionOicpData(req.user.tenantID, transaction.id, transaction.oicpData); + await TransactionStorage.saveTransactionOicpData(req.tenant, transaction.id, transaction.oicpData); // Ok await Logging.logInfo({ tenantID: req.user.tenantID, @@ -320,7 +320,7 @@ export default class TransactionService { UtilsService.assertObjectExists(action, tag, `Tag ID '${filteredRequest.TagID}' does not exist`, MODULE_NAME, 'handleAssignTransactionsToUser', req.user); // Get unassigned transactions - const count = await TransactionStorage.getUnassignedTransactionsCount(req.user.tenantID, tag.id); + const count = await TransactionStorage.getUnassignedTransactionsCount(req.tenant, tag.id); // Return res.json(count); next(); @@ -340,8 +340,7 @@ export default class TransactionService { const filteredRequest = TransactionSecurity.filterTransactionRequest(req.query); UtilsService.assertIdIsProvided(action, filteredRequest.ID.toString(), MODULE_NAME, 'handleRebuildTransactionConsumptions', req.user); // Get Transaction - const transaction = await TransactionStorage.getTransaction( - req.user.tenantID, filteredRequest.ID, { withUser: true }); + const transaction = await TransactionStorage.getTransaction(req.tenant, filteredRequest.ID, { withUser: true }); UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.ID}' does not exist`, MODULE_NAME, 'handleRebuildTransactionConsumptions', req.user); // Get unassigned transactions @@ -383,7 +382,7 @@ export default class TransactionService { }); } // Get the user - const user: User = await UserStorage.getUser(req.user.tenantID, filteredRequest.UserID); + const user: User = await UserStorage.getUser(req.tenant, filteredRequest.UserID); UtilsService.assertObjectExists(action, user, `User ID '${filteredRequest.UserID}' does not exist`, MODULE_NAME, 'handleAssignTransactionsToUser', req.user); // Get the tag @@ -409,7 +408,7 @@ export default class TransactionService { }); } // Assign - await TransactionStorage.assignTransactionsToUser(req.user.tenantID, user.id, tag.id); + await TransactionStorage.assignTransactionsToUser(req.tenant, user.id, tag.id); res.json(Constants.REST_RESPONSE_SUCCESS); next(); } @@ -428,7 +427,7 @@ export default class TransactionService { }); } // Get - const transaction = await TransactionStorage.getTransaction(req.user.tenantID, transactionId); + const transaction = await TransactionStorage.getTransaction(req.tenant, transactionId); UtilsService.assertObjectExists(action, transaction, `Transaction ID '${transactionId}' does not exist`, MODULE_NAME, 'handleDeleteTransaction', req.user); // Delete @@ -472,11 +471,11 @@ export default class TransactionService { }); } // Get Transaction - const transaction = await TransactionStorage.getTransaction(req.user.tenantID, transactionId); + const transaction = await TransactionStorage.getTransaction(req.tenant, transactionId); UtilsService.assertObjectExists(action, transaction, `Transaction ID ${transactionId} does not exist`, MODULE_NAME, 'handleTransactionSoftStop', req.user); // Get the Charging Station - const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, transaction.chargeBoxID); + const chargingStation = await ChargingStationStorage.getChargingStation(req.tenant, transaction.chargeBoxID); UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${transaction.chargeBoxID}' does not exist`, MODULE_NAME, 'handleTransactionSoftStop', req.user); // Check if already stopped @@ -484,7 +483,7 @@ export default class TransactionService { // Clear Connector OCPPUtils.clearChargingStationConnector(chargingStation, transaction.connectorId); // Save Connectors - await ChargingStationStorage.saveChargingStationConnectors(req.tenant.id, chargingStation.id, chargingStation.connectors); + await ChargingStationStorage.saveChargingStationConnectors(req.tenant, chargingStation.id, chargingStation.connectors); await Logging.logSecurityInfo({ tenantID: req.user.tenantID, source: chargingStation.id, @@ -569,7 +568,7 @@ export default class TransactionService { ]; } // Get Transaction - const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.TransactionId, + const transaction = await TransactionStorage.getTransaction(req.tenant, filteredRequest.TransactionId, { withTag: filteredRequest.WithTag, withCar: filteredRequest.WithCar, withUser: filteredRequest.WithUser }, projectFields); UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.TransactionId}' does not exist`, MODULE_NAME, 'handleGetConsumptionFromTransaction', req.user); @@ -641,7 +640,7 @@ export default class TransactionService { const filteredRequest = TransactionSecurity.filterTransactionRequest(req.query); UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetTransaction', req.user); // Get Transaction - const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.ID, + const transaction = await TransactionStorage.getTransaction(req.tenant, filteredRequest.ID, { withTag: filteredRequest.WithTag, withCar: filteredRequest.WithCar, withUser: filteredRequest.WithUser }, [ 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'tagID', 'tag.visualID', 'tag.description', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID', @@ -700,7 +699,7 @@ export default class TransactionService { public static async handleGetTransactionYears(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { // Get Transactions - const transactionsYears = await TransactionStorage.getTransactionYears(req.user.tenantID); + const transactionsYears = await TransactionStorage.getTransactionYears(req.tenant); const result: any = {}; if (transactionsYears) { result.years = []; @@ -789,7 +788,7 @@ export default class TransactionService { filter.siteAdminIDs = Authorizations.getAuthorizedSiteAdminIDs(req.user); } // Get Reports - const reports = await TransactionStorage.getRefundReports(req.user.tenantID, filter, { + const reports = await TransactionStorage.getRefundReports(req.tenant, filter, { limit: filteredRequest.Limit, skip: filteredRequest.Skip, sort: filteredRequest.SortFields, @@ -854,7 +853,7 @@ export default class TransactionService { const filteredRequest = TransactionSecurity.filterTransactionRequest(req.query); UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleExportTransactionOcpiCdr', req.user); // Get Transaction - const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.ID, {}, ['id', 'ocpiData']); + const transaction = await TransactionStorage.getTransaction(req.tenant, filteredRequest.ID, {}, ['id', 'ocpiData']); UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.ID}' does not exist`, MODULE_NAME, 'handleExportTransactionOcpiCdr', req.user); // Check @@ -911,7 +910,7 @@ export default class TransactionService { // Filter const filteredRequest = TransactionSecurity.filterTransactionsInErrorRequest(req.query); // Site Area - const transactions = await TransactionStorage.getTransactionsInError(req.user.tenantID, + const transactions = await TransactionStorage.getTransactionsInError(req.tenant, { ...filter, search: filteredRequest.Search, issuer: true, @@ -1009,7 +1008,7 @@ export default class TransactionService { const billingImpl = await BillingFactory.getBillingImpl(tenant); for (const transactionID of transactionsIDs) { // Get - const transaction = await TransactionStorage.getTransaction(loggedUser.tenantID, transactionID); + const transaction = await TransactionStorage.getTransaction(await TenantStorage.getTenant(loggedUser.tenantID), transactionID); // Not Found if (!transaction) { result.inError++; @@ -1052,7 +1051,8 @@ export default class TransactionService { const foundConnector = Utils.getConnectorFromID(transaction.chargeBox, transaction.connectorId); if (foundConnector && transaction.id === foundConnector.currentTransactionID) { OCPPUtils.clearChargingStationConnector(transaction.chargeBox, transaction.connectorId); - await ChargingStationStorage.saveChargingStationConnectors(loggedUser.tenantID, transaction.chargeBox.id, transaction.chargeBox.connectors); + await ChargingStationStorage.saveChargingStationConnectors(await TenantStorage.getTenant(loggedUser.tenantID), + transaction.chargeBox.id, transaction.chargeBox.connectors); } // To Delete transactionsIDsToDelete.push(transactionID); @@ -1063,7 +1063,7 @@ export default class TransactionService { } } // Delete All Transactions - result.inSuccess = await TransactionStorage.deleteTransactions(loggedUser.tenantID, transactionsIDsToDelete); + result.inSuccess = await TransactionStorage.deleteTransactions(await TenantStorage.getTenant(loggedUser.tenantID), transactionsIDsToDelete); // Log await Logging.logActionsResponse(loggedUser.tenantID, ServerAction.TRANSACTIONS_DELETE, @@ -1135,7 +1135,7 @@ export default class TransactionService { } } // Get the transactions - const transactions = await TransactionStorage.getTransactions(req.user.tenantID, + const transactions = await TransactionStorage.getTransactions(req.tenant, { ...extrafilters, chargeBoxIDs: filteredRequest.ChargingStationID ? filteredRequest.ChargingStationID.split('|') : null, diff --git a/src/server/rest/v1/service/UserService.ts b/src/server/rest/v1/service/UserService.ts index 1bb1c8d5e2..880cefa393 100644 --- a/src/server/rest/v1/service/UserService.ts +++ b/src/server/rest/v1/service/UserService.ts @@ -39,6 +39,7 @@ import UserStorage from '../../../../storage/mongodb/UserStorage'; import UserToken from '../../../../types/UserToken'; import UserValidator from '../validator/UserValidator'; import Utils from '../../../../utils/Utils'; +import UtilsSecurity from './security/UtilsSecurity'; import UtilsService from './UtilsService'; import _ from 'lodash'; import csvToJson from 'csvtojson/v2'; @@ -102,9 +103,9 @@ export default class UserService { req.tenant, req.user, user, filteredRequest.siteIDs, action); // Save if (action === ServerAction.ADD_SITES_TO_USER) { - await UserStorage.addSitesToUser(req.user.tenantID, filteredRequest.userID, sites.map((site) => site.id)); + await UserStorage.addSitesToUser(req.tenant, filteredRequest.userID, sites.map((site) => site.id)); } else { - await UserStorage.removeSitesFromUser(req.user.tenantID, filteredRequest.userID, sites.map((site) => site.id)); + await UserStorage.removeSitesFromUser(req.tenant, filteredRequest.userID, sites.map((site) => site.id)); } // Log await Logging.logSecurityInfo({ @@ -127,7 +128,7 @@ export default class UserService { // Delete OCPI User if (!user.issuer) { // Delete User - await UserStorage.deleteUser(req.user.tenantID, user.id); + await UserStorage.deleteUser(req.tenant, user.id); await Logging.logSecurityInfo({ tenantID: req.user.tenantID, user: req.user, actionOnUser: user, @@ -146,7 +147,7 @@ export default class UserService { // Delete Car await UserService.checkAndDeleteCar(req.tenant, req.user, user); // Delete User - await UserStorage.deleteUser(req.user.tenantID, user.id); + await UserStorage.deleteUser(req.tenant, user.id); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -167,7 +168,7 @@ export default class UserService { let user = await UtilsService.checkAndGetUserAuthorization( req.tenant, req.user, filteredRequest.id, Action.UPDATE, action, filteredRequest); // Check email already exists - const userWithEmail = await UserStorage.getUserByEmail(req.user.tenantID, filteredRequest.email); + const userWithEmail = await UserStorage.getUserByEmail(req.tenant, filteredRequest.email); if (userWithEmail && user.id !== userWithEmail.id) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -197,12 +198,12 @@ export default class UserService { lastChangedOn: lastChangedOn, }; // Update User (override TagIDs because it's not of the same type as in filteredRequest) - await UserStorage.saveUser(req.user.tenantID, user, true); + await UserStorage.saveUser(req.tenant, user, true); // Save User's password if (filteredRequest.password) { // Update the password const newPasswordHashed = await Utils.hashPasswordBcrypt(filteredRequest.password); - await UserStorage.saveUserPassword(req.user.tenantID, filteredRequest.id, + await UserStorage.saveUserPassword(req.tenant, filteredRequest.id, { password: newPasswordHashed, passwordWrongNbrTrials: 0, @@ -214,17 +215,17 @@ export default class UserService { if (Authorizations.isAdmin(req.user) || Authorizations.isSuperAdmin(req.user)) { // Save User's Status if (filteredRequest.status) { - await UserStorage.saveUserStatus(req.user.tenantID, user.id, filteredRequest.status); + await UserStorage.saveUserStatus(req.tenant, user.id, filteredRequest.status); } // Save User's Role if (filteredRequest.role) { - await UserStorage.saveUserRole(req.user.tenantID, user.id, filteredRequest.role); + await UserStorage.saveUserRole(req.tenant, user.id, filteredRequest.role); } // Save Admin Data if (Utils.objectHasProperty(filteredRequest, 'plateID')) { const adminData: { plateID?: string; } = {}; adminData.plateID = filteredRequest.plateID || null; - await UserStorage.saveUserAdminData(req.user.tenantID, user.id, adminData); + await UserStorage.saveUserAdminData(req.tenant, user.id, adminData); } } // Update Billing @@ -272,7 +273,7 @@ export default class UserService { const user = await UtilsService.checkAndGetUserAuthorization( req.tenant, req.user, filteredRequest.id, Action.UPDATE, action); // Update User (override TagIDs because it's not of the same type as in filteredRequest) - await UserStorage.saveUserMobileToken(req.user.tenantID, user.id, { + await UserStorage.saveUserMobileToken(req.tenant, user.id, { mobileToken: filteredRequest.mobileToken, mobileOs: filteredRequest.mobileOS, mobileLastChangedOn: new Date() @@ -310,16 +311,14 @@ 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.READ, action); + req.tenant, req.user, userID, Action.READ, action, null, null, null, false); // Get the user image - const userImage = await UserStorage.getUserImage(req.user.tenantID, user.id); + const userImage = await UserStorage.getUserImage(req.tenant, user.id); res.json(userImage); next(); } public static async handleExportUsers(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { - // Export with tags - req.query['WithTag'] = 'true'; await UtilsService.exportToCSV(req, res, 'exported-users.csv', UserService.getUsers.bind(this), UserService.convertToCSV.bind(this)); @@ -346,7 +345,7 @@ export default class UserService { return; } // Get Sites - const sites = await UserStorage.getUserSites(req.user.tenantID, + const sites = await UserStorage.getUserSites(req.tenant, { search: filteredRequest.Search, userIDs: [filteredRequest.UserID], @@ -384,7 +383,7 @@ export default class UserService { const authorizationUserInErrorFilters = await AuthorizationService.checkAndGetUsersInErrorAuthorizations( req.tenant, req.user, filteredRequest); // Get users - const users = await UserStorage.getUsersInError(req.user.tenantID, + const users = await UserStorage.getUsersInError(req.tenant, { search: filteredRequest.Search, roles: (filteredRequest.Role ? filteredRequest.Role.split('|') : null), @@ -439,7 +438,7 @@ export default class UserService { }; try { // Delete all previously imported users - await UserStorage.deleteImportedUsers(req.user.tenantID); + await UserStorage.deleteImportedUsers(req.tenant); // Get the stream const busboy = new Busboy({ headers: req.headers }); req.pipe(busboy); @@ -483,6 +482,9 @@ export default class UserService { // Set default value user.importedBy = importedBy; user.importedOn = importedOn; + user.importedData = { + 'autoActivateUserAtImport' : UtilsSecurity.filterBoolean(req.headers.autoactivateuseratimport) + }; // Import const importSuccess = await UserService.processUser(action, req, user, usersToBeImported); if (!importSuccess) { @@ -490,7 +492,7 @@ export default class UserService { } // 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); + await UserService.insertUsers(req.tenant, req.user, action, usersToBeImported, result); } // eslint-disable-next-line @typescript-eslint/no-misused-promises }, async (error: CSVError) => { @@ -517,7 +519,7 @@ export default class UserService { connectionClosed = true; // Insert batched if (usersToBeImported.length > 0) { - await UserService.insertUsers(req.user.tenantID, req.user, action, usersToBeImported, result); + await UserService.insertUsers(req.tenant, req.user, action, usersToBeImported, result); } // Release the lock await LockingManager.release(importUsersLock); @@ -562,7 +564,7 @@ export default class UserService { } // Insert batched if ((usersToBeImported.length % Constants.IMPORT_BATCH_INSERT_SIZE) === 0) { - await UserService.insertUsers(req.user.tenantID, req.user, action, usersToBeImported, result); + await UserService.insertUsers(req.tenant, req.user, action, usersToBeImported, result); } }); // eslint-disable-next-line @typescript-eslint/no-misused-promises @@ -627,7 +629,7 @@ export default class UserService { }); } // Get the email - const foundUser = await UserStorage.getUserByEmail(req.user.tenantID, filteredRequest.email); + const foundUser = await UserStorage.getUserByEmail(req.tenant, filteredRequest.email); if (foundUser) { throw new AppError({ source: Constants.CENTRAL_SERVER, @@ -648,11 +650,11 @@ export default class UserService { issuer: true, } as User; // Create the User - newUser.id = await UserStorage.saveUser(req.user.tenantID, newUser, true); + newUser.id = await UserStorage.saveUser(req.tenant, newUser, true); // Save password if (newUser.password) { const newPasswordHashed = await Utils.hashPasswordBcrypt(newUser.password); - await UserStorage.saveUserPassword(req.user.tenantID, newUser.id, + await UserStorage.saveUserPassword(req.tenant, newUser.id, { password: newPasswordHashed, passwordWrongNbrTrials: 0, @@ -664,11 +666,11 @@ export default class UserService { if (Authorizations.isAdmin(req.user) || Authorizations.isSuperAdmin(req.user)) { // Save User Status if (newUser.status) { - await UserStorage.saveUserStatus(req.user.tenantID, newUser.id, newUser.status); + await UserStorage.saveUserStatus(req.tenant, newUser.id, newUser.status); } // Save User Role if (newUser.role) { - await UserStorage.saveUserRole(req.user.tenantID, newUser.id, newUser.role); + await UserStorage.saveUserRole(req.tenant, newUser.id, newUser.role); } // Save Admin Data if (newUser.plateID || Utils.objectHasProperty(newUser, 'notificationsActive')) { @@ -683,7 +685,7 @@ export default class UserService { } } // Save User Admin data - await UserStorage.saveUserAdminData(req.user.tenantID, newUser.id, adminData); + await UserStorage.saveUserAdminData(req.tenant, newUser.id, adminData); } } // Assign user to all sites with auto-assign flag set @@ -693,7 +695,7 @@ export default class UserService { ); if (!Utils.isEmptyArray(sites.result)) { const siteIDs = sites.result.map((site) => site.id); - await UserStorage.addSitesToUser(req.user.tenantID, newUser.id, siteIDs); + await UserStorage.addSitesToUser(req.tenant, newUser.id, siteIDs); } // Update Billing await UserService.updateUserBilling(ServerAction.USER_CREATE, req.tenant, req.user, newUser); @@ -709,16 +711,16 @@ export default class UserService { next(); } - private static async insertUsers(tenantID: string, user: UserToken, action: ServerAction, usersToBeImported: ImportedUser[], result: ActionsResponse): Promise { + private static async insertUsers(tenant: Tenant, user: UserToken, action: ServerAction, usersToBeImported: ImportedUser[], result: ActionsResponse): Promise { try { - const nbrInsertedUsers = await UserStorage.saveImportedUsers(tenantID, usersToBeImported); + const nbrInsertedUsers = await UserStorage.saveImportedUsers(tenant, usersToBeImported); result.inSuccess += nbrInsertedUsers; } catch (error) { // Handle dup keys result.inSuccess += error.result.nInserted; result.inError += error.writeErrors.length; await Logging.logError({ - tenantID: tenantID, + tenantID: tenant.id, module: MODULE_NAME, method: 'insertUsers', action: action, user: user.id, @@ -733,7 +735,7 @@ export default class UserService { let headers = null; // Header if (writeHeader) { - const headerArray = [ + headers = [ 'id', 'name', 'firstName', @@ -744,11 +746,10 @@ export default class UserService { 'eulaAcceptedOn', 'createdOn', 'changedOn', - 'changedBy' - ]; - headers = headerArray.join(Constants.CSV_SEPARATOR); + 'changedBy', + ].join(Constants.CSV_SEPARATOR); } - // Conten t + // Content const rows = users.map((user) => { const row = [ user.id, @@ -786,7 +787,7 @@ export default class UserService { } } // Get users - const users = await UserStorage.getUsers(req.user.tenantID, + const users = await UserStorage.getUsers(req.tenant, { search: filteredRequest.Search, issuer: Utils.isBoolean(filteredRequest.Issuer) || filteredRequest.Issuer ? Utils.convertToBoolean(filteredRequest.Issuer) : null, @@ -817,6 +818,8 @@ export default class UserService { name: importedUser.name.toUpperCase(), firstName: importedUser.firstName, email: importedUser.email, + importedData: importedUser.importedData, + siteIDs: importedUser.siteIDs }; // Validate User data UserValidator.getInstance().validateImportedUserCreation(newImportedUser); diff --git a/src/server/rest/v1/service/UtilsService.ts b/src/server/rest/v1/service/UtilsService.ts index 71df78db30..d8ac48a414 100644 --- a/src/server/rest/v1/service/UtilsService.ts +++ b/src/server/rest/v1/service/UtilsService.ts @@ -72,7 +72,7 @@ export default class UtilsService { }); } // Get ChargingStation - const chargingStation = await ChargingStationStorage.getChargingStation(tenant.id, chargingStationID, + const chargingStation = await ChargingStationStorage.getChargingStation(tenant, chargingStationID, { ...additionalFilters, ...authorizationFilter.filters @@ -176,7 +176,7 @@ export default class UtilsService { }); } // Get User - const user = await UserStorage.getUser(tenant.id, userID, + const user = await UserStorage.getUser(tenant, userID, { ...additionalFilters, ...authorizationFilter.filters @@ -354,7 +354,7 @@ export default class UtilsService { }); } // Get Users - let users = (await UserStorage.getUsers(tenant.id, + let users = (await UserStorage.getUsers(tenant, { userIDs, ...additionalFilters, @@ -478,7 +478,7 @@ export default class UtilsService { }); } // Get Charging Stations - const chargingStations = (await ChargingStationStorage.getChargingStations(tenant.id, + const chargingStations = (await ChargingStationStorage.getChargingStations(tenant, { chargingStationIDs, ...additionalFilters, @@ -530,7 +530,7 @@ export default class UtilsService { }); } // Get SiteArea & check it exists - const siteArea = await SiteAreaStorage.getSiteArea(tenant.id, siteAreaID, + const siteArea = await SiteAreaStorage.getSiteArea(tenant, siteAreaID, { ...additionalFilters, ...authorizationFilter.filters, diff --git a/src/storage/mongodb/ChargingStationStorage.ts b/src/storage/mongodb/ChargingStationStorage.ts index 7d51c5d73d..d05e6a1f81 100644 --- a/src/storage/mongodb/ChargingStationStorage.ts +++ b/src/storage/mongodb/ChargingStationStorage.ts @@ -15,6 +15,7 @@ import DbParams from '../../types/database/DbParams'; import { InactivityStatus } from '../../types/Transaction'; import Logging from '../../utils/Logging'; import { ServerAction } from '../../types/Server'; +import Tenant from '../../types/Tenant'; import TenantComponents from '../../types/TenantComponents'; import TenantStorage from './TenantStorage'; import Utils from '../../utils/Utils'; @@ -132,9 +133,9 @@ export default class ChargingStationStorage { await Logging.traceEnd(Constants.DEFAULT_TENANT, MODULE_NAME, 'saveChargingStationTemplate', uniqueTimerID, chargingStationTemplate); } - public static async getChargingStation(tenantID: string, id: string = Constants.UNKNOWN_STRING_ID, + public static async getChargingStation(tenant: Tenant, id: string = Constants.UNKNOWN_STRING_ID, params: { includeDeleted?: boolean, issuer?: boolean; siteIDs?: string[] } = {}, projectFields?: string[]): Promise { - const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenantID, { + const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenant, { chargingStationIDs: [id], withSite: true, withSiteArea: true, @@ -145,19 +146,19 @@ export default class ChargingStationStorage { return chargingStationsMDB.count === 1 ? chargingStationsMDB.result[0] : null; } - public static async getChargingStationByOcpiEvseID(tenantID: string, ocpiEvseID: string = Constants.UNKNOWN_STRING_ID, + public static async getChargingStationByOcpiEvseID(tenant: Tenant, ocpiEvseID: string = Constants.UNKNOWN_STRING_ID, projectFields?: string[]): Promise { - const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenantID, { + const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenant, { ocpiEvseID, withSiteArea: true, }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); return chargingStationsMDB.count === 1 ? chargingStationsMDB.result[0] : null; } - public static async getChargingStationByOcpiLocationUid(tenantID: string, ocpiLocationID: string = Constants.UNKNOWN_STRING_ID, + public static async getChargingStationByOcpiLocationUid(tenant: Tenant, ocpiLocationID: string = Constants.UNKNOWN_STRING_ID, ocpiEvseUid: string = Constants.UNKNOWN_STRING_ID, projectFields?: string[]): Promise { - const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenantID, { + const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenant, { ocpiLocationID, ocpiEvseUid, withSiteArea: true @@ -165,16 +166,16 @@ export default class ChargingStationStorage { return chargingStationsMDB.count === 1 ? chargingStationsMDB.result[0] : null; } - public static async getChargingStationByOicpEvseID(tenantID: string, oicpEvseID: string = Constants.UNKNOWN_STRING_ID, + public static async getChargingStationByOicpEvseID(tenant: Tenant, oicpEvseID: string = Constants.UNKNOWN_STRING_ID, projectFields?: string[]): Promise { - const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenantID, { + const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenant, { oicpEvseID: oicpEvseID, withSiteArea: true }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); return chargingStationsMDB.count === 1 ? chargingStationsMDB.result[0] : null; } - public static async getChargingStations(tenantID: string, + public static async getChargingStations(tenant: Tenant, params: { search?: string; chargingStationIDs?: string[]; chargingStationSerialNumbers?: string[]; siteAreaIDs?: string[]; withNoSiteArea?: boolean; connectorStatuses?: string[]; connectorTypes?: string[]; statusChangedBefore?: Date; withSiteArea?: boolean; @@ -184,9 +185,9 @@ export default class ChargingStationStorage { }, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getChargingStations'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getChargingStations'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -335,13 +336,13 @@ export default class ChargingStationStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const chargingStationsCountMDB = await global.database.getCollection(tenantID, 'chargingstations') + const chargingStationsCountMDB = await global.database.getCollection(tenant.id, 'chargingstations') .aggregate([...aggregation, { $count: 'count' }]) .toArray(); // Check if only the total count is requested if (dbParams.onlyRecordCount) { // Return only the count - await Logging.traceEnd(tenantID, MODULE_NAME, 'getChargingStations', uniqueTimerID, chargingStationsCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getChargingStations', uniqueTimerID, chargingStationsCountMDB); return { count: (chargingStationsCountMDB.length > 0 ? chargingStationsCountMDB[0].count : 0), result: [] @@ -371,20 +372,20 @@ export default class ChargingStationStorage { }); // Users on connectors DatabaseUtils.pushArrayLookupInAggregation('connectors', DatabaseUtils.pushUserLookupInAggregation.bind(this), { - tenantID, aggregation: aggregation, localField: 'connectors.currentUserID', foreignField: '_id', + tenantID: tenant.id, aggregation: aggregation, localField: 'connectors.currentUserID', foreignField: '_id', asField: 'connectors.user', oneToOneCardinality: true, objectIDFields: ['createdBy', 'lastChangedBy'] }, { sort: dbParams.sort }); // Site Area if (params.withSiteArea) { DatabaseUtils.pushSiteAreaLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'siteAreaID', foreignField: '_id', + tenantID: tenant.id, aggregation: aggregation, localField: 'siteAreaID', foreignField: '_id', asField: 'siteArea', oneToOneCardinality: true }); } // Site if (params.withSite) { DatabaseUtils.pushSiteLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'siteID', foreignField: '_id', + tenantID: tenant.id, aggregation: aggregation, localField: 'siteID', foreignField: '_id', asField: 'site', oneToOneCardinality: true }); } @@ -393,7 +394,7 @@ export default class ChargingStationStorage { // Convert siteID back to string after having queried the site DatabaseUtils.pushConvertObjectIDToString(aggregation, 'siteArea.siteID'); // Add Created By / Last Changed By - DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation); + DatabaseUtils.pushCreatedLastChangedInAggregation(tenant.id, aggregation); // Convert Object ID to string DatabaseUtils.pushConvertObjectIDToString(aggregation, 'siteAreaID'); DatabaseUtils.pushConvertObjectIDToString(aggregation, 'siteID'); @@ -406,13 +407,13 @@ export default class ChargingStationStorage { }); } // Read DB - const chargingStationsMDB = await global.database.getCollection(tenantID, 'chargingstations') + const chargingStationsMDB = await global.database.getCollection(tenant.id, 'chargingstations') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getChargingStations', uniqueTimerID, chargingStationsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getChargingStations', uniqueTimerID, chargingStationsMDB); return { count: (chargingStationsCountMDB.length > 0 ? (chargingStationsCountMDB[0].count === Constants.DB_RECORD_COUNT_CEIL ? -1 : chargingStationsCountMDB[0].count) : 0), @@ -420,13 +421,13 @@ export default class ChargingStationStorage { }; } - public static async getChargingStationsInError(tenantID: string, + public static async getChargingStationsInError(tenant: Tenant, params: { search?: string; siteIDs?: string[]; siteAreaIDs: string[]; errorType?: string[] }, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getChargingStations'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getChargingStations'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -462,7 +463,7 @@ export default class ChargingStationStorage { // Build lookups to fetch sites from chargers aggregation.push({ $lookup: { - from: DatabaseUtils.getCollectionName(tenantID, 'siteareas'), + from: DatabaseUtils.getCollectionName(tenant.id, 'siteareas'), localField: 'siteAreaID', foreignField: '_id', as: 'sitearea' @@ -486,7 +487,7 @@ export default class ChargingStationStorage { const facets: any = { $facet: {} }; if (!Utils.isEmptyArray(params.errorType)) { // Check allowed - if (!Utils.isTenantComponentActive(await TenantStorage.getTenant(tenantID), TenantComponents.ORGANIZATION) + if (!Utils.isTenantComponentActive(tenant, TenantComponents.ORGANIZATION) && params.errorType.includes(ChargingStationInErrorType.MISSING_SITE_AREA)) { throw new BackendError({ source: Constants.CENTRAL_SERVER, @@ -510,7 +511,7 @@ export default class ChargingStationStorage { aggregation.push({ $addFields: { 'uniqueId': { $concat: ['$_id', '#', '$errorCode'] } } }); } // Add Created By / Last Changed By - DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation); + DatabaseUtils.pushCreatedLastChangedInAggregation(tenant.id, aggregation); // Sort if (!dbParams.sort) { dbParams.sort = { _id: 1 }; @@ -531,24 +532,24 @@ export default class ChargingStationStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const chargingStationsMDB = await global.database.getCollection(tenantID, 'chargingstations') + const chargingStationsMDB = await global.database.getCollection(tenant.id, 'chargingstations') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getChargingStations', uniqueTimerID, chargingStationsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getChargingStations', uniqueTimerID, chargingStationsMDB); return { count: chargingStationsMDB.length, result: chargingStationsMDB }; } - public static async saveChargingStation(tenantID: string, chargingStationToSave: ChargingStation): Promise { + public static async saveChargingStation(tenant: Tenant, chargingStationToSave: ChargingStation): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveChargingStation'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveChargingStation'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Build Request const chargingStationMDB = { _id: chargingStationToSave.id, @@ -602,20 +603,20 @@ export default class ChargingStationStorage { // Add Created/LastChanged By DatabaseUtils.addLastChangedCreatedProps(chargingStationMDB, chargingStationToSave); // Modify and return the modified document - await global.database.getCollection(tenantID, 'chargingstations').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'chargingstations').findOneAndUpdate( { _id: chargingStationToSave.id }, { $set: chargingStationMDB }, { upsert: true }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveChargingStation', uniqueTimerID, chargingStationMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveChargingStation', uniqueTimerID, chargingStationMDB); return chargingStationMDB._id; } - public static async saveChargingStationConnectors(tenantID: string, id: string, connectors: Connector[], backupConnectors?: Connector[]): Promise { + public static async saveChargingStationConnectors(tenant: Tenant, id: string, connectors: Connector[], backupConnectors?: Connector[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveChargingStationConnectors'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveChargingStationConnectors'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); const updatedProps: any = {}; // Set connectors updatedProps.connectors = connectors.map((connector) => @@ -626,24 +627,24 @@ export default class ChargingStationStorage { ChargingStationStorage.filterConnectorMDB(backupConnector)); } // Modify document - await global.database.getCollection(tenantID, 'chargingstations').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'chargingstations').findOneAndUpdate( { '_id': id }, { $set: updatedProps }, { upsert: true }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveChargingStationConnectors', uniqueTimerID, connectors); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveChargingStationConnectors', uniqueTimerID, connectors); } - public static async saveChargingStationCFApplicationIDAndInstanceIndex(tenantID: string, id: string, + public static async saveChargingStationCFApplicationIDAndInstanceIndex(tenant: Tenant, id: string, cfApplicationIDAndInstanceIndex: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveChargingStationCFApplicationIDAndInstanceIndex'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveChargingStationCFApplicationIDAndInstanceIndex'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify document - await global.database.getCollection(tenantID, 'chargingstations').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'chargingstations').findOneAndUpdate( { '_id': id }, { $set: { @@ -652,17 +653,17 @@ export default class ChargingStationStorage { }, { upsert: true }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveChargingStationCFApplicationIDAndInstanceIndex', uniqueTimerID, cfApplicationIDAndInstanceIndex); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveChargingStationCFApplicationIDAndInstanceIndex', uniqueTimerID, cfApplicationIDAndInstanceIndex); } - public static async saveChargingStationOicpData(tenantID: string, id: string, + public static async saveChargingStationOicpData(tenant: Tenant, id: string, oicpData: ChargingStationOicpData): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveChargingStationOicpData'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveChargingStationOicpData'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify document - await global.database.getCollection(tenantID, 'chargingstations').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'chargingstations').findOneAndUpdate( { '_id': id }, { $set: { @@ -671,33 +672,33 @@ export default class ChargingStationStorage { }, { upsert: false }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveChargingStationOicpData', uniqueTimerID, oicpData); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveChargingStationOicpData', uniqueTimerID, oicpData); } - public static async saveChargingStationLastSeen(tenantID: string, id: string, + public static async saveChargingStationLastSeen(tenant: Tenant, id: string, params: { lastSeen: Date; currentIPAddress?: string | string[] }): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveChargingStationLastSeen'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveChargingStationLastSeen'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Set data // Modify document - await global.database.getCollection(tenantID, 'chargingstations').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'chargingstations').findOneAndUpdate( { '_id': id }, { $set: params }, { upsert: true }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveChargingStationLastSeen', uniqueTimerID, params); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveChargingStationLastSeen', uniqueTimerID, params); } - public static async saveChargingStationOcpiData(tenantID: string, id: string, + public static async saveChargingStationOcpiData(tenant: Tenant, id: string, ocpiData: ChargingStationOcpiData): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveChargingStationOcpiData'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveChargingStationOcpiData'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify document - await global.database.getCollection(tenantID, 'chargingstations').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'chargingstations').findOneAndUpdate( { '_id': id }, { $set: { @@ -706,17 +707,17 @@ export default class ChargingStationStorage { }, { upsert: false }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveChargingStationOcpiData', uniqueTimerID, ocpiData); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveChargingStationOcpiData', uniqueTimerID, ocpiData); } - public static async saveChargingStationRemoteAuthorizations(tenantID: string, id: string, + public static async saveChargingStationRemoteAuthorizations(tenant: Tenant, id: string, remoteAuthorizations: RemoteAuthorization[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveChargingStationRemoteAuthorizations'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveChargingStationRemoteAuthorizations'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify document - await global.database.getCollection(tenantID, 'chargingstations').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'chargingstations').findOneAndUpdate( { '_id': id }, { $set: { @@ -725,43 +726,43 @@ export default class ChargingStationStorage { }, { upsert: false }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveChargingStationRemoteAuthorizations', uniqueTimerID, remoteAuthorizations); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveChargingStationRemoteAuthorizations', uniqueTimerID, remoteAuthorizations); } - public static async saveChargingStationFirmwareStatus(tenantID: string, id: string, firmwareUpdateStatus: OCPPFirmwareStatus): Promise { + public static async saveChargingStationFirmwareStatus(tenant: Tenant, id: string, firmwareUpdateStatus: OCPPFirmwareStatus): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveChargingStationFirmwareStatus'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveChargingStationFirmwareStatus'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify document - await global.database.getCollection(tenantID, 'chargingstations').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'chargingstations').findOneAndUpdate( { '_id': id }, { $set: { firmwareUpdateStatus } }, { upsert: true }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveChargingStationFirmwareStatus', uniqueTimerID, firmwareUpdateStatus); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveChargingStationFirmwareStatus', uniqueTimerID, firmwareUpdateStatus); } - public static async deleteChargingStation(tenantID: string, id: string): Promise { + public static async deleteChargingStation(tenant: Tenant, id: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteChargingStation'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteChargingStation'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Delete Configuration - await global.database.getCollection(tenantID, 'configurations') + await global.database.getCollection(tenant.id, 'configurations') .findOneAndDelete({ '_id': id }); // Delete Charging Profiles - await ChargingStationStorage.deleteChargingProfiles(tenantID, id); + await ChargingStationStorage.deleteChargingProfiles(tenant, id); // Delete Charging Station - await global.database.getCollection(tenantID, 'chargingstations') + await global.database.getCollection(tenant.id, 'chargingstations') .findOneAndDelete({ '_id': id }); // Keep the rest (boot notification, authorize...) // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteChargingStation', uniqueTimerID, { id }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteChargingStation', uniqueTimerID, { id }); } - public static async getOcppParameterValue(tenantID: string, chargeBoxID: string, paramName: string): Promise { - const configuration = await ChargingStationStorage.getOcppParameters(tenantID, chargeBoxID); + public static async getOcppParameterValue(tenant: Tenant, chargeBoxID: string, paramName: string): Promise { + const configuration = await ChargingStationStorage.getOcppParameters(tenant, chargeBoxID); let value: string = null; if (configuration) { // Get the value @@ -777,13 +778,13 @@ export default class ChargingStationStorage { return value; } - static async saveOcppParameters(tenantID: string, parameters: ChargingStationOcppParameters): Promise { + static async saveOcppParameters(tenant: Tenant, parameters: ChargingStationOcppParameters): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveOcppParameters'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveOcppParameters'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify - await global.database.getCollection(tenantID, 'configurations').findOneAndUpdate({ + await global.database.getCollection(tenant.id, 'configurations').findOneAndUpdate({ '_id': parameters.id }, { $set: { @@ -795,16 +796,16 @@ export default class ChargingStationStorage { returnDocument: 'after' }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveOcppParameters', uniqueTimerID, parameters); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveOcppParameters', uniqueTimerID, parameters); } - public static async getOcppParameters(tenantID: string, id: string): Promise> { + public static async getOcppParameters(tenant: Tenant, id: string): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getOcppParameters'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getOcppParameters'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Read DB - const parametersMDB = await global.database.getCollection(tenantID, 'configurations') + const parametersMDB = await global.database.getCollection(tenant.id, 'configurations') .findOne({ '_id': id }); if (parametersMDB) { // Sort @@ -820,28 +821,28 @@ export default class ChargingStationStorage { }); } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getOcppParameters', uniqueTimerID, parametersMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getOcppParameters', uniqueTimerID, parametersMDB); return { count: parametersMDB.configuration.length, result: parametersMDB.configuration }; } // No conf - await Logging.traceEnd(tenantID, MODULE_NAME, 'getOcppParameters', uniqueTimerID, parametersMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getOcppParameters', uniqueTimerID, parametersMDB); return { count: 0, result: [] }; } - public static async getChargingProfile(tenantID: string, id: string): Promise { - const chargingProfilesMDB = await ChargingStationStorage.getChargingProfiles(tenantID, { + public static async getChargingProfile(tenant: Tenant, id: string): Promise { + const chargingProfilesMDB = await ChargingStationStorage.getChargingProfiles(tenant, { chargingProfileID: id }, Constants.DB_PARAMS_SINGLE_RECORD); return chargingProfilesMDB.count === 1 ? chargingProfilesMDB.result[0] : null; } - public static async getChargingProfiles(tenantID: string, + public static async getChargingProfiles(tenant: Tenant, params: { search?: string; chargingStationIDs?: string[]; connectorID?: number; chargingProfileID?: string; profilePurposeType?: ChargingProfilePurposeType; transactionId?: number; withChargingStation?: boolean; @@ -849,9 +850,9 @@ export default class ChargingStationStorage { } = {}, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getChargingProfiles'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getChargingProfiles'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -898,13 +899,13 @@ export default class ChargingStationStorage { if (params.withChargingStation || params.withSiteArea || !Utils.isEmptyArray(params.siteIDs)) { // Charging Stations DatabaseUtils.pushChargingStationLookupInAggregation({ - tenantID, aggregation, localField: 'chargingStationID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: 'chargingStationID', foreignField: '_id', asField: 'chargingStation', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); // Site Areas if (params.withSiteArea || !Utils.isEmptyArray(params.siteIDs)) { DatabaseUtils.pushSiteAreaLookupInAggregation({ - tenantID, aggregation, localField: 'chargingStation.siteAreaID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: 'chargingStation.siteAreaID', foreignField: '_id', asField: 'chargingStation.siteArea', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); // Convert @@ -930,12 +931,12 @@ export default class ChargingStationStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const chargingProfilesCountMDB = await global.database.getCollection>(tenantID, 'chargingprofiles') + const chargingProfilesCountMDB = await global.database.getCollection>(tenant.id, 'chargingprofiles') .aggregate([...aggregation, { $count: 'count' }], { allowDiskUse: true }) .toArray(); // Check if only the total count is requested if (dbParams.onlyRecordCount) { - await Logging.traceEnd(tenantID, MODULE_NAME, 'getChargingProfiles', uniqueTimerID, chargingProfilesCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getChargingProfiles', uniqueTimerID, chargingProfilesCountMDB); return { count: (chargingProfilesCountMDB.length > 0 ? chargingProfilesCountMDB[0].count : 0), result: [] @@ -944,7 +945,7 @@ export default class ChargingStationStorage { // Remove the limit aggregation.pop(); // Add Created By / Last Changed By - DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation); + DatabaseUtils.pushCreatedLastChangedInAggregation(tenant.id, aggregation); // Rename ID DatabaseUtils.pushRenameDatabaseID(aggregation); // Sort @@ -970,13 +971,13 @@ export default class ChargingStationStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const chargingProfilesMDB = await global.database.getCollection(tenantID, 'chargingprofiles') + const chargingProfilesMDB = await global.database.getCollection(tenant.id, 'chargingprofiles') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getChargingProfiles', uniqueTimerID, chargingProfilesMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getChargingProfiles', uniqueTimerID, chargingProfilesMDB); return { count: (chargingProfilesCountMDB.length > 0 ? (chargingProfilesCountMDB[0].count === Constants.DB_RECORD_COUNT_CEIL ? -1 : chargingProfilesCountMDB[0].count) : 0), @@ -984,10 +985,10 @@ export default class ChargingStationStorage { }; } - public static async saveChargingProfile(tenantID: string, chargingProfileToSave: ChargingProfile): Promise { - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveChargingProfile'); + public static async saveChargingProfile(tenant: Tenant, chargingProfileToSave: ChargingProfile): Promise { + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveChargingProfile'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); const chargingProfileFilter: any = {}; // Build Request if (chargingProfileToSave.id) { @@ -1004,36 +1005,36 @@ export default class ChargingStationStorage { chargePointID: Utils.convertToInt(chargingProfileToSave.chargePointID), profile: chargingProfileToSave.profile }; - await global.database.getCollection(tenantID, 'chargingprofiles').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'chargingprofiles').findOneAndUpdate( chargingProfileFilter, { $set: chargingProfileMDB }, { upsert: true }); - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveChargingProfile', uniqueTimerID, chargingProfileMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveChargingProfile', uniqueTimerID, chargingProfileMDB); return chargingProfileFilter._id as string; } - public static async deleteChargingProfile(tenantID: string, id: string): Promise { + public static async deleteChargingProfile(tenant: Tenant, id: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteChargingProfile'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteChargingProfile'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Delete Charging Profile - await global.database.getCollection(tenantID, 'chargingprofiles') + await global.database.getCollection(tenant.id, 'chargingprofiles') .findOneAndDelete({ '_id': id }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteChargingProfile', uniqueTimerID, { id }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteChargingProfile', uniqueTimerID, { id }); } - public static async deleteChargingProfiles(tenantID: string, chargingStationID: string): Promise { + public static async deleteChargingProfiles(tenant: Tenant, chargingStationID: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteChargingProfile'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteChargingProfile'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Delete Charging Profiles - await global.database.getCollection(tenantID, 'chargingprofiles') + await global.database.getCollection(tenant.id, 'chargingprofiles') .findOneAndDelete({ 'chargingStationID': chargingStationID }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteChargingProfile', uniqueTimerID, { chargingStationID }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteChargingProfile', uniqueTimerID, { chargingStationID }); } public static getChargingStationFirmware(filename: string): GridFSBucketReadStream { diff --git a/src/storage/mongodb/RegistrationTokenStorage.ts b/src/storage/mongodb/RegistrationTokenStorage.ts index 173b98edbc..b2d7a77394 100644 --- a/src/storage/mongodb/RegistrationTokenStorage.ts +++ b/src/storage/mongodb/RegistrationTokenStorage.ts @@ -7,16 +7,17 @@ import DbParams from '../../types/database/DbParams'; import Logging from '../../utils/Logging'; import { ObjectId } from 'mongodb'; import RegistrationToken from '../../types/RegistrationToken'; +import Tenant from '../../types/Tenant'; import Utils from '../../utils/Utils'; const MODULE_NAME = 'RegistrationTokenStorage'; export default class RegistrationTokenStorage { - static async saveRegistrationToken(tenantID: string, registrationToken: RegistrationToken): Promise { + static async saveRegistrationToken(tenant: Tenant, registrationToken: RegistrationToken): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveRegistrationToken'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveRegistrationToken'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Set const registrationTokenMDB = { _id: registrationToken.id ? DatabaseUtils.convertToObjectID(registrationToken.id) : new ObjectId(), @@ -28,23 +29,23 @@ export default class RegistrationTokenStorage { // Add Last Changed/Created props DatabaseUtils.addLastChangedCreatedProps(registrationTokenMDB, registrationToken); // Modify - await global.database.getCollection(tenantID, 'registrationtokens').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'registrationtokens').findOneAndUpdate( { _id: registrationTokenMDB._id }, { $set: registrationTokenMDB }, { upsert: true, returnDocument: 'after' } ); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveRegistrationToken', uniqueTimerID, registrationTokenMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveRegistrationToken', uniqueTimerID, registrationTokenMDB); return registrationTokenMDB._id.toString(); } - static async getRegistrationTokens(tenantID: string, + static async getRegistrationTokens(tenant: Tenant, params: { tokenIDs?: string[]; siteIDs?: string[]; siteAreaID?: string } = {}, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getRegistrationTokens'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getRegistrationTokens'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -55,7 +56,7 @@ export default class RegistrationTokenStorage { const aggregation = []; // Add Site Area DatabaseUtils.pushSiteAreaLookupInAggregation({ - tenantID, aggregation, localField: 'siteAreaID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: 'siteAreaID', foreignField: '_id', asField: 'siteArea', oneToOneCardinality: true }); // Set the filters @@ -88,13 +89,13 @@ export default class RegistrationTokenStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const registrationTokensCountMDB = await global.database.getCollection>(tenantID, 'registrationtokens') + const registrationTokensCountMDB = await global.database.getCollection>(tenant.id, 'registrationtokens') .aggregate([...aggregation, { $count: 'count' }], { allowDiskUse: true }) .toArray(); // Check if only the total count is requested if (dbParams.onlyRecordCount) { // Return only the count - await Logging.traceEnd(tenantID, MODULE_NAME, 'getRegistrationTokens', uniqueTimerID, registrationTokensCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getRegistrationTokens', uniqueTimerID, registrationTokensCountMDB); return { count: (registrationTokensCountMDB.length > 0 ? registrationTokensCountMDB[0].count : 0), result: [] @@ -120,17 +121,17 @@ export default class RegistrationTokenStorage { $limit: (dbParams.limit > 0 && dbParams.limit < Constants.DB_RECORD_COUNT_CEIL) ? dbParams.limit : Constants.DB_RECORD_COUNT_CEIL }); // Add Created By / Last Changed By - DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation); + DatabaseUtils.pushCreatedLastChangedInAggregation(tenant.id, aggregation); // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const registrationTokens = await global.database.getCollection(tenantID, 'registrationtokens') + const registrationTokens = await global.database.getCollection(tenant.id, 'registrationtokens') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getRegistrationTokens', uniqueTimerID, registrationTokens); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getRegistrationTokens', uniqueTimerID, registrationTokens); // Ok return { count: (registrationTokensCountMDB.length > 0 ? @@ -139,20 +140,20 @@ export default class RegistrationTokenStorage { }; } - static async getRegistrationToken(tenantID: string, id: string = Constants.UNKNOWN_OBJECT_ID, + static async getRegistrationToken(tenant: Tenant, id: string = Constants.UNKNOWN_OBJECT_ID, projectFields?: string[]): Promise { - const registrationTokensMDB = await RegistrationTokenStorage.getRegistrationTokens(tenantID, { + const registrationTokensMDB = await RegistrationTokenStorage.getRegistrationTokens(tenant, { tokenIDs: [id] }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); return registrationTokensMDB.count === 1 ? registrationTokensMDB.result[0] : null; } - static async deleteRegistrationToken(tenantID: string, id: string): Promise { + static async deleteRegistrationToken(tenant: Tenant, id: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteRegistrationToken'); - await global.database.getCollection(tenantID, 'registrationtokens') + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteRegistrationToken'); + await global.database.getCollection(tenant.id, 'registrationtokens') .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteRegistrationToken', uniqueTimerID, { id }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteRegistrationToken', uniqueTimerID, { id }); } } diff --git a/src/storage/mongodb/SiteAreaStorage.ts b/src/storage/mongodb/SiteAreaStorage.ts index 7ce6c599ab..b2b58ac51e 100644 --- a/src/storage/mongodb/SiteAreaStorage.ts +++ b/src/storage/mongodb/SiteAreaStorage.ts @@ -7,22 +7,23 @@ import DbParams from '../../types/database/DbParams'; import Logging from '../../utils/Logging'; import { ObjectId } from 'mongodb'; import SiteArea from '../../types/SiteArea'; +import Tenant from '../../types/Tenant'; import Utils from '../../utils/Utils'; const MODULE_NAME = 'SiteAreaStorage'; export default class SiteAreaStorage { - public static async addAssetsToSiteArea(tenantID: string, siteArea: SiteArea, assetIDs: string[]): Promise { + public static async addAssetsToSiteArea(tenant: Tenant, siteArea: SiteArea, assetIDs: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'addAssetsToSiteArea'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'addAssetsToSiteArea'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Site Area provided? if (siteArea) { // At least one Asset if (assetIDs && assetIDs.length > 0) { // Update all assets - await global.database.getCollection(tenantID, 'assets').updateMany( + await global.database.getCollection(tenant.id, 'assets').updateMany( { '_id': { $in: assetIDs.map((assetID) => DatabaseUtils.convertToObjectID(assetID)) } }, { $set: { @@ -33,20 +34,20 @@ export default class SiteAreaStorage { } } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'addAssetsToSiteArea', uniqueTimerID, assetIDs); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'addAssetsToSiteArea', uniqueTimerID, assetIDs); } - public static async removeAssetsFromSiteArea(tenantID: string, siteAreaID: string, assetIDs: string[]): Promise { + public static async removeAssetsFromSiteArea(tenant: Tenant, siteAreaID: string, assetIDs: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'removeAssetsFromSiteArea'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'removeAssetsFromSiteArea'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Site Area provided? if (siteAreaID) { // At least one Asset if (assetIDs && assetIDs.length > 0) { // Update all assets - await global.database.getCollection(tenantID, 'assets').updateMany( + await global.database.getCollection(tenant.id, 'assets').updateMany( { '_id': { $in: assetIDs.map((assetID) => DatabaseUtils.convertToObjectID(assetID)) } }, { $set: { @@ -57,28 +58,28 @@ export default class SiteAreaStorage { } } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'removeAssetsFromSiteArea', uniqueTimerID, assetIDs); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'removeAssetsFromSiteArea', uniqueTimerID, assetIDs); } - public static async getSiteAreaImage(tenantID: string, id: string): Promise { + public static async getSiteAreaImage(tenant: Tenant, id: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getSiteAreaImage'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getSiteAreaImage'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Read DB - const siteAreaImageMDB = await global.database.getCollection<{ _id: ObjectId; image: string }>(tenantID, 'siteareaimages') + const siteAreaImageMDB = await global.database.getCollection<{ _id: ObjectId; image: string }>(tenant.id, 'siteareaimages') .findOne({ _id: DatabaseUtils.convertToObjectID(id) }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getSiteAreaImage', uniqueTimerID, siteAreaImageMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getSiteAreaImage', uniqueTimerID, siteAreaImageMDB); return { id: id, image: siteAreaImageMDB ? siteAreaImageMDB.image : null }; } - public static async getSiteArea(tenantID: string, id: string = Constants.UNKNOWN_OBJECT_ID, + public static async getSiteArea(tenant: Tenant, id: string = Constants.UNKNOWN_OBJECT_ID, params: { withSite?: boolean; withChargingStations?: boolean, withAvailableChargingStations?: boolean; withImage?: boolean } = {}, projectFields?: string[]): Promise { - const siteAreasMDB = await SiteAreaStorage.getSiteAreas(tenantID, { + const siteAreasMDB = await SiteAreaStorage.getSiteAreas(tenant, { siteAreaIDs: [id], withSite: params.withSite, withChargingStations: params.withChargingStations, @@ -88,11 +89,11 @@ export default class SiteAreaStorage { return siteAreasMDB.count === 1 ? siteAreasMDB.result[0] : null; } - public static async saveSiteArea(tenantID: string, siteAreaToSave: SiteArea, saveImage = false): Promise { + public static async saveSiteArea(tenant: Tenant, siteAreaToSave: SiteArea, saveImage = false): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveSiteArea'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveSiteArea'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Set const siteAreaMDB: any = { _id: !siteAreaToSave.id ? new ObjectId() : DatabaseUtils.convertToObjectID(siteAreaToSave.id), @@ -121,20 +122,20 @@ export default class SiteAreaStorage { // Add Last Changed/Created props DatabaseUtils.addLastChangedCreatedProps(siteAreaMDB, siteAreaToSave); // Modify - await global.database.getCollection(tenantID, 'siteareas').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'siteareas').findOneAndUpdate( { _id: siteAreaMDB._id }, { $set: siteAreaMDB }, { upsert: true, returnDocument: 'after' } ); if (saveImage) { - await SiteAreaStorage.saveSiteAreaImage(tenantID, siteAreaMDB._id.toString(), siteAreaToSave.image); + await SiteAreaStorage.saveSiteAreaImage(tenant, siteAreaMDB._id.toString(), siteAreaToSave.image); } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveSiteArea', uniqueTimerID, siteAreaMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveSiteArea', uniqueTimerID, siteAreaMDB); return siteAreaMDB._id.toString(); } - public static async getSiteAreas(tenantID: string, + public static async getSiteAreas(tenant: Tenant, params: { siteAreaIDs?: string[]; search?: string; siteIDs?: string[]; companyIDs?: string[]; withSite?: boolean; issuer?: boolean; name?: string; withChargingStations?: boolean; withOnlyChargingStations?: boolean; withAvailableChargingStations?: boolean; @@ -142,9 +143,9 @@ export default class SiteAreaStorage { } = {}, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getSiteAreas'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getSiteAreas'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -190,7 +191,7 @@ export default class SiteAreaStorage { // Company if (!Utils.isEmptyArray(params.companyIDs)) { DatabaseUtils.pushSiteLookupInAggregation({ - tenantID, aggregation, localField: 'siteID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: 'siteID', foreignField: '_id', asField: 'site', oneToOneCardinality: true }); filters['site.companyID'] = { @@ -219,13 +220,13 @@ export default class SiteAreaStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const siteAreasCountMDB = await global.database.getCollection>(tenantID, 'siteareas') + const siteAreasCountMDB = await global.database.getCollection>(tenant.id, 'siteareas') .aggregate([...aggregation, { $count: 'count' }], { allowDiskUse: true }) .toArray(); // Check if only the total count is requested if (dbParams.onlyRecordCount) { // Return only the count - await Logging.traceEnd(tenantID, MODULE_NAME, 'getSiteAreas', uniqueTimerID, siteAreasCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getSiteAreas', uniqueTimerID, siteAreasCountMDB); return { count: (siteAreasCountMDB.length > 0 ? siteAreasCountMDB[0].count : 0), result: [] @@ -255,14 +256,14 @@ export default class SiteAreaStorage { // Sites if (params.withSite) { DatabaseUtils.pushSiteLookupInAggregation({ - tenantID, aggregation, localField: 'siteID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: 'siteID', foreignField: '_id', asField: 'site', oneToOneCardinality: true }); } // Charging Stations if (params.withChargingStations || params.withOnlyChargingStations || params.withAvailableChargingStations) { DatabaseUtils.pushChargingStationLookupInAggregation({ - tenantID, aggregation, localField: '_id', foreignField: 'siteAreaID', + tenantID: tenant.id, aggregation, localField: '_id', foreignField: 'siteAreaID', asField: 'chargingStations' }); } @@ -274,7 +275,7 @@ export default class SiteAreaStorage { $concat: [ `${Utils.buildRestServerURL()}/client/util/SiteAreaImage?ID=`, { $toString: '$_id' }, - `&TenantID=${tenantID}&LastChangedOn=`, + `&TenantID=${tenant.id}&LastChangedOn=`, { $toString: '$lastChangedOn' } ] } @@ -284,7 +285,7 @@ export default class SiteAreaStorage { // Convert Object ID to string DatabaseUtils.pushConvertObjectIDToString(aggregation, 'siteID'); // Add Last Changed / Created - DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation); + DatabaseUtils.pushCreatedLastChangedInAggregation(tenant.id, aggregation); // Handle the ID DatabaseUtils.pushRenameDatabaseID(aggregation); // Project @@ -294,7 +295,7 @@ export default class SiteAreaStorage { 'chargingStations.deleted', 'chargingStations.cannotChargeInParallel', 'chargingStations.public', 'chargingStations.inactive']); } // Read DB - const siteAreasMDB = await global.database.getCollection(tenantID, 'siteareas') + const siteAreasMDB = await global.database.getCollection(tenant.id, 'siteareas') .aggregate(aggregation, { allowDiskUse: true }) @@ -324,7 +325,7 @@ export default class SiteAreaStorage { } } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getSiteAreas', uniqueTimerID, siteAreasMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getSiteAreas', uniqueTimerID, siteAreasMDB); // Ok return { projectedFields: projectFields, @@ -334,17 +335,17 @@ export default class SiteAreaStorage { }; } - public static async addChargingStationsToSiteArea(tenantID: string, siteArea: SiteArea, chargingStationIDs: string[]): Promise { + public static async addChargingStationsToSiteArea(tenant: Tenant, siteArea: SiteArea, chargingStationIDs: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'addChargingStationsToSiteArea'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'addChargingStationsToSiteArea'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Site provided? if (siteArea) { // At least one ChargingStation if (chargingStationIDs && chargingStationIDs.length > 0) { // Update all chargers - await global.database.getCollection(tenantID, 'chargingstations').updateMany( + await global.database.getCollection(tenant.id, 'chargingstations').updateMany( { '_id': { $in: chargingStationIDs } }, { $set: { @@ -356,20 +357,20 @@ export default class SiteAreaStorage { } } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'addChargingStationsToSiteArea', uniqueTimerID, chargingStationIDs); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'addChargingStationsToSiteArea', uniqueTimerID, chargingStationIDs); } - public static async removeChargingStationsFromSiteArea(tenantID: string, siteAreaID: string, chargingStationIDs: string[]): Promise { + public static async removeChargingStationsFromSiteArea(tenant: Tenant, siteAreaID: string, chargingStationIDs: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'removeChargingStationsFromSiteArea'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'removeChargingStationsFromSiteArea'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Site provided? if (siteAreaID) { // At least one ChargingStation if (chargingStationIDs && chargingStationIDs.length > 0) { // Update all chargers - await global.database.getCollection(tenantID, 'chargingstations').updateMany( + await global.database.getCollection(tenant.id, 'chargingstations').updateMany( { '_id': { $in: chargingStationIDs } }, { $set: { @@ -381,67 +382,67 @@ export default class SiteAreaStorage { } } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'removeChargingStationsFromSiteArea', uniqueTimerID, chargingStationIDs); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'removeChargingStationsFromSiteArea', uniqueTimerID, chargingStationIDs); } - public static async deleteSiteArea(tenantID: string, id: string): Promise { - await SiteAreaStorage.deleteSiteAreas(tenantID, [id]); + public static async deleteSiteArea(tenant: Tenant, id: string): Promise { + await SiteAreaStorage.deleteSiteAreas(tenant, [id]); } - public static async deleteSiteAreas(tenantID: string, siteAreaIDs: string[]): Promise { + public static async deleteSiteAreas(tenant: Tenant, siteAreaIDs: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteSiteAreas'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteSiteAreas'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Remove Charging Station's Site Area - await global.database.getCollection(tenantID, 'chargingstations').updateMany( + await global.database.getCollection(tenant.id, 'chargingstations').updateMany( { siteAreaID: { $in: siteAreaIDs.map((ID) => DatabaseUtils.convertToObjectID(ID)) } }, { $set: { siteAreaID: null } } ); // Remove Asset's Site Area - await global.database.getCollection(tenantID, 'assets').updateMany( + await global.database.getCollection(tenant.id, 'assets').updateMany( { siteAreaID: { $in: siteAreaIDs.map((ID) => DatabaseUtils.convertToObjectID(ID)) } }, { $set: { siteAreaID: null } } ); // Delete SiteArea - await global.database.getCollection(tenantID, 'siteareas').deleteMany( + await global.database.getCollection(tenant.id, 'siteareas').deleteMany( { '_id': { $in: siteAreaIDs.map((ID) => DatabaseUtils.convertToObjectID(ID)) } } ); // Delete Image - await global.database.getCollection(tenantID, 'sitesareaimages').deleteMany( + await global.database.getCollection(tenant.id, 'sitesareaimages').deleteMany( { '_id': { $in: siteAreaIDs.map((ID) => DatabaseUtils.convertToObjectID(ID)) } } ); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteSiteAreas', uniqueTimerID, siteAreaIDs); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteSiteAreas', uniqueTimerID, siteAreaIDs); } - public static async deleteSiteAreasFromSites(tenantID: string, siteIDs: string[]): Promise { + public static async deleteSiteAreasFromSites(tenant: Tenant, siteIDs: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteSiteAreasFromSites'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteSiteAreasFromSites'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Find site areas to delete - const siteareas: string[] = (await global.database.getCollection(tenantID, 'siteareas') + const siteareas: string[] = (await global.database.getCollection(tenant.id, 'siteareas') .find({ siteID: { $in: siteIDs.map((id) => DatabaseUtils.convertToObjectID(id)) } }) .project({ _id: 1 }).toArray()).map((idWrapper): string => idWrapper._id.toString()); // Delete site areas - await SiteAreaStorage.deleteSiteAreas(tenantID, siteareas); + await SiteAreaStorage.deleteSiteAreas(tenant, siteareas); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteSiteAreasFromSites', uniqueTimerID, siteIDs); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteSiteAreasFromSites', uniqueTimerID, siteIDs); } - private static async saveSiteAreaImage(tenantID: string, siteAreaID: string, siteAreaImageToSave: string): Promise { + private static async saveSiteAreaImage(tenant: Tenant, siteAreaID: string, siteAreaImageToSave: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveSiteAreaImage'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveSiteAreaImage'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify - await global.database.getCollection(tenantID, 'siteareaimages').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'siteareaimages').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(siteAreaID) }, { $set: { image: siteAreaImageToSave } }, { upsert: true, returnDocument: 'after' } ); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveSiteAreaImage', uniqueTimerID, siteAreaImageToSave); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveSiteAreaImage', uniqueTimerID, siteAreaImageToSave); } } diff --git a/src/storage/mongodb/SiteStorage.ts b/src/storage/mongodb/SiteStorage.ts index 15b081b0c1..ff608de23b 100644 --- a/src/storage/mongodb/SiteStorage.ts +++ b/src/storage/mongodb/SiteStorage.ts @@ -469,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(tenant.id, + const chargingStations = await ChargingStationStorage.getChargingStations(tenant, { siteIDs: [siteMDB.id], includeDeleted: false, withSiteArea: true }, Constants.DB_PARAMS_MAX_LIMIT); // Skip site with no charging stations if asked if (params.withOnlyChargingStations && chargingStations.count === 0) { @@ -508,7 +508,7 @@ export default class SiteStorage { // Check Tenant DatabaseUtils.checkTenantObject(tenant); // Delete all Site Areas - await SiteAreaStorage.deleteSiteAreasFromSites(tenant.id, ids); + await SiteAreaStorage.deleteSiteAreasFromSites(tenant, ids); // Convert const cids: ObjectId[] = ids.map((id) => DatabaseUtils.convertToObjectID(id)); // Delete Site @@ -536,7 +536,7 @@ export default class SiteStorage { .toArray()) .map((site): string => site._id.toString()); // Delete all Site Areas - await SiteAreaStorage.deleteSiteAreasFromSites(tenant.id, siteIDs); + await SiteAreaStorage.deleteSiteAreasFromSites(tenant, siteIDs); // Delete Sites await SiteStorage.deleteSites(tenant, siteIDs); // Debug diff --git a/src/storage/mongodb/StatisticsStorage.ts b/src/storage/mongodb/StatisticsStorage.ts index 01606ee443..5c037950ec 100644 --- a/src/storage/mongodb/StatisticsStorage.ts +++ b/src/storage/mongodb/StatisticsStorage.ts @@ -2,6 +2,7 @@ import StatisticFilter, { ChargingStationStats, StatsGroupBy, UserStats } from ' import DatabaseUtils from './DatabaseUtils'; import Logging from '../../utils/Logging'; +import Tenant from '../../types/Tenant'; import Utils from '../../utils/Utils'; import global from '../../types/GlobalType'; @@ -9,11 +10,11 @@ const MODULE_NAME = 'StatisticsStorage'; export default class StatisticsStorage { - static async getChargingStationStats(tenantID: string, params: StatisticFilter, groupBy: string): Promise { + static async getChargingStationStats(tenant: Tenant, params: StatisticFilter, groupBy: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getChargingStationStats'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getChargingStationStats'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Build filter const filters: any = {}; // Date provided? @@ -122,19 +123,19 @@ export default class StatisticsStorage { $sort: { 'month': 1, 'unit': 1, 'chargeBox': 1 } }); // Read DB - const chargingStationStatsMDB = await global.database.getCollection(tenantID, 'transactions') + const chargingStationStatsMDB = await global.database.getCollection(tenant.id, 'transactions') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getChargingStationStats', uniqueTimerID, chargingStationStatsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getChargingStationStats', uniqueTimerID, chargingStationStatsMDB); return chargingStationStatsMDB; } - static async getUserStats(tenantID: string, params: StatisticFilter, groupBy: string): Promise { + static async getUserStats(tenant: Tenant, params: StatisticFilter, groupBy: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getUserStats'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getUserStats'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Build filter const filters: any = {}; // Date provided? @@ -234,7 +235,7 @@ export default class StatisticsStorage { } // Lookup for users DatabaseUtils.pushUserLookupInAggregation({ - tenantID, aggregation, localField: '_id.userID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: '_id.userID', foreignField: '_id', asField: 'user', oneToOneCardinality: true, oneToOneCardinalityNotNull: true }, [ { $project: { _id: 1, name: 1, firstName: 1 } } ]); // Single Record @@ -252,11 +253,11 @@ export default class StatisticsStorage { $sort: { 'month': 1, 'unit': 1, 'userID': 1 } // Instead of chargeBox userID ? }); // Read DB - const userStatsMDB = await global.database.getCollection(tenantID, 'transactions') + const userStatsMDB = await global.database.getCollection(tenant.id, 'transactions') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getUserStats', uniqueTimerID, userStatsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getUserStats', uniqueTimerID, userStatsMDB); return userStatsMDB; } } diff --git a/src/storage/mongodb/TagStorage.ts b/src/storage/mongodb/TagStorage.ts index 68dc83e5b2..13a99b033c 100644 --- a/src/storage/mongodb/TagStorage.ts +++ b/src/storage/mongodb/TagStorage.ts @@ -27,7 +27,8 @@ export default class TagStorage { default: Utils.convertToBoolean(tag.default), visualID: tag.visualID ?? new ObjectId().toString(), ocpiToken: tag.ocpiToken, - description: tag.description + description: tag.description, + importedData: tag.importedData }; // Check Created/Last Changed By DatabaseUtils.addLastChangedCreatedProps(tagMDB, tag); @@ -52,7 +53,9 @@ export default class TagStorage { status: importedTagToSave.status, errorDescription: importedTagToSave.errorDescription, importedOn: importedTagToSave.importedOn, - importedBy: importedTagToSave.importedBy + importedBy: importedTagToSave.importedBy, + siteIDs: importedTagToSave.siteIDs, + importedData: importedTagToSave.importedData }; await global.database.getCollection(tenantID, 'importedtags').findOneAndUpdate( { _id: tagMDB._id }, @@ -76,7 +79,9 @@ export default class TagStorage { status: importedTagToSave.status, errorDescription: importedTagToSave.errorDescription, importedOn: importedTagToSave.importedOn, - importedBy: importedTagToSave.importedBy + importedBy: importedTagToSave.importedBy, + siteIDs: importedTagToSave.siteIDs, + importedData: importedTagToSave.importedData })); // Insert all at once const result = await global.database.getCollection(tenantID, 'importedtags').insertMany( @@ -119,7 +124,7 @@ export default class TagStorage { // Check Tenant await DatabaseUtils.checkTenant(tenantID); // Count documents - const nbrOfDocuments = await global.database.getCollection(tenantID, 'importedtags').count(); + const nbrOfDocuments = await global.database.getCollection(tenantID, 'importedtags').countDocuments(); // Debug await Logging.traceEnd(tenantID, MODULE_NAME, 'getImportedTagsCount', uniqueTimerID); return nbrOfDocuments; diff --git a/src/storage/mongodb/TransactionStorage.ts b/src/storage/mongodb/TransactionStorage.ts index bae4cd773c..a58f9f2572 100644 --- a/src/storage/mongodb/TransactionStorage.ts +++ b/src/storage/mongodb/TransactionStorage.ts @@ -12,42 +12,43 @@ import DbParams from '../../types/database/DbParams'; import Logging from '../../utils/Logging'; import { NotifySessionNotStarted } from '../../types/UserNotifications'; import { ServerAction } from '../../types/Server'; +import Tenant from '../../types/Tenant'; import Utils from '../../utils/Utils'; import moment from 'moment'; const MODULE_NAME = 'TransactionStorage'; export default class TransactionStorage { - public static async deleteTransaction(tenantID: string, transactionID: number): Promise { - await TransactionStorage.deleteTransactions(tenantID, [transactionID]); + public static async deleteTransaction(tenant: Tenant, transactionID: number): Promise { + await TransactionStorage.deleteTransactions(tenant, [transactionID]); } - public static async deleteTransactions(tenantID: string, transactionsIDs: number[]): Promise { + public static async deleteTransactions(tenant: Tenant, transactionsIDs: number[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteTransaction'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteTransaction'); // Check - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Delete - const result = await global.database.getCollection(tenantID, 'transactions') + const result = await global.database.getCollection(tenant.id, 'transactions') .deleteMany({ '_id': { $in: transactionsIDs } }); // Delete Meter Values - await global.database.getCollection(tenantID, 'metervalues') + await global.database.getCollection(tenant.id, 'metervalues') .deleteMany({ 'transactionId': { $in: transactionsIDs } }); // Delete Consumptions - await ConsumptionStorage.deleteConsumptions(tenantID, transactionsIDs); + await ConsumptionStorage.deleteConsumptions(tenant.id, transactionsIDs); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteTransaction', uniqueTimerID, { transactionsIDs }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteTransaction', uniqueTimerID, { transactionsIDs }); return result.deletedCount; } - public static async saveTransaction(tenantID: string, transactionToSave: Transaction): Promise { + public static async saveTransaction(tenant: Tenant, transactionToSave: Transaction): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveTransaction'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveTransaction'); // Check - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // ID not provided? if (!transactionToSave.id) { - transactionToSave.id = await TransactionStorage.findAvailableID(tenantID); + transactionToSave.id = await TransactionStorage.findAvailableID(tenant); } // Transfer const transactionMDB: any = { @@ -201,24 +202,24 @@ export default class TransactionStorage { }; } // Modify - await global.database.getCollection(tenantID, 'transactions').findOneAndReplace( + await global.database.getCollection(tenant.id, 'transactions').findOneAndReplace( { '_id': Utils.convertToInt(transactionToSave.id) }, transactionMDB, { upsert: true }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveTransaction', uniqueTimerID, transactionMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveTransaction', uniqueTimerID, transactionMDB); // Return return transactionToSave.id; } - public static async saveTransactionOcpiData(tenantID: string, id: number, + public static async saveTransactionOcpiData(tenant: Tenant, id: number, ocpiData: TransactionOcpiData): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveTransactionOcpiData'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveTransactionOcpiData'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify document - await global.database.getCollection(tenantID, 'transactions').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'transactions').findOneAndUpdate( { '_id': id }, { $set: { @@ -227,17 +228,17 @@ export default class TransactionStorage { }, { upsert: false }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveTransactionOcpiData', uniqueTimerID, ocpiData); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveTransactionOcpiData', uniqueTimerID, ocpiData); } - public static async saveTransactionOicpData(tenantID: string, id: number, + public static async saveTransactionOicpData(tenant: Tenant, id: number, oicpData: TransactionOicpData): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveTransactionOicpData'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveTransactionOicpData'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify document - await global.database.getCollection(tenantID, 'transactions').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'transactions').findOneAndUpdate( { '_id': id }, { $set: { @@ -246,17 +247,17 @@ export default class TransactionStorage { }, { upsert: false }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveTransactionOicpData', uniqueTimerID, oicpData); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveTransactionOicpData', uniqueTimerID, oicpData); } - public static async saveTransactionBillingData(tenantID: string, id: number, + public static async saveTransactionBillingData(tenant: Tenant, id: number, billingData: TransactionBillingData): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveTransactionBillingData'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveTransactionBillingData'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify document - await global.database.getCollection(tenantID, 'transactions').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'transactions').findOneAndUpdate( { '_id': id }, { $set: { @@ -265,17 +266,17 @@ export default class TransactionStorage { }, { upsert: false }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveTransactionBillingData', uniqueTimerID, billingData); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveTransactionBillingData', uniqueTimerID, billingData); } - public static async saveTransactionRefundData(tenantID: string, id: number, + public static async saveTransactionRefundData(tenant: Tenant, id: number, refundData: TransactionRefundData): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveTransactionRefundData'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveTransactionRefundData'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify document - await global.database.getCollection(tenantID, 'transactions').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'transactions').findOneAndUpdate( { '_id': id }, { $set: { @@ -284,14 +285,14 @@ export default class TransactionStorage { }, { upsert: false }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveTransactionRefundData', uniqueTimerID, refundData); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveTransactionRefundData', uniqueTimerID, refundData); } - public static async assignTransactionsToUser(tenantID: string, userID: string, tagID: string): Promise { + public static async assignTransactionsToUser(tenant: Tenant, userID: string, tagID: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'assignTransactionsToUser'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'assignTransactionsToUser'); // Assign transactions - await global.database.getCollection(tenantID, 'transactions').updateMany({ + await global.database.getCollection(tenant.id, 'transactions').updateMany({ $and: [ { 'userID': null }, { 'tagID': tagID } @@ -302,30 +303,30 @@ export default class TransactionStorage { } }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'assignTransactionsToUser', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'assignTransactionsToUser', uniqueTimerID); } - public static async getUnassignedTransactionsCount(tenantID: string, tagID: string): Promise { + public static async getUnassignedTransactionsCount(tenant: Tenant, tagID: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getUnassignedTransactionsCount'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getUnassignedTransactionsCount'); // Get the number of unassigned transactions - const unassignedCount = await global.database.getCollection(tenantID, 'transactions').find({ + const unassignedCount = await global.database.getCollection(tenant.id, 'transactions').find({ $and: [ { 'userID': null }, { 'tagID': tagID } ] }).count(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getUnassignedTransactionsCount', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getUnassignedTransactionsCount', uniqueTimerID); return unassignedCount; } - public static async getTransactionYears(tenantID: string): Promise { + public static async getTransactionYears(tenant: Tenant): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getTransactionYears'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getTransactionYears'); // Check - await DatabaseUtils.checkTenant(tenantID); - const firstTransactionsMDB = await global.database.getCollection(tenantID, 'transactions') + DatabaseUtils.checkTenantObject(tenant); + const firstTransactionsMDB = await global.database.getCollection(tenant.id, 'transactions') .find({}) .sort({ timestamp: 1 }) .limit(1) @@ -340,11 +341,11 @@ export default class TransactionStorage { transactionYears.push(i); } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getTransactionYears', uniqueTimerID, firstTransactionsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getTransactionYears', uniqueTimerID, firstTransactionsMDB); return transactionYears; } - public static async getTransactions(tenantID: string, + public static async getTransactions(tenant: Tenant, params: { transactionIDs?: number[]; issuer?: boolean; search?: string; ownerID?: string; userIDs?: string[]; siteAdminIDs?: string[]; chargeBoxIDs?: string[]; siteAreaIDs?: string[]; siteIDs?: string[]; connectorIDs?: number[]; startDateTime?: Date; withChargingStation?: boolean; @@ -362,9 +363,9 @@ export default class TransactionStorage { }; }> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getTransactions'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getTransactions'); // Check - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -602,7 +603,7 @@ export default class TransactionStorage { break; } // Count Records - const transactionsCountMDB = await global.database.getCollection(tenantID, 'transactions') + const transactionsCountMDB = await global.database.getCollection(tenant.id, 'transactions') .aggregate([...aggregation, statsQuery], { allowDiskUse: true }) .toArray(); let transactionCountMDB = (transactionsCountMDB && transactionsCountMDB.length > 0) ? transactionsCountMDB[0] : null; @@ -647,7 +648,7 @@ export default class TransactionStorage { } // Check if only the total count is requested if (dbParams.onlyRecordCount) { - await Logging.traceEnd(tenantID, MODULE_NAME, 'getTransactions', uniqueTimerID, transactionCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getTransactions', uniqueTimerID, transactionCountMDB); return { count: transactionCountMDB ? transactionCountMDB.count : 0, stats: transactionCountMDB ? transactionCountMDB : {}, @@ -697,7 +698,7 @@ export default class TransactionStorage { // Tag if (params.withTag) { DatabaseUtils.pushTagLookupInAggregation({ - tenantID, aggregation: aggregation, asField: 'tag', localField: 'tagID', + tenantID: tenant.id, aggregation: aggregation, asField: 'tag', localField: 'tagID', foreignField: '_id', oneToOneCardinality: true }); // TODO: [To Investigate] Cause big perf issue in prod (local it takes 2sec with this lookup instead of 165ms, in prod it can takes up to 20s) @@ -709,28 +710,28 @@ export default class TransactionStorage { // Company if (params.withCompany) { DatabaseUtils.pushCompanyLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'companyID', foreignField: '_id', + tenantID: tenant.id, aggregation: aggregation, localField: 'companyID', foreignField: '_id', asField: 'company', oneToOneCardinality: true }); } // Site if (params.withSite) { DatabaseUtils.pushSiteLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'siteID', foreignField: '_id', + tenantID: tenant.id, aggregation: aggregation, localField: 'siteID', foreignField: '_id', asField: 'site', oneToOneCardinality: true }); } // Site Area if (params.withSiteArea) { DatabaseUtils.pushSiteAreaLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'siteAreaID', foreignField: '_id', + tenantID: tenant.id, aggregation: aggregation, localField: 'siteAreaID', foreignField: '_id', asField: 'siteArea', oneToOneCardinality: true }); } // Charging Station if (params.withChargingStation) { 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'); @@ -754,18 +755,18 @@ export default class TransactionStorage { // User if (params.withUser) { DatabaseUtils.pushUserLookupInAggregation({ - tenantID, aggregation: aggregation, asField: 'user', localField: 'userID', + tenantID: tenant.id, aggregation: aggregation, asField: 'user', localField: 'userID', foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); DatabaseUtils.pushUserLookupInAggregation({ - tenantID, aggregation: aggregation, asField: 'stop.user', localField: 'stop.userID', + tenantID: tenant.id, aggregation: aggregation, asField: 'stop.user', localField: 'stop.userID', foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); } // Car if (params.withCar) { DatabaseUtils.pushCarLookupInAggregation({ - tenantID, aggregation: aggregation, asField: 'car', localField: 'carID', + tenantID: tenant.id, aggregation: aggregation, asField: 'car', localField: 'carID', foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); DatabaseUtils.pushCarCatalogLookupInAggregation({ @@ -787,13 +788,13 @@ export default class TransactionStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const transactionsMDB = await global.database.getCollection(tenantID, 'transactions') + const transactionsMDB = await global.database.getCollection(tenant.id, 'transactions') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getTransactions', uniqueTimerID, transactionsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getTransactions', uniqueTimerID, transactionsMDB); return { count: transactionCountMDB ? (transactionCountMDB.count === Constants.DB_RECORD_COUNT_CEIL ? -1 : transactionCountMDB.count) : 0, stats: transactionCountMDB ? transactionCountMDB : {}, @@ -801,13 +802,13 @@ export default class TransactionStorage { }; } - public static async getRefundReports(tenantID: string, + public static async getRefundReports(tenant: Tenant, params: { ownerID?: string; siteAdminIDs?: string[] }, dbParams: DbParams, projectFields?: string[]): Promise<{ count: number; result: RefundReport[] }> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getTransactions'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getTransactions'); // Check - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -863,7 +864,7 @@ export default class TransactionStorage { } }; // Count Records - const transactionsCountMDB = await global.database.getCollection(tenantID, 'transactions') + const transactionsCountMDB = await global.database.getCollection(tenant.id, 'transactions') .aggregate([...aggregation, statsQuery], { allowDiskUse: true }) .toArray(); let reportCountMDB = (transactionsCountMDB && transactionsCountMDB.length > 0) ? transactionsCountMDB[0] : null; @@ -875,7 +876,7 @@ export default class TransactionStorage { } // Check if only the total count is requested if (dbParams.onlyRecordCount) { - await Logging.traceEnd(tenantID, MODULE_NAME, 'getRefundReports', uniqueTimerID, reportCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getRefundReports', uniqueTimerID, reportCountMDB); return { count: reportCountMDB ? reportCountMDB.count : 0, result: [] @@ -909,7 +910,7 @@ export default class TransactionStorage { }); // Add respective 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 }); // Rename ID @@ -919,29 +920,29 @@ export default class TransactionStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const reportsMDB = await global.database.getCollection(tenantID, 'transactions') + const reportsMDB = await global.database.getCollection(tenant.id, 'transactions') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getRefundReports', uniqueTimerID, reportsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getRefundReports', uniqueTimerID, reportsMDB); return { count: reportCountMDB ? (reportCountMDB.count === Constants.DB_RECORD_COUNT_CEIL ? -1 : reportCountMDB.count) : 0, result: reportsMDB }; } - static async getTransactionsInError(tenantID: string, + static async getTransactionsInError(tenant: Tenant, params: { search?: string; issuer?: boolean; userIDs?: string[]; chargingStationIDs?: string[]; siteAreaIDs?: string[]; siteIDs?: string[]; startDateTime?: Date; endDateTime?: Date; withChargingStations?: boolean; errorType?: TransactionInErrorType[]; connectorIDs?: number[]; }, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getTransactionsInError'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getTransactionsInError'); // Check - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -1010,14 +1011,14 @@ export default class TransactionStorage { (params.errorType && params.errorType.includes(TransactionInErrorType.OVER_CONSUMPTION))) { // Add Charge Box DatabaseUtils.pushChargingStationLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'chargeBoxID', foreignField: '_id', asField: 'chargeBox', + tenantID: tenant.id, aggregation: aggregation, localField: 'chargeBoxID', foreignField: '_id', asField: 'chargeBox', oneToOneCardinality: true, oneToOneCardinalityNotNull: false, pipelineMatch: { 'issuer': true } }); DatabaseUtils.pushConvertObjectIDToString(aggregation, 'chargeBox.siteAreaID'); } // User DatabaseUtils.pushUserLookupInAggregation({ - tenantID, aggregation: aggregation, asField: 'user', localField: 'userID', + tenantID: tenant.id, aggregation: aggregation, asField: 'user', localField: 'userID', foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); // Car Catalog @@ -1029,7 +1030,7 @@ export default class TransactionStorage { if (params.errorType && params.errorType.includes(TransactionInErrorType.MISSING_USER)) { // Site Area DatabaseUtils.pushSiteAreaLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'siteAreaID', foreignField: '_id', + tenantID: tenant.id, aggregation: aggregation, localField: 'siteAreaID', foreignField: '_id', asField: 'siteArea', oneToOneCardinality: true }); } @@ -1051,7 +1052,7 @@ export default class TransactionStorage { } // Users DatabaseUtils.pushUserLookupInAggregation({ - tenantID, aggregation: aggregation, asField: 'stop.user', localField: 'stop.userID', + tenantID: tenant.id, aggregation: aggregation, asField: 'stop.user', localField: 'stop.userID', foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); // Rename ID @@ -1083,22 +1084,22 @@ export default class TransactionStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const transactionsMDB = await global.database.getCollection(tenantID, 'transactions') + const transactionsMDB = await global.database.getCollection(tenant.id, 'transactions') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getTransactionsInError', uniqueTimerID, transactionsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getTransactionsInError', uniqueTimerID, transactionsMDB); return { count: transactionsMDB.length, result: transactionsMDB }; } - public static async getTransaction(tenantID: string, id: number = Constants.UNKNOWN_NUMBER_ID, + public static async getTransaction(tenant: Tenant, id: number = Constants.UNKNOWN_NUMBER_ID, params: { withTag?: boolean; withCar?: boolean; withUser?: boolean, withChargingStation?: boolean } = {}, projectFields?: string[]): Promise { - const transactionsMDB = await TransactionStorage.getTransactions(tenantID, { + const transactionsMDB = await TransactionStorage.getTransactions(tenant, { transactionIDs: [id], withTag: params.withTag, withCar: params.withCar, @@ -1108,35 +1109,35 @@ export default class TransactionStorage { return transactionsMDB.count === 1 ? transactionsMDB.result[0] : null; } - public static async getOCPITransactionBySessionID(tenantID: string, sessionID: string): Promise { - const transactionsMDB = await TransactionStorage.getTransactions(tenantID, + public static async getOCPITransactionBySessionID(tenant: Tenant, sessionID: string): Promise { + const transactionsMDB = await TransactionStorage.getTransactions(tenant, { ocpiSessionID: sessionID }, Constants.DB_PARAMS_SINGLE_RECORD); return transactionsMDB.count === 1 ? transactionsMDB.result[0] : null; } - public static async getOCPITransactionByAuthorizationID(tenantID: string, authorizationID: string): Promise { - const transactionsMDB = await TransactionStorage.getTransactions(tenantID, + public static async getOCPITransactionByAuthorizationID(tenant: Tenant, authorizationID: string): Promise { + const transactionsMDB = await TransactionStorage.getTransactions(tenant, { ocpiAuthorizationID: authorizationID }, Constants.DB_PARAMS_SINGLE_RECORD); return transactionsMDB.count === 1 ? transactionsMDB.result[0] : null; } - public static async getOICPTransactionBySessionID(tenantID: string, oicpSessionID: string): Promise { - const transactionsMDB = await TransactionStorage.getTransactions(tenantID, + public static async getOICPTransactionBySessionID(tenant: Tenant, oicpSessionID: string): Promise { + const transactionsMDB = await TransactionStorage.getTransactions(tenant, { oicpSessionID: oicpSessionID }, Constants.DB_PARAMS_SINGLE_RECORD); return transactionsMDB.count === 1 ? transactionsMDB.result[0] : null; } - public static async getActiveTransaction(tenantID: string, chargeBoxID: string, connectorId: number): Promise { + public static async getActiveTransaction(tenant: Tenant, chargeBoxID: string, connectorId: number): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getActiveTransaction'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getActiveTransaction'); // Check - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); const aggregation = []; // Filters aggregation.push({ @@ -1148,7 +1149,7 @@ export default class TransactionStorage { }); // Add User DatabaseUtils.pushUserLookupInAggregation({ - tenantID, aggregation, localField: 'userID', foreignField: '_id', asField: 'user', + tenantID: tenant.id, aggregation, localField: 'userID', foreignField: '_id', asField: 'user', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); // Rename ID @@ -1163,20 +1164,20 @@ export default class TransactionStorage { DatabaseUtils.clearFieldValueIfSubFieldIsNull(aggregation, 'stop', 'timestamp'); DatabaseUtils.clearFieldValueIfSubFieldIsNull(aggregation, 'remotestop', 'timestamp'); // Read DB - const transactionsMDB = await global.database.getCollection(tenantID, 'transactions') + const transactionsMDB = await global.database.getCollection(tenant.id, 'transactions') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getActiveTransaction', uniqueTimerID, transactionsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getActiveTransaction', uniqueTimerID, transactionsMDB); return transactionsMDB.length === 1 ? transactionsMDB[0] : null; } - public static async getLastTransactionFromChargingStation(tenantID: string, chargeBoxID: string, connectorId: number, + public static async getLastTransactionFromChargingStation(tenant: Tenant, chargeBoxID: string, connectorId: number, params: { withChargingStation?: boolean; withUser?: boolean; withTag: boolean; }): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getLastTransaction'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getLastTransaction'); // Check - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); const aggregation = []; // Filters aggregation.push({ @@ -1198,21 +1199,21 @@ export default class TransactionStorage { // Add Charging Station if (params.withChargingStation) { DatabaseUtils.pushChargingStationLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'chargeBoxID', foreignField: '_id', + tenantID: tenant.id, aggregation: aggregation, localField: 'chargeBoxID', foreignField: '_id', asField: 'chargeBox', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); } // Add User if (params.withUser) { DatabaseUtils.pushUserLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'userID', foreignField: '_id', + tenantID: tenant.id, aggregation: aggregation, localField: 'userID', foreignField: '_id', asField: 'user', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); } // Tag if (params.withTag) { DatabaseUtils.pushTagLookupInAggregation({ - tenantID, aggregation: aggregation, asField: 'tag', localField: 'tagID', + tenantID: tenant.id, aggregation: aggregation, asField: 'tag', localField: 'tagID', foreignField: '_id', oneToOneCardinality: true }); } @@ -1229,27 +1230,27 @@ export default class TransactionStorage { DatabaseUtils.clearFieldValueIfSubFieldIsNull(aggregation, 'stop', 'timestamp'); DatabaseUtils.clearFieldValueIfSubFieldIsNull(aggregation, 'remotestop', 'timestamp'); // Read DB - const transactionsMDB = await global.database.getCollection(tenantID, 'transactions') + const transactionsMDB = await global.database.getCollection(tenant.id, 'transactions') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getLastTransaction', uniqueTimerID, transactionsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getLastTransaction', uniqueTimerID, transactionsMDB); return transactionsMDB.length === 1 ? transactionsMDB[0] : null; } - public static async findAvailableID(tenantID: string): Promise { + public static async findAvailableID(tenant: Tenant): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, '_findAvailableID'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, '_findAvailableID'); // Check - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); let existingTransaction: Transaction; do { // Generate new transaction ID const id = Utils.getRandomIntSafe(); - existingTransaction = await TransactionStorage.getTransaction(tenantID, id); + existingTransaction = await TransactionStorage.getTransaction(tenant, id); if (existingTransaction) { await Logging.logWarning({ - tenantID: tenantID, + tenantID: tenant.id, module: MODULE_NAME, method: '_findAvailableID', action: ServerAction.TRANSACTION_STARTED, message: `Transaction ID '${id}' already exists, generating a new one...` @@ -1259,15 +1260,15 @@ export default class TransactionStorage { } } while (existingTransaction); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, '_findAvailableID', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, '_findAvailableID', uniqueTimerID); } - public static async getNotStartedTransactions(tenantID: string, + public static async getNotStartedTransactions(tenant: Tenant, params: { checkPastAuthorizeMins: number; sessionShouldBeStartedAfterMins: number }): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getNotStartedTransactions'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getNotStartedTransactions'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Compute the date some minutes ago const authorizeStartDate = moment().subtract(params.checkPastAuthorizeMins, 'minutes').toDate(); const authorizeEndDate = moment().subtract(params.sessionShouldBeStartedAfterMins, 'minutes').toDate(); @@ -1311,7 +1312,7 @@ export default class TransactionStorage { // Lookup for transactions aggregation.push({ $lookup: { - from: DatabaseUtils.getCollectionName(tenantID, 'transactions'), + from: DatabaseUtils.getCollectionName(tenant.id, 'transactions'), let: { tagID: '$_id', dateStart: '$dateStart', dateEnd: '$dateEnd' }, pipeline: [{ $match: { @@ -1344,12 +1345,12 @@ export default class TransactionStorage { }); // Lookup for users DatabaseUtils.pushUserLookupInAggregation({ - tenantID, aggregation, localField: 'userID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: 'userID', foreignField: '_id', asField: 'user', oneToOneCardinality: true, oneToOneCardinalityNotNull: true }); // Lookup for charging station DatabaseUtils.pushChargingStationLookupInAggregation({ - tenantID, aggregation, localField: 'chargeBoxID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: 'chargeBoxID', foreignField: '_id', asField: 'chargingStation', oneToOneCardinality: true, oneToOneCardinalityNotNull: true }); DatabaseUtils.pushConvertObjectIDToString(aggregation, 'chargingStation.siteAreaID'); @@ -1365,13 +1366,13 @@ export default class TransactionStorage { }); // Read DB const notifySessionNotStartedMDB: NotifySessionNotStarted[] = - await global.database.getCollection(tenantID, 'authorizes') + await global.database.getCollection(tenant.id, 'authorizes') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getNotStartedTransactions', uniqueTimerID, notifySessionNotStartedMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getNotStartedTransactions', uniqueTimerID, notifySessionNotStartedMDB); return { count: notifySessionNotStartedMDB.length, result: notifySessionNotStartedMDB diff --git a/src/storage/mongodb/UserStorage.ts b/src/storage/mongodb/UserStorage.ts index fdbe56cab7..1ff7c504e4 100644 --- a/src/storage/mongodb/UserStorage.ts +++ b/src/storage/mongodb/UserStorage.ts @@ -17,6 +17,7 @@ import Logging from '../../utils/Logging'; import Mustache from 'mustache'; import { ObjectId } from 'mongodb'; import TagStorage from './TagStorage'; +import Tenant from '../../types/Tenant'; import TenantComponents from '../../types/TenantComponents'; import TenantStorage from './TenantStorage'; import UserNotifications from '../../types/UserNotifications'; @@ -27,11 +28,11 @@ import moment from 'moment'; const MODULE_NAME = 'UserStorage'; export default class UserStorage { - public static async getEndUserLicenseAgreement(tenantID: string, language = 'en'): Promise { + public static async getEndUserLicenseAgreement(tenant: Tenant, language = 'en'): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getEndUserLicenseAgreement'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getEndUserLicenseAgreement'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); let currentEulaHash: string; // Supported languages? if (!Constants.SUPPORTED_LANGUAGES.includes(language)) { @@ -41,7 +42,7 @@ export default class UserStorage { // Get current eula const currentEula = UserStorage.getEndUserLicenseAgreementFromFile(language); // Read DB - const eulasMDB = await global.database.getCollection(tenantID, 'eulas') + const eulasMDB = await global.database.getCollection(tenant.id, 'eulas') .find({ 'language': language }) .sort({ 'version': -1 }) .limit(1) @@ -62,14 +63,14 @@ export default class UserStorage { hash: currentEulaHash }; // Create - await global.database.getCollection(tenantID, 'eulas') + await global.database.getCollection(tenant.id, 'eulas') .insertOne(eula); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getEndUserLicenseAgreement', uniqueTimerID, eula); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getEndUserLicenseAgreement', uniqueTimerID, eula); return eula; } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getEndUserLicenseAgreement', uniqueTimerID, eulaMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getEndUserLicenseAgreement', uniqueTimerID, eulaMDB); return eulaMDB; } // Create default @@ -81,35 +82,35 @@ export default class UserStorage { hash: Cypher.hash(currentEula) }; // Create - await global.database.getCollection(tenantID, 'eulas').insertOne(eula); + await global.database.getCollection(tenant.id, 'eulas').insertOne(eula); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getEndUserLicenseAgreement', uniqueTimerID, eula); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getEndUserLicenseAgreement', uniqueTimerID, eula); // Return return eula; } - public static async getUserByTagId(tenantID: string, tagID: string = Constants.UNKNOWN_STRING_ID): Promise { - const tagMDB = await TagStorage.getTag(tenantID, tagID, { withUser: true }); + public static async getUserByTagId(tenant: Tenant, tagID: string = Constants.UNKNOWN_STRING_ID): Promise { + const tagMDB = await TagStorage.getTag(tenant.id, tagID, { withUser: true }); return tagMDB ? tagMDB.user : null; } - public static async getUserByEmail(tenantID: string, email: string = Constants.UNKNOWN_STRING_ID): Promise { - const userMDB = await UserStorage.getUsers(tenantID, { + public static async getUserByEmail(tenant: Tenant, email: string = Constants.UNKNOWN_STRING_ID): Promise { + const userMDB = await UserStorage.getUsers(tenant, { email: email, }, Constants.DB_PARAMS_SINGLE_RECORD); return userMDB.count === 1 ? userMDB.result[0] : null; } - public static async getUserByPasswordResetHash(tenantID: string, passwordResetHash: string = Constants.UNKNOWN_STRING_ID): Promise { - const userMDB = await UserStorage.getUsers(tenantID, { + public static async getUserByPasswordResetHash(tenant: Tenant, passwordResetHash: string = Constants.UNKNOWN_STRING_ID): Promise { + const userMDB = await UserStorage.getUsers(tenant, { passwordResetHash: passwordResetHash }, Constants.DB_PARAMS_SINGLE_RECORD); return userMDB.count === 1 ? userMDB.result[0] : null; } - public static async getUser(tenantID: string, id: string = Constants.UNKNOWN_OBJECT_ID, + public static async getUser(tenant: Tenant, id: string = Constants.UNKNOWN_OBJECT_ID, params: { withImage?: boolean; siteIDs?: string[]; } = {}, projectFields?: string[]): Promise { - const userMDB = await UserStorage.getUsers(tenantID, { + const userMDB = await UserStorage.getUsers(tenant, { userIDs: [id], withImage: params.withImage, siteIDs: params.siteIDs, @@ -117,53 +118,53 @@ export default class UserStorage { return userMDB.count === 1 ? userMDB.result[0] : null; } - public static async getUserByBillingID(tenantID: string, billingID: string): Promise { - const userMDB = await UserStorage.getUsers(tenantID, { + public static async getUserByBillingID(tenant: Tenant, billingID: string): Promise { + const userMDB = await UserStorage.getUsers(tenant, { billingUserID: billingID }, Constants.DB_PARAMS_SINGLE_RECORD); return userMDB.count === 1 ? userMDB.result[0] : null; } - public static async getUserImage(tenantID: string, id: string): Promise { + public static async getUserImage(tenant: Tenant, id: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getUserImage'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getUserImage'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Read DB - const userImageMDB = await global.database.getCollection<{ _id: ObjectId; image: string }>(tenantID, 'userimages') + const userImageMDB = await global.database.getCollection<{ _id: ObjectId; image: string }>(tenant.id, 'userimages') .findOne({ _id: DatabaseUtils.convertToObjectID(id) }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getUserImage', uniqueTimerID, userImageMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getUserImage', uniqueTimerID, userImageMDB); return { id: id, image: (userImageMDB ? userImageMDB.image : null) }; } - public static async removeSitesFromUser(tenantID: string, userID: string, siteIDs: string[]): Promise { + public static async removeSitesFromUser(tenant: Tenant, userID: string, siteIDs: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'removeSitesFromUser'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'removeSitesFromUser'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // User provided? if (userID) { // At least one Site if (!Utils.isEmptyArray(siteIDs)) { // Create the lis - await global.database.getCollection(tenantID, 'siteusers').deleteMany({ + await global.database.getCollection(tenant.id, 'siteusers').deleteMany({ 'userID': DatabaseUtils.convertToObjectID(userID), 'siteID': { $in: siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) } }); } } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'removeSitesFromUser', uniqueTimerID, siteIDs); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'removeSitesFromUser', uniqueTimerID, siteIDs); } - public static async addSitesToUser(tenantID: string, userID: string, siteIDs: string[]): Promise { + public static async addSitesToUser(tenant: Tenant, userID: string, siteIDs: string[]): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'addSitesToUser'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'addSitesToUser'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // At least one Site if (!Utils.isEmptyArray(siteIDs)) { const siteUsersMDB = []; @@ -177,17 +178,39 @@ export default class UserStorage { }); } // Execute - await global.database.getCollection(tenantID, 'siteusers').insertMany(siteUsersMDB); + await global.database.getCollection(tenant.id, 'siteusers').insertMany(siteUsersMDB); } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'addSitesToUser', uniqueTimerID, siteIDs); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'addSitesToUser', uniqueTimerID, siteIDs); } - public static async saveUser(tenantID: string, userToSave: User, saveImage = false): Promise { + public static async addSiteToUser(tenant: Tenant, userID: string, siteID: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUser'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'addSitesToUser'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); + const siteUserMDB = { + '_id': Cypher.hash(`${siteID}~${userID}`), + 'userID': DatabaseUtils.convertToObjectID(userID), + 'siteID': DatabaseUtils.convertToObjectID(siteID), + 'siteAdmin': false + }; + // Execute + await global.database.getCollection(tenant.id, 'siteusers').findOneAndUpdate( + { userID: siteUserMDB.userID, siteID: siteUserMDB.siteID }, + { $set: siteUserMDB }, + { upsert: true } + ); + // Debug + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUser', uniqueTimerID, siteID); + return siteUserMDB._id; + } + + public static async saveUser(tenant: Tenant, userToSave: User, saveImage = false): Promise { + // Debug + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUser'); + // Check Tenant + DatabaseUtils.checkTenantObject(tenant); // Check if ID or email is provided if (!userToSave.id && !userToSave.email) { throw new BackendError({ @@ -217,6 +240,7 @@ export default class UserStorage { locale: userToSave.locale, iNumber: userToSave.iNumber, costCenter: userToSave.costCenter, + importedData: userToSave.importedData, notificationsActive: userToSave.notificationsActive, notifications: { sendSessionStarted: userToSave.notifications ? Utils.convertToBoolean(userToSave.notifications.sendSessionStarted) : false, @@ -261,21 +285,21 @@ export default class UserStorage { // Check Created/Last Changed By DatabaseUtils.addLastChangedCreatedProps(userMDB, userToSave); // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( userFilter, { $set: userMDB }, { upsert: true, returnDocument: 'after' }); // Delegate saving image as well if specified if (saveImage) { - await UserStorage.saveUserImage(tenantID, userMDB._id.toString(), userToSave.image); + await UserStorage.saveUserImage(tenant, userMDB._id.toString(), userToSave.image); } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUser', uniqueTimerID, userMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUser', uniqueTimerID, userMDB); return userMDB._id.toString(); } - public static async saveImportedUser(tenantID: string, importedUserToSave: ImportedUser): Promise { - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveImportedUser'); + public static async saveImportedUser(tenant: Tenant, importedUserToSave: ImportedUser): Promise { + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveImportedUser'); const userMDB = { _id: importedUserToSave.id ? DatabaseUtils.convertToObjectID(importedUserToSave.id) : new ObjectId(), email: importedUserToSave.email, @@ -284,20 +308,22 @@ export default class UserStorage { status: importedUserToSave.status, errorDescription: importedUserToSave.errorDescription, importedOn: Utils.convertToDate(importedUserToSave.importedOn), - importedBy: DatabaseUtils.convertToObjectID(importedUserToSave.importedBy) + importedBy: DatabaseUtils.convertToObjectID(importedUserToSave.importedBy), + importedData: importedUserToSave.importedData, + siteIDs: importedUserToSave.siteIDs, }; - await global.database.getCollection(tenantID, 'importedusers').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'importedusers').findOneAndUpdate( { _id: userMDB._id }, { $set: userMDB }, { upsert: true, returnDocument: 'after' } ); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveImportedUser', uniqueTimerID, userMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveImportedUser', uniqueTimerID, userMDB); return userMDB._id.toString(); } - public static async saveImportedUsers(tenantID: string, importedUsersToSave: ImportedUser[]): Promise { - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveImportedUsers'); + public static async saveImportedUsers(tenant: Tenant, importedUsersToSave: ImportedUser[]): Promise { + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveImportedUsers'); const importedUsersToSaveMDB: any = importedUsersToSave.map((importedUserToSave) => ({ _id: importedUserToSave.id ? DatabaseUtils.convertToObjectID(importedUserToSave.id) : new ObjectId(), email: importedUserToSave.email, @@ -306,161 +332,163 @@ export default class UserStorage { status: importedUserToSave.status, errorDescription: importedUserToSave.errorDescription, importedOn: Utils.convertToDate(importedUserToSave.importedOn), - importedBy: DatabaseUtils.convertToObjectID(importedUserToSave.importedBy) + importedBy: DatabaseUtils.convertToObjectID(importedUserToSave.importedBy), + importedData: importedUserToSave.importedData, + siteIDs: importedUserToSave.siteIDs, })); // Insert all at once - const result = await global.database.getCollection(tenantID, 'importedusers').insertMany( + const result = await global.database.getCollection(tenant.id, 'importedusers').insertMany( importedUsersToSaveMDB, { ordered: false } ); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveImportedUsers', uniqueTimerID, importedUsersToSave); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveImportedUsers', uniqueTimerID, importedUsersToSave); return result.insertedCount; } - public static async deleteImportedUser(tenantID: string, importedUserID: string): Promise { + public static async deleteImportedUser(tenant: Tenant, importedUserID: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteImportedUser'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteImportedUser'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Delete - await global.database.getCollection(tenantID, 'importedusers').deleteOne( + await global.database.getCollection(tenant.id, 'importedusers').deleteOne( { '_id': DatabaseUtils.convertToObjectID(importedUserID), }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteImportedUser', uniqueTimerID, { id: importedUserID }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteImportedUser', uniqueTimerID, { id: importedUserID }); } - public static async deleteImportedUsers(tenantID: string): Promise { + public static async deleteImportedUsers(tenant: Tenant): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteImportedUsers'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteImportedUsers'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Delete - await global.database.getCollection(tenantID, 'importedusers').deleteMany({}); + await global.database.getCollection(tenant.id, 'importedusers').deleteMany({}); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteImportedUsers', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteImportedUsers', uniqueTimerID); } - public static async saveUserPassword(tenantID: string, userID: string, + public static async saveUserPassword(tenant: Tenant, userID: string, params: { password?: string; passwordResetHash?: string; passwordWrongNbrTrials?: number; passwordBlockedUntil?: Date; }): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserPassword'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserPassword'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: params }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserPassword', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserPassword', uniqueTimerID); } - public static async saveUserStatus(tenantID: string, userID: string, status: UserStatus): Promise { + public static async saveUserStatus(tenant: Tenant, userID: string, status: UserStatus): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserStatus'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserStatus'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: { status } }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserStatus', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserStatus', uniqueTimerID); } - public static async saveUserLastSelectedCarID(tenantID: string, userID: string, lastSelectedCarID: string): Promise { + public static async saveUserLastSelectedCarID(tenant: Tenant, userID: string, lastSelectedCarID: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserStatus'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserStatus'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: { lastSelectedCarID } }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserStatus', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserStatus', uniqueTimerID); } - public static async saveUserMobileToken(tenantID: string, userID: string, + public static async saveUserMobileToken(tenant: Tenant, userID: string, params: { mobileToken: string; mobileOs: string; mobileLastChangedOn: Date }): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserMobileToken'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserMobileToken'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: params }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserMobileToken', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserMobileToken', uniqueTimerID); } - public static async saveUserMobilePhone(tenantID: string, userID: string, + public static async saveUserMobilePhone(tenant: Tenant, userID: string, params: { mobile?: string; phone?: string; }): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserMobilePhone'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserMobilePhone'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: params }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserMobilePhone', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserMobilePhone', uniqueTimerID); } - public static async saveUserRole(tenantID: string, userID: string, role: string): Promise { + public static async saveUserRole(tenant: Tenant, userID: string, role: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserRole'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserRole'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: { role } }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserRole', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserRole', uniqueTimerID); } - public static async saveUserEULA(tenantID: string, userID: string, + public static async saveUserEULA(tenant: Tenant, userID: string, params: { eulaAcceptedHash: string; eulaAcceptedOn: Date; eulaAcceptedVersion: number }): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserRole'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserRole'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: params }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserRole', uniqueTimerID, params); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserRole', uniqueTimerID, params); } - public static async saveUserAccountVerification(tenantID: string, userID: string, + public static async saveUserAccountVerification(tenant: Tenant, userID: string, params: { verificationToken?: string; verifiedAt?: Date }): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserAccountVerification'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserAccountVerification'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: params }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserAccountVerification', uniqueTimerID, params); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserAccountVerification', uniqueTimerID, params); } - public static async saveUserAdminData(tenantID: string, userID: string, + public static async saveUserAdminData(tenant: Tenant, userID: string, params: { plateID?: string; notificationsActive?: boolean; notifications?: UserNotifications }): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserAdminData'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserAdminData'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Set data const updatedUserMDB: any = {}; // Set only provided values @@ -474,18 +502,18 @@ export default class UserStorage { updatedUserMDB.notifications = params.notifications; } // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: updatedUserMDB }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserAdminData', uniqueTimerID, params); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserAdminData', uniqueTimerID, params); } - public static async saveUserBillingData(tenantID: string, userID: string, billingData: BillingUserData): Promise { + public static async saveUserBillingData(tenant: Tenant, userID: string, billingData: BillingUserData): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserBillingData'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserBillingData'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); if (billingData) { // Set data const updatedUserMDB: any = { @@ -498,23 +526,23 @@ export default class UserStorage { } }; // Modify and return the modified document - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: updatedUserMDB }); } else { - await global.database.getCollection(tenantID, 'users').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'users').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $unset: { billingData: '' } }); // This removes the field from the document } // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserBillingData', uniqueTimerID, billingData); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserBillingData', uniqueTimerID, billingData); } - public static async saveUserImage(tenantID: string, userID: string, userImageToSave: string): Promise { + public static async saveUserImage(tenant: Tenant, userID: string, userImageToSave: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveUserImage'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveUserImage'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Check if ID is provided if (!userID) { // ID must be provided! @@ -526,15 +554,15 @@ export default class UserStorage { }); } // Modify and return the modified document - await global.database.getCollection(tenantID, 'userimages').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'userimages').findOneAndUpdate( { '_id': DatabaseUtils.convertToObjectID(userID) }, { $set: { image: userImageToSave } }, { upsert: true, returnDocument: 'after' }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveUserImage', uniqueTimerID, userImageToSave); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveUserImage', uniqueTimerID, userImageToSave); } - public static async getUsers(tenantID: string, + public static async getUsers(tenant: Tenant, params: { notificationsActive?: boolean; siteIDs?: string[]; excludeSiteID?: string; search?: string; userIDs?: string[]; email?: string; issuer?: boolean; passwordResetHash?: string; roles?: string[]; @@ -543,9 +571,9 @@ export default class UserStorage { }, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getUsers'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getUsers'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -630,7 +658,7 @@ export default class UserStorage { // Add Site if (params.siteIDs || params.excludeSiteID) { DatabaseUtils.pushSiteUserLookupInAggregation({ - tenantID, aggregation, localField: '_id', foreignField: 'userID', asField: 'siteusers' + tenantID: tenant.id, aggregation, localField: '_id', foreignField: 'userID', asField: 'siteusers' }); if (params.siteIDs) { aggregation.push({ @@ -649,13 +677,13 @@ export default class UserStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const usersCountMDB = await global.database.getCollection(tenantID, 'users') + const usersCountMDB = await global.database.getCollection(tenant.id, 'users') .aggregate([...aggregation, { $count: 'count' }], { allowDiskUse: true }) .toArray(); // Check if only the total count is requested if (dbParams.onlyRecordCount) { // Return only the count - await Logging.traceEnd(tenantID, MODULE_NAME, 'getUsers', uniqueTimerID, usersCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getUsers', uniqueTimerID, usersCountMDB); return { count: (!Utils.isEmptyArray(usersCountMDB) ? usersCountMDB[0].count : 0), result: [] @@ -681,17 +709,17 @@ export default class UserStorage { // Change ID DatabaseUtils.pushRenameDatabaseID(aggregation); // Add Created By / Last Changed By - DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation); + DatabaseUtils.pushCreatedLastChangedInAggregation(tenant.id, aggregation); // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const usersMDB = await global.database.getCollection(tenantID, 'users') + const usersMDB = await global.database.getCollection(tenant.id, 'users') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getUsers', uniqueTimerID, usersMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getUsers', uniqueTimerID, usersMDB); // Ok return { count: (!Utils.isEmptyArray(usersCountMDB) ? @@ -701,25 +729,25 @@ export default class UserStorage { }; } - public static async getImportedUsersCount(tenantID: string): Promise { + public static async getImportedUsersCount(tenant: Tenant): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getImportedUsersCount'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getImportedUsersCount'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Count documents - const nbrOfDocuments = await global.database.getCollection(tenantID, 'importedusers').count(); + const nbrOfDocuments = await global.database.getCollection(tenant.id, 'importedusers').countDocuments(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getImportedUsersCount', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getImportedUsersCount', uniqueTimerID); return nbrOfDocuments; } - public static async getImportedUsers(tenantID: string, + public static async getImportedUsers(tenant: Tenant, params: { status?: ImportStatus; search?: string }, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getImportedUsers'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getImportedUsers'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -751,13 +779,13 @@ export default class UserStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const usersImportCountMDB = await global.database.getCollection(tenantID, 'importedusers') + const usersImportCountMDB = await global.database.getCollection(tenant.id, 'importedusers') .aggregate([...aggregation, { $count: 'count' }], { allowDiskUse: true }) .toArray(); // Check if only the total count is requested if (dbParams.onlyRecordCount) { // Return only the count - await Logging.traceEnd(tenantID, MODULE_NAME, 'getImportedUsers', uniqueTimerID, usersImportCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getImportedUsers', uniqueTimerID, usersImportCountMDB); return { count: (!Utils.isEmptyArray(usersImportCountMDB) ? usersImportCountMDB[0].count : 0), result: [] @@ -785,17 +813,17 @@ export default class UserStorage { // Convert Object ID to string DatabaseUtils.pushConvertObjectIDToString(aggregation, 'importedBy'); // Add Created By / Last Changed By - DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation); + DatabaseUtils.pushCreatedLastChangedInAggregation(tenant.id, aggregation); // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const usersImportMDB = await global.database.getCollection(tenantID, 'importedusers') + const usersImportMDB = await global.database.getCollection(tenant.id, 'importedusers') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getUsersImport', uniqueTimerID, usersImportMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getUsersImport', uniqueTimerID, usersImportMDB); // Ok return { count: (!Utils.isEmptyArray(usersImportCountMDB) ? @@ -804,13 +832,13 @@ export default class UserStorage { }; } - public static async getUsersInError(tenantID: string, + public static async getUsersInError(tenant: Tenant, params: { search?: string; roles?: string[]; errorTypes?: string[] }, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getUsers'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getUsers'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -841,7 +869,7 @@ export default class UserStorage { // Mongodb Lookup block // Add Tags DatabaseUtils.pushTagLookupInAggregation({ - tenantID, aggregation, localField: '_id', foreignField: 'userID', asField: 'tags' + tenantID: tenant.id, aggregation, localField: '_id', foreignField: 'userID', asField: 'tags' }); // Mongodb facets block // If the organization component is active the system looks for non active users or active users that @@ -849,7 +877,6 @@ export default class UserStorage { // If the organization component is not active then the system just looks for non active users. const facets: any = { $facet: {} }; const array = []; - const tenant = await TenantStorage.getTenant(tenantID); for (const type of params.errorTypes) { if ((type === UserInErrorType.NOT_ASSIGNED && !Utils.isTenantComponentActive(tenant, TenantComponents.ORGANIZATION)) || ((type === UserInErrorType.NO_BILLING_DATA || type === UserInErrorType.FAILED_BILLING_SYNCHRO) && !Utils.isTenantComponentActive(tenant, TenantComponents.BILLING))) { @@ -860,7 +887,7 @@ export default class UserStorage { continue; } array.push(`$${type}`); - facets.$facet[type] = UserStorage.getUserInErrorFacet(tenantID, type); + facets.$facet[type] = UserStorage.getUserInErrorFacet(tenant, type); } // Do not add facet aggregation if no facet found if (Object.keys(facets.$facet).length > 0) { @@ -874,7 +901,7 @@ export default class UserStorage { // Change ID DatabaseUtils.pushRenameDatabaseID(aggregation); // Add Created By / Last Changed By - DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation); + DatabaseUtils.pushCreatedLastChangedInAggregation(tenant.id, aggregation); // Mongodb sort, skip and limit block if (!dbParams.sort) { dbParams.sort = { status: -1, name: 1, firstName: 1 }; @@ -893,13 +920,13 @@ export default class UserStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const usersMDB = await global.database.getCollection(tenantID, 'users') + const usersMDB = await global.database.getCollection(tenant.id, 'users') .aggregate(aggregation, { allowDiskUse: false }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getUsers', uniqueTimerID, usersMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getUsers', uniqueTimerID, usersMDB); // Ok return { count: usersMDB.length, @@ -907,37 +934,37 @@ export default class UserStorage { }; } - public static async deleteUser(tenantID: string, id: string): Promise { + public static async deleteUser(tenant: Tenant, id: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteUser'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteUser'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Delete User Image - await global.database.getCollection(tenantID, 'userimages') + await global.database.getCollection(tenant.id, 'userimages') .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Delete Site Users - await global.database.getCollection(tenantID, 'siteusers') + await global.database.getCollection(tenant.id, 'siteusers') .deleteMany({ 'userID': DatabaseUtils.convertToObjectID(id) }); // Delete Tags - await global.database.getCollection(tenantID, 'tags') + await global.database.getCollection(tenant.id, 'tags') .deleteMany({ 'userID': DatabaseUtils.convertToObjectID(id) }); // Delete Connections - await global.database.getCollection(tenantID, 'connections') + await global.database.getCollection(tenant.id, 'connections') .deleteMany({ 'userId': DatabaseUtils.convertToObjectID(id) }); // Delete User - await global.database.getCollection(tenantID, 'users') + await global.database.getCollection(tenant.id, 'users') .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteUser', uniqueTimerID, { id }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteUser', uniqueTimerID, { id }); } - public static async getUserSites(tenantID: string, + public static async getUserSites(tenant: Tenant, params: { search?: string; userIDs: string[]; siteAdmin?: boolean; siteOwner?: boolean }, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getUserSites'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getUserSites'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -966,7 +993,7 @@ export default class UserStorage { }); // Get Sites DatabaseUtils.pushSiteLookupInAggregation({ - tenantID, aggregation, localField: 'siteID', foreignField: '_id', + tenantID: tenant.id, aggregation, localField: 'siteID', foreignField: '_id', asField: 'site', oneToOneCardinality: true, oneToOneCardinalityNotNull: true }); // Another match for searching on Sites @@ -983,12 +1010,12 @@ export default class UserStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const sitesCountMDB = await global.database.getCollection(tenantID, 'siteusers') + const sitesCountMDB = 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, 'getUserSites', uniqueTimerID, sitesCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getUserSites', uniqueTimerID, sitesCountMDB); return { count: (!Utils.isEmptyArray(sitesCountMDB) ? sitesCountMDB[0].count : 0), result: [] @@ -1019,12 +1046,12 @@ export default class UserStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const siteUsersMDB = await global.database.getCollection<{ userID: string; siteID: string; siteAdmin: boolean; siteOwner: boolean; site: Site }>(tenantID, 'siteusers') + const siteUsersMDB = await global.database.getCollection<{ userID: string; siteID: string; siteAdmin: boolean; siteOwner: boolean; site: Site }>(tenant.id, 'siteusers') .aggregate(aggregation, { allowDiskUse: true }).toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getUserSites', uniqueTimerID, siteUsersMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getUserSites', uniqueTimerID, siteUsersMDB); // Ok return { count: (!Utils.isEmptyArray(sitesCountMDB) ? @@ -1079,7 +1106,7 @@ export default class UserStorage { }; } - private static getUserInErrorFacet(tenantID: string, errorType: string) { + private static getUserInErrorFacet(tenant: Tenant, errorType: string) { switch (errorType) { case UserInErrorType.NOT_ACTIVE: return [ @@ -1090,7 +1117,7 @@ export default class UserStorage { return [ { $lookup: { - from: DatabaseUtils.getCollectionName(tenantID, 'siteusers'), + from: DatabaseUtils.getCollectionName(tenant.id, 'siteusers'), localField: '_id', foreignField: 'userID', as: 'sites' diff --git a/src/types/Server.ts b/src/types/Server.ts index 2bad58d922..fde5c20703 100644 --- a/src/types/Server.ts +++ b/src/types/Server.ts @@ -261,6 +261,7 @@ export enum ServerAction { UNKNOWN_USER_BADGED = 'UnknownUserBadged', TRANSACTION_STARTED = 'TransactionStarted', VERIFICATION_EMAIL = 'VerificationEmail', + VERIFICATION_EMAIL_USER_IMPORT = 'VerificationEmailUserImport', EMAIL_SERVER_ERROR = 'EmailServerError', PATCH_EVSE_STATUS_ERROR = 'PatchEVSEStatusError', PATCH_EVSE_ERROR = 'PatchEVSEError', @@ -270,6 +271,7 @@ export enum ServerAction { BILLING_USER_SYNCHRONIZATION_FAILED = 'BillingUserSynchronizationFailed', BILLING_INVOICE_SYNCHRONIZATION_FAILED = 'BillingInvoiceSynchronizationFailed', USER_ACCOUNT_VERIFICATION = 'UserAccountVerification', + USER_CREATE_PASSWORD = 'UserCreatePassword', ADMIN_ACCOUNT_VERIFICATION = 'AdminAccountVerificationNotification', CAR_CATALOG_SYNCHRONIZATION_FAILED = 'CarCatalogSynchronizationFailed', @@ -487,7 +489,14 @@ export enum ServerRoute { REST_TRANSACTIONS = 'transactions', REST_TRANSACTION = 'transactions/:id', + REST_TRANSACTIONS_EXPORT = 'transactions/action/export', + REST_TRANSACTION_CDR = 'transactions/:id/ocpi/cdr', + REST_TRANSACTION_CDR_EXPORT = 'transactions/:id/ocpi/cdr/action/export', REST_TRANSACTIONS_CONSUMPTION = 'transactions/:id/consumptions', + REST_TRANSACTION_CONSUMPTIONS_REBUILD = 'transactions/:id/consumptions/rebuild', + REST_TRANSACTION_SOFT_STOP = 'transactions/:id/stop/soft', + REST_TRANSACTIONS_REFUND = 'transactions/action/refund', + REST_TRANSACTIONS_ASSIGN_USER = 'transactions/action/assign', REST_USERS = 'users', REST_USER = 'users/:id', diff --git a/src/types/Tag.ts b/src/types/Tag.ts index 8c4ee8d789..17587d5832 100644 --- a/src/types/Tag.ts +++ b/src/types/Tag.ts @@ -15,6 +15,10 @@ export default interface Tag extends CreatedUpdatedProps, AuthorizationActions { ocpiToken?: OCPIToken; user?: User; default?: boolean + importedData?: { + autoActivateUserAtImport: boolean; + autoActivateTagAtImport: boolean; + }; } export interface ImportedTag { @@ -28,6 +32,11 @@ export interface ImportedTag { name?: string; firstName?: string; email?: string; + importedData?: { + autoActivateUserAtImport: boolean; + autoActivateTagAtImport: boolean; + }; + siteIDs?: string; } export const TagRequiredImportProperties = [ diff --git a/src/types/User.ts b/src/types/User.ts index 896f9d9035..99f14fcdec 100644 --- a/src/types/User.ts +++ b/src/types/User.ts @@ -38,6 +38,9 @@ export default interface User extends CreatedUpdatedProps, AuthorizationActions mobileLastChangedOn: Date; lastSelectedCarID?: string; authorizationID?: string; + importedData?: { + autoActivateUserAtImport: boolean; + }; } export interface UserSite { @@ -57,6 +60,10 @@ export interface ImportedUser { importedOn?: Date; status?: ImportStatus errorDescription?: string; + importedData?: { + autoActivateUserAtImport: boolean; + }; + siteIDs?: string; } export enum UserStatus { diff --git a/src/types/UserNotifications.ts b/src/types/UserNotifications.ts index 7c47cd7cf4..c20bad8107 100644 --- a/src/types/UserNotifications.ts +++ b/src/types/UserNotifications.ts @@ -1,8 +1,6 @@ import User, { UserStatus } from './User'; -import { BillingInvoice } from './Billing'; import ChargingStation from './ChargingStation'; -import Decimal from 'decimal.js'; import NotificationTask from '../notification/NotificationTask'; import { SMTPError } from 'emailjs'; @@ -180,6 +178,7 @@ export interface NewRegisteredUserNotification extends BaseNotification { export interface VerificationEmailNotification extends BaseNotification { user: User; + tenantName?: string; evseDashboardURL: string; evseDashboardVerifyEmailURL: string; } @@ -345,3 +344,10 @@ export interface AdminAccountVerificationNotification extends BaseNotification { evseDashboardURL: string; evseUserToVerifyURL: string; } + +export interface UserCreatePassword extends BaseNotification { + user: User; + tenantName: string; + evseDashboardURL: string; + evseDashboardCreatePasswordURL: string; +} diff --git a/src/utils/Constants.ts b/src/utils/Constants.ts index ca62a734eb..e3316baf31 100644 --- a/src/utils/Constants.ts +++ b/src/utils/Constants.ts @@ -251,6 +251,12 @@ export default class Constants { public static readonly OICP_SERVER = 'OICP Server'; + public static readonly MODULE_AXIOS = 'Axios'; + public static readonly MODULE_JSON_OCPP_SERVER_16 = 'OcppJ-16'; + public static readonly MODULE_SOAP_OCPP_SERVER_12 = 'OcppS-12'; + public static readonly MODULE_SOAP_OCPP_SERVER_15 = 'OcppS-15'; + public static readonly MODULE_SOAP_OCPP_SERVER_16 = 'OcppS-16'; + // OICP constants public static readonly OICP_PROGRESS_NOTIFICATION_MAX_INTERVAL = 300; // Hubject restriction: "Progress Notification can be sent only at interval of at least 300 seconds." (5 Minutes) public static readonly OICP_VIRTUAL_USER_EMAIL = 'virtual@oicp.com'; diff --git a/src/utils/Logging.ts b/src/utils/Logging.ts index 04b1a5515f..1e9b7022ac 100644 --- a/src/utils/Logging.ts +++ b/src/utils/Logging.ts @@ -397,7 +397,7 @@ export default class Logging { tenantID: tenantID, action: ServerAction.HTTP_REQUEST, message: `Axios HTTP Request >> ${request.method.toLocaleUpperCase()} '${request.url}'`, - module: MODULE_NAME, method: 'interceptor', + module: Constants.MODULE_AXIOS, method: 'interceptor', detailedMessages: { request: Utils.cloneObject(request), } @@ -423,7 +423,7 @@ export default class Logging { tenantID, source: Constants.CENTRAL_SERVER, action: ServerAction.PERFORMANCES, - module: MODULE_NAME, method: 'logAxiosResponse', + module: Constants.MODULE_AXIOS, method: 'logAxiosResponse', message: `${message}: ${error.message}`, detailedMessages: { error: error.stack } }); @@ -441,7 +441,7 @@ export default class Logging { tenantID, source: Constants.CENTRAL_SERVER, action: ServerAction.PERFORMANCES, - module: MODULE_NAME, method: 'logAxiosResponse', + module: Constants.MODULE_AXIOS, method: 'logAxiosResponse', message: `${message}: ${error.message}`, detailedMessages: { error: error.stack } }); @@ -458,7 +458,7 @@ export default class Logging { tenantID: tenantID, action: ServerAction.HTTP_RESPONSE, message, - module: MODULE_NAME, method: 'logAxiosResponse', + module: Constants.MODULE_AXIOS, method: 'logAxiosResponse', detailedMessages: { status: response.status, statusText: response.statusText, @@ -477,18 +477,17 @@ export default class Logging { durationMs: executionDurationMillis, sizeKb: sizeOfDataKB, source: Constants.AXIOS_CLIENT, - module: MODULE_NAME, method: 'logAxiosResponse', + module: Constants.MODULE_AXIOS, method: 'logAxiosResponse', action: ServerAction.HTTP_RESPONSE, }) ); } catch (error) { // FIXME: Error Message: Converting circular structure to JSON - // Temporary FIX: Utils.cloneObject() removed 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}'`, - module: MODULE_NAME, method: 'interceptor', + module: Constants.MODULE_AXIOS, method: 'logAxiosResponse', detailedMessages: { status: response.status, statusText: response.statusText @@ -503,7 +502,7 @@ export default class Logging { tenantID: tenantID, action: ServerAction.HTTP_ERROR, message: `Axios HTTP Error >> ${error.config?.method?.toLocaleUpperCase()}/${error.response?.status} '${error.config?.url}' - ${error.message}`, - module: MODULE_NAME, method: 'interceptor', + module: Constants.MODULE_AXIOS, method: 'interceptor', detailedMessages: { url: error.config?.url, status: error.response?.status, diff --git a/test/api/BillingStripeTestData.ts b/test/api/BillingStripeTestData.ts index dbf39a96be..ec7321cc4a 100644 --- a/test/api/BillingStripeTestData.ts +++ b/test/api/BillingStripeTestData.ts @@ -61,7 +61,7 @@ export default class StripeIntegrationTestData { ); assert(userData && userData.id, 'response should not be null'); // Let's get the newly created user - this.dynamicUser = await UserStorage.getUser(this.getTenantID(), userData.id); + this.dynamicUser = await UserStorage.getUser(this.getTenant(), userData.id); } public async forceBillingSettings(immediateBilling: boolean): Promise { @@ -311,7 +311,7 @@ export default class StripeIntegrationTestData { public async checkNoUsersWithTestData() : Promise { // const response = await this.adminUserService.userApi.readAll({ withTestBillingData: true }, { limit: 1, skip: 0 }); // assert(response?.data?.result.length === 0, 'There should be no users with test billing data anymore'); - const response = await UserStorage.getUsers(this.getTenantID(), { + const response = await UserStorage.getUsers(this.getTenant(), { withTestBillingData: true }, { limit: 1, @@ -362,14 +362,14 @@ export default class StripeIntegrationTestData { ); assert(userData && userData.id, 'response should not be null'); // Let's get the newly created user - const testUser = await UserStorage.getUser(this.getTenantID(), userData.id); + const testUser = await UserStorage.getUser(this.getTenant(), userData.id); expect(testUser.billingData).not.to.be.null; const corruptedBillingData: BillingUserData = { ...testUser.billingData, customerID: 'cus_corrupted_data' }; // Let's update the billing data with an inconsistent customer ID - await UserStorage.saveUserBillingData(this.getTenantID(), testUser.id, corruptedBillingData); + await UserStorage.saveUserBillingData(this.getTenant(), testUser.id, corruptedBillingData); // Let's now try to repair the user data. const billingUser: BillingUser = await this.billingImpl.forceSynchronizeUser(user); expect(corruptedBillingData.customerID).to.not.be.eq(billingUser.billingData.customerID); diff --git a/test/api/SmartChargingTest.ts b/test/api/SmartChargingTest.ts index 411e395a4a..6fa78a44ee 100644 --- a/test/api/SmartChargingTest.ts +++ b/test/api/SmartChargingTest.ts @@ -478,7 +478,7 @@ describe('Smart Charging Service', function() { 'limit': Utils.roundTo(24 * 3 * aCBufferFactor, 3) } ]); - await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant().id, chargingProfiles[0]); + await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant(), chargingProfiles[0]); TestData.validateChargingProfile(chargingProfiles[1], transaction1); expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset([ { @@ -494,7 +494,7 @@ describe('Smart Charging Service', function() { 'limit': Utils.roundTo(13 * 3 * aCBufferFactor, 3) } ]); - await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant().id, chargingProfiles[1]); + await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant(), chargingProfiles[1]); TestData.validateChargingProfile(chargingProfiles[2], transaction2); expect(chargingProfiles[2].profile.chargingSchedule.chargingSchedulePeriod).containSubset([ { @@ -531,7 +531,7 @@ describe('Smart Charging Service', function() { 'limit': Utils.roundTo(20 * aCBufferFactor, 3) } ]); - await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant().id, chargingProfiles[0]); + await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant(), chargingProfiles[0]); }); @@ -610,9 +610,9 @@ describe('Smart Charging Service', function() { expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit96); TestData.validateChargingProfile(chargingProfiles[2], transaction2); expect(chargingProfiles[2].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit32); - await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant().id, chargingProfiles[0].chargingStationID); - await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant().id, chargingProfiles[1].chargingStationID); - await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant().id, chargingProfiles[2].chargingStationID); + await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant(), chargingProfiles[0].chargingStationID); + await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant(), chargingProfiles[1].chargingStationID); + await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant(), chargingProfiles[2].chargingStationID); }); it('Test for sticky limit - 1 three phased and 2 single phased cars charging and one car on a single phased station with no consumption on two cars', async () => { @@ -787,7 +787,7 @@ describe('Smart Charging Service', function() { 'limit': Utils.roundTo(16 * aCBufferFactor, 3) } ]); - await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant().id, chargingProfiles[0]); + await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant(), chargingProfiles[0]); TestData.validateChargingProfile(chargingProfiles[1], transaction1); expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset([ { @@ -803,7 +803,7 @@ describe('Smart Charging Service', function() { 'limit': Utils.roundTo(20 * aCBufferFactor, 3) } ]); - await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant().id, chargingProfiles[1]); + await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant(), chargingProfiles[1]); }); it('Test for sticky limit with different buffer value - two cars charging with lower site area limit', async () => { @@ -871,8 +871,8 @@ describe('Smart Charging Service', function() { expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit32); TestData.validateChargingProfile(chargingProfiles[1], transaction1); expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit32); - await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant().id, chargingProfiles[0].chargingStationID); - await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant().id, chargingProfiles[1].chargingStationID); + await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant(), chargingProfiles[0].chargingStationID); + await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant(), chargingProfiles[1].chargingStationID); }); it('Test for sticky limit - two cars charging with no consumption on one car', async () => { @@ -1058,7 +1058,7 @@ describe('Smart Charging Service', function() { 'limit': Utils.roundTo(40000 / Voltage.VOLTAGE_230 / 3 * dCBufferFactor, 3) * 3 }, ]); - await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant().id, chargingProfiles[0]); + await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant(), chargingProfiles[0]); TestData.validateChargingProfile(chargingProfiles[1], transaction1); expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset([ { @@ -1074,7 +1074,7 @@ describe('Smart Charging Service', function() { 'limit': Utils.roundTo(30000 / Voltage.VOLTAGE_230 / 3 * dCBufferFactor, 3) * 3 } ]); - await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant().id, chargingProfiles[1]); + await ChargingStationStorage.saveChargingProfile(testData.tenantContext.getTenant(), chargingProfiles[1]); }); it('Test for sticky limit with different buffer value - two cars charging with lower site area limit', async () => { @@ -1181,8 +1181,8 @@ describe('Smart Charging Service', function() { 'limit': 327 }, ]); - await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant().id, chargingProfiles[0].chargingStationID); - await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant().id, chargingProfiles[1].chargingStationID); + await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant(), chargingProfiles[0].chargingStationID); + await ChargingStationStorage.deleteChargingProfiles(testData.tenantContext.getTenant(), chargingProfiles[1].chargingStationID); }); }); }); diff --git a/test/api/UserTest.ts b/test/api/UserTest.ts index 5ef4f6c965..f36df63868 100644 --- a/test/api/UserTest.ts +++ b/test/api/UserTest.ts @@ -9,6 +9,7 @@ import Factory from '../factories/Factory'; import { HTTPError } from '../../src/types/HTTPError'; import { ServerRoute } from '../../src/types/Server'; import SiteContext from './context/SiteContext'; +import { StartTransactionErrorCode } from '../../src/types/Transaction'; import { StatusCodes } from 'http-status-codes'; import Tag from '../../src/types/Tag'; import TenantContext from './context/TenantContext'; @@ -277,7 +278,7 @@ describe('User', function() { expect(response.status).to.be.eq(StatusCodes.OK); expect(response.data.tag.visualID).to.be.eq(testData.newTag.visualID); expect(response.data.car).to.be.undefined; - expect(response.data.errorCodes).to.be.empty; + expect(response.data.errorCodes).to.be.not.null; }); it('Should be able to delete the created user', async () => { diff --git a/test/api/client/TransactionApi.ts b/test/api/client/TransactionApi.ts index 429ecbcabd..bc049cfc99 100644 --- a/test/api/client/TransactionApi.ts +++ b/test/api/client/TransactionApi.ts @@ -8,15 +8,15 @@ export default class TransactionApi extends CrudApi { } public async readById(id) { - return await super.readById(id, `/v1/api/transactions/${id}?WithUser=true`); + return await super.readById(id, `${this.buildRestEndpointUrl(ServerRoute.REST_TRANSACTION, { id })}?WithUser=true`); } public async readAllActive(params, paging = TestConstants.DEFAULT_PAGING, ordering = TestConstants.DEFAULT_ORDERING) { - return await super.readAll({ ...params, Status: 'active' }, paging, ordering, `/v1/api/${ServerRoute.REST_TRANSACTIONS}`); + return await super.readAll({ ...params, Status: 'active' }, paging, ordering, this.buildRestEndpointUrl(ServerRoute.REST_TRANSACTIONS)); } public async readAllCompleted(params, paging = TestConstants.DEFAULT_PAGING, ordering = TestConstants.DEFAULT_ORDERING) { - return await super.readAll({ ...params, Status: 'completed' }, paging, ordering, `/v1/api/${ServerRoute.REST_TRANSACTIONS}`); + return await super.readAll({ ...params, Status: 'completed' }, paging, ordering, this.buildRestEndpointUrl(ServerRoute.REST_TRANSACTIONS)); } public async readAllInError(params, paging = TestConstants.DEFAULT_PAGING, ordering = TestConstants.DEFAULT_ORDERING) { @@ -24,7 +24,7 @@ export default class TransactionApi extends CrudApi { } public async readAllConsumption(params) { - return await super.read(params, `/v1/api/transactions/${params.TransactionId}/consumptions?WithUser=true`); + return await super.read(params, `${this.buildRestEndpointUrl(ServerRoute.REST_TRANSACTIONS_CONSUMPTION, { id: params.TransactionId })}?WithUser=true`); } public async readAllYears(params) { @@ -32,13 +32,13 @@ export default class TransactionApi extends CrudApi { } public async delete(id) { - return await super.delete(id, '/client/api/TransactionDelete'); + return await super.delete(id, this.buildRestEndpointUrl(ServerRoute.REST_TRANSACTION, { id })); } public async deleteMany(ids) { return await this._authenticatedApi.send({ method: 'DELETE', - url: '/client/api/TransactionsDelete', + url: this.buildRestEndpointUrl(ServerRoute.REST_TRANSACTIONS), data: { transactionsIDs: ids, } diff --git a/test/api/context/ContextBuilder.ts b/test/api/context/ContextBuilder.ts index 5c4221940f..f1cbdede09 100644 --- a/test/api/context/ContextBuilder.ts +++ b/test/api/context/ContextBuilder.ts @@ -137,7 +137,7 @@ export default class ContextBuilder { await this.superAdminCentralServerService.updateEntity( this.superAdminCentralServerService.tenantApi, buildTenant); console.log(`${buildTenant.id} (${buildTenant.name}) - Create tenant context`); - const userId = await UserStorage.saveUser(buildTenant.id, { + const userId = await UserStorage.saveUser(buildTenant, { 'id': ContextDefinition.TENANT_USER_LIST[0].id, 'issuer': true, 'name': 'Admin', @@ -148,16 +148,16 @@ export default class ContextBuilder { 'mobile': '66666666666', 'plateID': '666-FB-69' } as User); - await UserStorage.saveUserStatus(buildTenant.id, userId, ContextDefinition.TENANT_USER_LIST[0].status); - await UserStorage.saveUserRole(buildTenant.id, userId, ContextDefinition.TENANT_USER_LIST[0].role); - await UserStorage.saveUserPassword(buildTenant.id, userId, { password: await Utils.hashPasswordBcrypt(config.get('admin.password')) }); + await UserStorage.saveUserStatus(buildTenant, userId, ContextDefinition.TENANT_USER_LIST[0].status); + await UserStorage.saveUserRole(buildTenant, userId, ContextDefinition.TENANT_USER_LIST[0].role); + await UserStorage.saveUserPassword(buildTenant, userId, { password: await Utils.hashPasswordBcrypt(config.get('admin.password')) }); if (ContextDefinition.TENANT_USER_LIST[0].tags) { for (const tag of ContextDefinition.TENANT_USER_LIST[0].tags) { tag.userID = ContextDefinition.TENANT_USER_LIST[0].id; await TagStorage.saveTag(buildTenant.id, tag); } } - const defaultAdminUser = await UserStorage.getUser(buildTenant.id, ContextDefinition.TENANT_USER_LIST[0].id); + const defaultAdminUser = await UserStorage.getUser(buildTenant, ContextDefinition.TENANT_USER_LIST[0].id); // Create Central Server Service const localCentralServiceService: CentralServerService = new CentralServerService(buildTenant.subdomain); // Create Tenant component settings @@ -235,17 +235,17 @@ export default class ContextBuilder { const newPasswordHashed = await Utils.hashPasswordBcrypt(config.get('admin.password')); createUser.id = userDef.id; const user: User = createUser; - await UserStorage.saveUser(buildTenant.id, user); - await UserStorage.saveUserStatus(buildTenant.id, user.id, userDef.status); - await UserStorage.saveUserRole(buildTenant.id, user.id, userDef.role); - await UserStorage.saveUserPassword(buildTenant.id, user.id, { password: newPasswordHashed }); + await UserStorage.saveUser(buildTenant, user); + await UserStorage.saveUserStatus(buildTenant, user.id, userDef.status); + await UserStorage.saveUserRole(buildTenant, user.id, userDef.role); + await UserStorage.saveUserPassword(buildTenant, user.id, { password: newPasswordHashed }); if (userDef.tags) { for (const tag of userDef.tags) { tag.userID = user.id; await TagStorage.saveTag(buildTenant.id, tag); } } - const userModel = await UserStorage.getUser(buildTenant.id, user.id); + const userModel = await UserStorage.getUser(buildTenant, user.id); if (userDef.assignedToSite) { userListToAssign.push(userModel); } @@ -303,8 +303,8 @@ export default class ContextBuilder { siteAreaTemplate.numberOfPhases = siteAreaDef.numberOfPhases; siteAreaTemplate.voltage = siteAreaDef.voltage; console.log(`${buildTenant.id} (${buildTenant.name}) - Site Area '${siteAreaTemplate.name}'`); - const sireAreaID = await SiteAreaStorage.saveSiteArea(buildTenant.id, siteAreaTemplate); - const siteAreaModel = await SiteAreaStorage.getSiteArea(buildTenant.id, sireAreaID); + const sireAreaID = await SiteAreaStorage.saveSiteArea(buildTenant, siteAreaTemplate); + const siteAreaModel = await SiteAreaStorage.getSiteArea(buildTenant, sireAreaID); const siteAreaContext = siteContext.addSiteArea(siteAreaModel); const relevantCS = ContextDefinition.TENANT_CHARGING_STATION_LIST.filter( (chargingStation) => chargingStation.siteAreaNames && chargingStation.siteAreaNames.includes(siteAreaModel.name) === true); diff --git a/test/api/context/StatisticsContext.ts b/test/api/context/StatisticsContext.ts index 50a34328d9..bcd14e0914 100644 --- a/test/api/context/StatisticsContext.ts +++ b/test/api/context/StatisticsContext.ts @@ -93,14 +93,14 @@ export default class StatisticsContext { * @param transactionId The id of the transaction */ public async generateStaticRefundData(transactionId: number) { - const transaction = await TransactionStorage.getTransaction(this.tenantContext.getTenant().id, transactionId); + const transaction = await TransactionStorage.getTransaction(this.tenantContext.getTenant(), transactionId); transaction.refundData = { refundId: faker.random.alphaNumeric(32), refundedAt: new Date(), reportId: faker.random.alphaNumeric(20), status: RefundStatus.APPROVED, }; - await TransactionStorage.saveTransaction(this.tenantContext.getTenant().id, transaction); + await TransactionStorage.saveTransaction(this.tenantContext.getTenant(), transaction); console.log(`${this.tenantContext.getTenant().id} (${this.tenantContext.getTenant().name}) - Updated transaction '${transaction.id}' with refund data`); }