pricing model - 1st test
MODIFY
.vscode/launch.json
.vscode/launch.json
"name": "Debug Mocha Tests",
"type": "pwa-node",
"request": "attach",
- "port": 9229,
+ "port": 9230,
"timeout": 30000,
"skipFiles": [
"<node_internals>/**"

MODIFY
.vscode/settings.json
.vscode/settings.json
"mochaExplorer.parallel": false,
"mochaExplorer.exit": true,
"mochaExplorer.debuggerConfig": "Debug Mocha Tests",
+ "mochaExplorer.debuggerPort": 9230,
"search.usePCRE2": true
}

MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -210,6 +210,7 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
{ resource: Entity.LOGGINGS, action: Action.LIST },
{ resource: Entity.LOGGING, action: Action.READ },
{ resource: Entity.PRICING, action: [Action.READ, Action.UPDATE] },
+ { resource: Entity.PRICING_MODEL, action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE] },
{ resource: Entity.BILLING, action: [Action.CHECK_CONNECTION, Action.CLEAR_BILLING_TEST_DATA] },
{ resource: Entity.TAXES, action: [Action.LIST] },
// ---------------------------------------------------------------------------------------------------

MODIFY
src/server/rest/v1/service/PricingService.ts
src/server/rest/v1/service/PricingService.ts
@@ -101,7 +101,7 @@ export default class PricingService {
// Filter
const filteredRequest = PricingSecurity.filterPricingModelCreateRequest(req.body);
// Check
- UtilsService.checkIfPricingValid(filteredRequest, req);
+ UtilsService.checkIfPricingModelValid(filteredRequest, req);
// Get dynamic auth
const authorizationFilter = await AuthorizationService.checkAndGetPricingModelAuthorizations(
req.tenant, req.user, {}, Action.CREATE, filteredRequest as PricingModel);
@@ -142,7 +142,7 @@ export default class PricingService {
// Filter
const filteredRequest = PricingSecurity.filterPricingModelUpdateRequest(req.body);
// Check Mandatory fields
- UtilsService.checkIfPricingValid(filteredRequest, req);
+ UtilsService.checkIfPricingModelValid(filteredRequest, req);
// Check and Get Pricing
const pricingModel = await UtilsService.checkAndGetPricingModelAuthorization(
req.tenant, req.user, filteredRequest.id, Action.UPDATE, action, filteredRequest as PricingModel);

MODIFY
src/server/rest/v1/service/UtilsService.ts
src/server/rest/v1/service/UtilsService.ts
@@ -1330,13 +1330,13 @@ export default class UtilsService {
}
}
- public static checkIfPricingValid(pricing: Partial<PricingModel>, req: Request): void {
+ public static checkIfPricingModelValid(pricing: Partial<PricingModel>, req: Request): void {
if (req.method !== 'POST' && !pricing.id) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
errorCode: HTTPError.GENERAL_ERROR,
message: 'Pricing ID is mandatory',
- module: MODULE_NAME, method: 'checkIfPricingValid',
+ module: MODULE_NAME, method: 'checkIfPricingModelValid',
user: req.user.id
});
}
@@ -1345,7 +1345,7 @@ export default class UtilsService {
source: Constants.CENTRAL_SERVER,
errorCode: HTTPError.GENERAL_ERROR,
message: 'Pricing Definition is mandatory',
- module: MODULE_NAME, method: 'checkIfPricingValid',
+ module: MODULE_NAME, method: 'checkIfPricingModelValid',
user: req.user.id
});
}

