Assert Tenant's component deps!!!
MODIFY
src/server/rest/v1/service/TenantService.ts
src/server/rest/v1/service/TenantService.ts
@@ -231,6 +231,8 @@ export default class TenantService {
module: MODULE_NAME, method: 'handleCreateTenant'
});
}
+ // Check Tenant
+ UtilsService.checkIfTenantValid(filteredRequest, req);
// Get the Tenant with ID (subdomain)
const foundTenant = await TenantStorage.getTenantBySubdomain(filteredRequest.subdomain);
if (foundTenant) {
@@ -323,6 +325,8 @@ export default class TenantService {
value: filteredRequest.id
});
}
+ // Check Tenant
+ UtilsService.checkIfTenantValid(filteredRequest, req);
// Get
const tenant = await TenantStorage.getTenant(filteredRequest.id);
UtilsService.assertObjectExists(action, tenant, `Tenant ID '${filteredRequest.id}' does not exist`,

MODIFY
src/server/rest/v1/service/UtilsService.ts
src/server/rest/v1/service/UtilsService.ts
@@ -1328,6 +1328,89 @@ export default class UtilsService {
}
}
+ public static checkIfTenantValid(tenant: Partial<Tenant>, req: Request): void {
+ if (req.method !== 'POST' && !tenant.id) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Tenant ID is mandatory',
+ module: MODULE_NAME, method: 'checkIfTenantValid',
+ user: req.user.id
+ });
+ }
+ if (!tenant.name) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Tenant Name is mandatory',
+ module: MODULE_NAME, method: 'checkIfTenantValid',
+ user: req.user.id
+ });
+ }
+ if (!tenant.components) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Tenant Components is mandatory',
+ module: MODULE_NAME, method: 'checkIfTenantValid',
+ user: req.user.id
+ });
+ }
+ if (!tenant.components) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Tenant Components is mandatory',
+ module: MODULE_NAME, method: 'checkIfTenantValid',
+ user: req.user.id
+ });
+ }
+ if (tenant.components.oicp?.active && tenant.components.ocpi?.active) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'OICP and OCPI Components cannot be both active',
+ module: MODULE_NAME, method: 'checkIfTenantValid',
+ user: req.user.id
+ });
+ }
+ if (tenant.components.refund?.active && !tenant.components.pricing?.active) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Refund cannot be active without the Pricing component',
+ module: MODULE_NAME, method: 'checkIfTenantValid',
+ user: req.user.id
+ });
+ }
+ if (tenant.components.billing?.active && !tenant.components.pricing?.active) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Billing cannot be active without the Pricing component',
+ module: MODULE_NAME, method: 'checkIfTenantValid',
+ user: req.user.id
+ });
+ }
+ if (tenant.components.smartCharging?.active && !tenant.components.organization?.active) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Smart Charging cannot be active without the Organization component',
+ module: MODULE_NAME, method: 'checkIfTenantValid',
+ user: req.user.id
+ });
+ }
+ if (tenant.components.asset?.active && !tenant.components.organization?.active) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Asset cannot be active without the Organization component',
+ module: MODULE_NAME, method: 'checkIfTenantValid',
+ user: req.user.id
+ });
+ }
+ if (tenant.components.carConnector?.active && !tenant.components.car?.active) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Car Connector cannot be active without the Car component',
+ module: MODULE_NAME, method: 'checkIfTenantValid',
+ user: req.user.id
+ });
+ }
+ }
+
public static checkIfSiteAreaValid(siteArea: Partial<SiteArea>, req: Request): void {
if (req.method !== 'POST' && !siteArea.id) {
throw new AppError({

MODIFY
test/api/TenantTest.ts
test/api/TenantTest.ts
+import Tenant, { TenantComponents } from '../../src/types/Tenant';
import chai, { expect } from 'chai';
import CentralServerService from '../api/client/CentralServerService';
import Factory from '../factories/Factory';
import { HTTPError } from '../../src/types/HTTPError';
import { StatusCodes } from 'http-status-codes';
-import Tenant from '../../src/types/Tenant';
import TestUtils from './TestUtils';
import chaiSubset from 'chai-subset';
import faker from 'faker';
@@ -85,6 +85,10 @@ describe('Tenant', function() {
'active': false,
'type': null
},
+ 'carConnector': {
+ 'active': false,
+ 'type': null
+ },
'statistics': {
'active': false,
'type': null

MODIFY
test/factories/TenantFactory.ts
test/factories/TenantFactory.ts
@@ -5,6 +5,7 @@ export default Factory.define('tenant')
.attr('name', () => faker.company.companyName())
.attr('email', () => faker.internet.email().toLowerCase())
.attr('subdomain', () => faker.random.alphaNumeric(10).toLowerCase())
+ .attr('components', {})
.attr('address', {
address1: faker.address.streetAddress(false),
address2: faker.address.streetAddress(true),

Assert Tenant's component deps!!!