MODIFY
src/server/rest/v1/service/security/PricingSecurity.ts
src/server/rest/v1/service/security/PricingSecurity.ts
@@ -39,8 +39,9 @@ export default class PricingSecurity {
public static _filterPricingModelRequest(request: any): Partial<PricingModel> {
const filteredRequest = {
- name: sanitize(request.name),
- address: UtilsSecurity.filterAddressRequest(request.address)
+ contextID: sanitize(request.contextID),
+ // TODO - not yet sanitized properly
+ pricingDefinitions: sanitize(request.pricingDefinitions)
} as Partial<PricingModel>;
return filteredRequest;
}

MODIFY
src/storage/mongodb/PricingStorage.ts
src/storage/mongodb/PricingStorage.ts
@@ -5,6 +5,7 @@ import { DataResult } from '../../types/DataResult';
import DatabaseUtils from './DatabaseUtils';
import DbParams from '../../types/database/DbParams';
import Logging from '../../utils/Logging';
+import { ObjectId } from 'mongodb';
import PricingModel from '../../types/Pricing';
import Tenant from '../../types/Tenant';
import Utils from '../../utils/Utils';
@@ -18,21 +19,21 @@ export default class PricingStorage {
const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'savePricingModel');
// Check Tenant
DatabaseUtils.checkTenantObject(tenant);
- const pricingMDB = {
- _id: pricing.id,
+ const pricingModelMDB = {
+ _id: pricing.id ? DatabaseUtils.convertToObjectID(pricing.id) : new ObjectId(),
contextID: DatabaseUtils.convertToObjectID(pricing.contextID),
- pricingDefinitions: pricing.pricingDefinitions,
+ pricingDefinitions: pricing.pricingDefinitions, // TODO - check here some data consistency
};
// Check Created/Last Changed By
- DatabaseUtils.addLastChangedCreatedProps(pricingMDB, pricing);
+ DatabaseUtils.addLastChangedCreatedProps(pricingModelMDB, pricing);
// Save
await global.database.getCollection<any>(tenant.id, 'pricingmodels').findOneAndUpdate(
- { '_id': pricing.id },
- { $set: pricingMDB },
+ { '_id': pricingModelMDB._id },
+ { $set: pricingModelMDB },
{ upsert: true, returnDocument: 'after' });
// Debug
- await Logging.traceEnd(tenant.id, MODULE_NAME, 'savePricingModel', uniqueTimerID, pricingMDB);
- return pricingMDB._id.toString();
+ await Logging.traceEnd(tenant.id, MODULE_NAME, 'savePricingModel', uniqueTimerID, pricingModelMDB);
+ return pricingModelMDB._id.toString();
}
public static async deletePricingModel(tenant: Tenant, pricingID: string): Promise<void> {

MODIFY
test/api/BillingStripeIntegrationTest.ts
test/api/BillingStripeIntegrationTest.ts
@@ -23,6 +23,18 @@ describe('Billing Stripe Service', function() {
await testData.initialize();
});
+ describe('Initialization Pricing Model', () => {
+ before(async () => {
+ });
+
+ after(async () => {
+ });
+
+ it('check CRUD operations on a Pricing Model', async () => {
+ await testData.checkPricingModel();
+ });
+ });
+
describe('immediate billing OFF', () => {
before(async () => {
const immediateBilling = false;

MODIFY
test/api/BillingStripeTestData.ts
test/api/BillingStripeTestData.ts
@@ -9,6 +9,7 @@ import ContextDefinition from './context/ContextDefinition';
import ContextProvider from './context/ContextProvider';
import Cypher from '../../src/utils/Cypher';
import Factory from '../factories/Factory';
+import PricingModel from '../../src/types/Pricing';
import Stripe from 'stripe';
import StripeBillingIntegration from '../../src/integration/billing/stripe/StripeBillingIntegration';
import Tenant from '../../src/types/Tenant';
@@ -26,7 +27,6 @@ chai.use(chaiSubset);
chai.use(responseHelper);
export default class StripeIntegrationTestData {
-
// Tenant: utbilling
public tenantContext: TenantContext;
// User Service for action requiring admin permissions (e.g.: set/reset stripe settings)
@@ -430,4 +430,14 @@ export default class StripeIntegrationTestData {
assert(response?.data?.error, 'error should not be null');
}
}
+
+ public async checkPricingModel(): Promise<void> {
+ const pricingModel: Partial<PricingModel> = {
+ contextID: null, // a pricing model for the tenant
+ pricingDefinitions: []
+ };
+ const response = await this.adminUserService.pricingApi.createPricingModel(pricingModel);
+ assert(response?.data?.status === 'Success', 'The operation should succeed');
+ }
+
}

MODIFY
test/api/client/CentralServerService.ts
test/api/client/CentralServerService.ts
@@ -14,6 +14,7 @@ import { HTTPError } from '../../../src/types/HTTPError';
import LogsApi from './LogsApi';
import MailApi from './MailApi';
import OCPIEndpointApi from './OCPIEndpointApi';
+import PricingApi from './PricingApi';
import RegistrationTokenApi from './RegistrationTokenApi';
import SettingApi from './SettingApi';
import SiteApi from './SiteApi';
@@ -56,6 +57,7 @@ export default class CentralServerService {
public logsApi: LogsApi;
public statisticsApi: StatisticsApi;
public billingApi: BillingApi;
+ public pricingApi: PricingApi;
public smartChargingApi: SmartChargingApi;
public _baseApi: BaseApi;
private _baseURL: string;
@@ -105,6 +107,7 @@ export default class CentralServerService {
this.statisticsApi = new StatisticsApi(this.authenticatedApi);
this.registrationApi = new RegistrationTokenApi(this.authenticatedApi);
this.billingApi = new BillingApi(this.authenticatedApi);
+ this.pricingApi = new PricingApi(this.authenticatedApi);
this.assetApi = new AssetApi(this.authenticatedApi);
this.carApi = new CarApi(this.authenticatedApi);
this.smartChargingApi = new SmartChargingApi(this.authenticatedApi);

MODIFY
test/api/client/PricingApi.ts
test/api/client/PricingApi.ts
@@ -22,7 +22,7 @@ export default class PricingApi extends CrudApi {
return super.update(data, this.buildRestEndpointUrl(ServerRoute.REST_PRICING_MODEL, { id: data.id }));
}
- public async createPricingModel(data: PricingModel) {
+ public async createPricingModel(data: Partial<PricingModel>) {
return super.create(data, this.buildRestEndpointUrl(ServerRoute.REST_PRICING_MODELS));
}

pricing model - 1st test