Migrated EMSP Tariffs endpoint
Clean all EMSP old endpoints
DELETE
src/server/ocpi/AbstractOCPIService.ts
-import { NextFunction, Request, Response } from 'express';
-
-import AbstractEndpoint from './ocpi-services-impl/AbstractEndpoint';
-import AppError from '../../exception/AppError';
-import BackendError from '../../exception/BackendError';
-import Constants from '../../utils/Constants';
-import { HTTPError } from '../../types/HTTPError';
-import Logging from '../../utils/Logging';
-import OCPIEndpointStorage from '../../storage/mongodb/OCPIEndpointStorage';
-import OCPIServiceConfiguration from '../../types/configuration/OCPIServiceConfiguration';
-import { OCPIStatusCode } from '../../types/ocpi/OCPIStatusCode';
-import OCPIUtils from './OCPIUtils';
-import { ServerAction } from '../../types/Server';
-import { StatusCodes } from 'http-status-codes';
-import { TenantComponents } from '../../types/Tenant';
-import TenantStorage from '../../storage/mongodb/TenantStorage';
-import Utils from '../../utils/Utils';
-
-const MODULE_NAME = 'AbstractOCPIService';
-
-export interface TenantIdHoldingRequest extends Request {
- tenantID: string;
-}
-
-export default abstract class AbstractOCPIService {
- public static readonly VERSIONS_PATH = '/versions';
-
- private endpoints: Map<string, AbstractEndpoint> = new Map<string, AbstractEndpoint>();
-
- protected constructor(
- private readonly ocpiRestConfig: OCPIServiceConfiguration,
- private readonly role: string,
- private readonly version: string) {
- }
-
- public registerEndpoint(endpoint: AbstractEndpoint): void {
- this.endpoints.set(endpoint.getIdentifier(), endpoint);
- }
-
- public getRegisteredEndpoints(): Map<string, AbstractEndpoint> {
- return this.endpoints;
- }
-
- public getServiceUrl(req: Request): string {
- const baseUrl = this.getBaseUrl(req);
- const path = this.getPath();
- // Return Service url
- return `${baseUrl}${path}`;
- }
-
- public getBaseUrl(req: Request): string {
- // Get host from the req
- const host = req.get('host');
- // Return Service url
- return `https://${host}`;
- }
-
- public getPath(): string {
- return `${this.role}/${this.version}/`;
- }
-
- public getVersion(): string {
- return this.version;
- }
-
- public async restService(req: TenantIdHoldingRequest, res: Response, next: NextFunction): Promise<void> {
- // Parse the action
- const regexResult = /^\/\w*/g.exec(req.url);
- if (!regexResult) {
- throw new BackendError({
- module: MODULE_NAME, method: 'restService',
- message: 'Regex did not match.'
- });
- }
- const action = regexResult[0].substring(1) as ServerAction;
- // Set default tenant in case of exception
- req.user = { tenantID: Constants.DEFAULT_TENANT_ID };
- // Check action
- switch (action) {
- // If empty - return available endpoints
- case '' as ServerAction:
- this.getSupportedEndpoints(req, res, next);
- break;
- default:
- await this.processEndpointAction(action, req, res, next);
- break;
- }
- next();
- }
-
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- public getSupportedEndpoints(req: TenantIdHoldingRequest, res: Response, next: NextFunction): void {
- const fullUrl = this.getServiceUrl(req);
- const registeredEndpointsArray = Array.from(this.getRegisteredEndpoints().values());
- // Build payload
- const supportedEndpoints = registeredEndpointsArray.map((endpoint) => {
- const identifier = endpoint.getIdentifier();
- return { 'identifier': `${identifier}`, 'url': `${fullUrl}${identifier}/` };
- });
- // Return payload
- res.json(OCPIUtils.success({ 'version': this.getVersion(), 'endpoints': supportedEndpoints }));
- }
-
- public async processEndpointAction(ocpiAction: string, req: TenantIdHoldingRequest, res: Response, next: NextFunction): Promise<void> {
- try {
- const registeredEndpoints = this.getRegisteredEndpoints();
- // Get token from header
- if (!req.headers || !req.headers.authorization) {
- throw new AppError({
- module: MODULE_NAME, method: 'processEndpointAction',
- action: ServerAction.OCPI_ENDPOINT,
- errorCode: StatusCodes.UNAUTHORIZED,
- message: 'Missing authorization token',
- ocpiError: OCPIStatusCode.CODE_2001_INVALID_PARAMETER_ERROR
- });
- }
- // Get token
- let decodedToken: { tenant: string; tid: string };
- const token = req.headers.authorization.split(' ')[1];
- try {
- decodedToken = JSON.parse(OCPIUtils.atob(token));
- } catch (error) {
- throw new AppError({
- module: MODULE_NAME, method: 'processEndpointAction',
- action: ServerAction.OCPI_ENDPOINT,
- errorCode: StatusCodes.UNAUTHORIZED,
- message: 'Invalid authorization token',
- ocpiError: OCPIStatusCode.CODE_3000_GENERIC_SERVER_ERROR,
- detailedMessages: { error: error.stack }
- });
- }
- // Get tenant from the called URL - TODO: review this handle tenant and tid in decoded token
- const tenantSubdomain = (decodedToken.tenant ? decodedToken.tenant : decodedToken.tid);
- // Get tenant from database
- const tenant = await TenantStorage.getTenantBySubdomain(tenantSubdomain);
- // Check if tenant is found
- if (!tenant) {
- throw new AppError({
- module: MODULE_NAME, method: 'processEndpointAction',
- action: ServerAction.OCPI_ENDPOINT,
- errorCode: StatusCodes.UNAUTHORIZED,
- message: `The Tenant '${tenantSubdomain}' does not exist`,
- ocpiError: OCPIStatusCode.CODE_3000_GENERIC_SERVER_ERROR
- });
- }
- if (!Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) {
- throw new AppError({
- module: MODULE_NAME, method: 'processEndpointAction',
- action: ServerAction.OCPI_ENDPOINT,
- errorCode: StatusCodes.UNAUTHORIZED,
- message: `The Tenant '${tenantSubdomain}' does not support OCPI`,
- ocpiError: OCPIStatusCode.CODE_3000_GENERIC_SERVER_ERROR
- });
- }
- const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpointByLocalToken(tenant, token);
- // Check if endpoint is found
- if (!ocpiEndpoint) {
- throw new AppError({
- module: MODULE_NAME, method: 'processEndpointAction',
- action: ServerAction.OCPI_ENDPOINT,
- errorCode: StatusCodes.UNAUTHORIZED,
- message: 'Invalid token',
- ocpiError: OCPIStatusCode.CODE_3000_GENERIC_SERVER_ERROR
- });
- }
- // Pass tenant id to req
- req.user.tenantID = tenant.id;
- // Handle request action (endpoint)
- const endpoint = registeredEndpoints.get(ocpiAction);
- if (endpoint) {
- await Logging.logDebug({
- tenantID: tenant.id,
- module: MODULE_NAME, method: ocpiAction,
- message: `>> OCPI Request ${req.method} ${req.originalUrl}`,
- action: ServerAction.OCPI_ENDPOINT,
- detailedMessages: { params: req.body }
- });
- const response = await endpoint.process(req, res, next, tenant, ocpiEndpoint);
- if (response) {
- await Logging.logDebug({
- tenantID: tenant.id,
- module: MODULE_NAME, method: ocpiAction,
- message: `<< OCPI Response ${req.method} ${req.originalUrl}`,
- action: ServerAction.OCPI_ENDPOINT,
- detailedMessages: { response }
- });
- res.json(response);
- } else {
- await Logging.logWarning({
- tenantID: tenant.id,
- module: MODULE_NAME, method: ocpiAction,
- message: `<< OCPI Endpoint ${req.method} ${req.originalUrl} not implemented`,
- action: ServerAction.OCPI_ENDPOINT
- });
- res.sendStatus(StatusCodes.NOT_IMPLEMENTED);
- }
- } else {
- throw new AppError({
- module: MODULE_NAME, method: 'processEndpointAction',
- action: ServerAction.OCPI_ENDPOINT,
- errorCode: HTTPError.NOT_IMPLEMENTED_ERROR,
- message: `Endpoint '${ocpiAction}' not implemented`,
- ocpiError: OCPIStatusCode.CODE_3000_GENERIC_SERVER_ERROR
- });
- }
- } catch (error) {
- await Logging.logError({
- tenantID: req.tenant?.id ?? Constants.DEFAULT_TENANT_ID,
- module: MODULE_NAME, method: ocpiAction,
- message: `<< OCPI Response Error ${req.method} ${req.originalUrl}`,
- action: error.params?.action ?? ServerAction.OCPI_ENDPOINT,
- detailedMessages: { error: error.stack }
- });
- await Logging.logActionExceptionMessage(req.tenant?.id ?? Constants.DEFAULT_TENANT_ID, error.params?.action ?? ServerAction.OCPI_ENDPOINT, error);
- res.status(error.params?.errorCode ?? HTTPError.GENERAL_ERROR).json(OCPIUtils.toErrorResponse(error));
- }
- }
-}

MODIFY
src/server/ocpi/OCPIServer.ts
src/server/ocpi/OCPIServer.ts
@@ -9,13 +9,11 @@ import { HTTPError } from '../../types/HTTPError';
import Logging from '../../utils/Logging';
import OCPIEndpointStorage from '../../storage/mongodb/OCPIEndpointStorage';
import OCPIServiceConfiguration from '../../types/configuration/OCPIServiceConfiguration';
-import OCPIServices from './OCPIServices';
import { OCPIStatusCode } from '../../types/ocpi/OCPIStatusCode';
import OCPIUtils from './OCPIUtils';
import { ServerUtils } from '../ServerUtils';
import { StatusCodes } from 'http-status-codes';
import { TenantComponents } from '../../types/Tenant';
-import { TenantIdHoldingRequest } from './AbstractOCPIService';
import TenantStorage from '../../storage/mongodb/TenantStorage';
import Utils from '../../utils/Utils';
@@ -37,18 +35,6 @@ export default class OCPIServer {
this.expressApplication.use(Logging.traceExpressRequest.bind(this));
// Routers
this.expressApplication.use('/ocpi', new GlobalRouter().buildRoutes());
- // const ocpiServices = new OCPIServices(this.ocpiRestConfig);
- // // Register all services in express
- // for (const ocpiService of ocpiServices.getOCPIServiceImplementations()) {
- // // eslint-disable-next-line @typescript-eslint/no-misused-promises
- // this.expressApplication.use(ocpiService.getPath(), async (req: TenantIdHoldingRequest, res: Response, next: NextFunction) => {
- // try {
- // await ocpiService.restService(req, res, next);
- // } catch (error) {
- // next(error);
- // }
- // });
- // }
// Post init
ExpressUtils.postInitApplication(this.expressApplication);
}

DELETE
src/server/ocpi/OCPIServices.ts
-import AbstractOCPIService from './AbstractOCPIService';
-import EMSPCPOService200 from './ocpi-services-impl/ocpi-2.0/OCPIService';
-import EMSPService211 from './ocpi-services-impl/ocpi-2.1.1/EMSPService';
-import OCPIServiceConfiguration from '../../types/configuration/OCPIServiceConfiguration';
-
-export default class OCPIServices {
-
- private cpoServices: AbstractOCPIService[] = [];
- private emspServices: AbstractOCPIService[] = [];
-
- public constructor(ocpiRestConfig: OCPIServiceConfiguration) {
- // version 2.1.1
- this.emspServices.push(new EMSPService211(ocpiRestConfig));
- }
-
- public getOCPIServiceImplementations(): AbstractOCPIService[] {
- return this.cpoServices.concat(this.emspServices);
- }
-}

DELETE
src/server/ocpi/ocpi-services-impl/AbstractEndpoint.ts
-import { NextFunction, Request, Response } from 'express';
-
-import AbstractOCPIService from '../AbstractOCPIService';
-import OCPIEndpoint from '../../../types/ocpi/OCPIEndpoint';
-import { OCPIResponse } from '../../../types/ocpi/OCPIResponse';
-import Tenant from '../../../types/Tenant';
-
-export default abstract class AbstractEndpoint {
- // Create OCPI Service
- public constructor(
- protected readonly ocpiService: AbstractOCPIService,
- protected readonly identifier: string = 'default') {
- }
-
- // Get Endpoint Identifier
- public getIdentifier(): string {
- return this.identifier;
- }
-
- // Get Endpoint version
- public getVersion(): string {
- return this.ocpiService.getVersion();
- }
-
- // Return based URL of OCPI Service
- public getBaseUrl(req: Request): string {
- return this.ocpiService.getBaseUrl(req);
- }
-
- // Return based URL of OCPI Service
- public getServiceUrl(req: Request): string {
- return this.ocpiService.getServiceUrl(req);
- }
-
- // Abstract - Process endpoint
- abstract process(req: Request, res: Response, next: NextFunction, tenant: Tenant, ocpiEndpoint: OCPIEndpoint): Promise<OCPIResponse>;
-}
-

DELETE
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/EMSPService.ts
-import AbstractOCPIService from '../../AbstractOCPIService';
-import EMSPTariffsEndpoint from './emsp/EMSPTariffsEndpoint';
-import OCPIServiceConfiguration from '../../../../types/configuration/OCPIServiceConfiguration';
-
-export default class EMSPService extends AbstractOCPIService {
- public static readonly VERSION = '2.1.1';
- public static readonly PATH = '/ocpi/emsp';
-
- public constructor(ocpiRestConfig: OCPIServiceConfiguration) {
- super(ocpiRestConfig, EMSPService.PATH, EMSPService.VERSION);
- this.registerEndpoint(new EMSPTariffsEndpoint(this));
- }
-}

MODIFY
src/server/ocpi/router/common/V2.1.1/CPOEMSPCredentialsRouterV211.ts
src/server/ocpi/router/common/V2.1.1/CPOEMSPCredentialsRouterV211.ts
-import { OCPIServerRoute, ServerAction } from '../../../../../types/Server';
/* eslint-disable @typescript-eslint/no-misused-promises */
+import { OCPIServerRoute, ServerAction } from '../../../../../types/Server';
import express, { NextFunction, Request, Response } from 'express';
import CPOEMSPCredentialsService from '../../../service/common/v2.1.1/CPOEMSPCredentialsService';

MODIFY
src/server/ocpi/router/emsp/EMSPRouter.ts
src/server/ocpi/router/emsp/EMSPRouter.ts
@@ -7,6 +7,7 @@ import EMSPCdrsRouterV211 from './V2.1.1/EMSPCdrsRouterV211';
import EMSPCommandsRouterV211 from './V2.1.1/EMSPCommandsRouterV211';
import EMSPLocationsRouterV211 from './V2.1.1/EMSPLocationsRouterV211';
import EMSPSessionsRouterV211 from './V2.1.1/EMSPSessionsRouterV211';
+import EMSPTariffsRouterV211 from './V2.1.1/EMSPTariffsRouterV211';
import EMSPTokensRouterV211 from './V2.1.1/EMSPTokensRouterV211';
import EMSPVersionsService from '../../service/emsp/EMSPVersionsService';
import RouterUtils from '../../../../utils/RouterUtils';
@@ -37,6 +38,7 @@ export default class EMSPRouter {
new EMSPSessionsRouterV211().buildRoutes(),
new EMSPCdrsRouterV211().buildRoutes(),
new EMSPCommandsRouterV211().buildRoutes(),
+ new EMSPTariffsRouterV211().buildRoutes(),
new CPOEMSPCredentialsRouterV211().buildRoutes(),
]);
}

ADD
src/server/ocpi/router/emsp/V2.1.1/EMSPTariffsRouterV211.ts
+import { OCPIServerRoute, ServerAction } from '../../../../../types/Server';
+/* eslint-disable @typescript-eslint/no-misused-promises */
+import express, { NextFunction, Request, Response } from 'express';
+
+import EMSPTariffsService from '../../../service/emsp/v2.1.1/EMSPTariffsService';
+import RouterUtils from '../../../../../utils/RouterUtils';
+
+export default class EMSPTariffsRouterV211 {
+ private router: express.Router;
+
+ public constructor() {
+ this.router = express.Router();
+ }
+
+ public buildRoutes(): express.Router {
+ this.buildRouteGetTariff();
+ return this.router;
+ }
+
+ protected buildRouteGetTariff(): void {
+ this.router.get(`/${OCPIServerRoute.OCPI_TARIFFS}*`, async (req: Request, res: Response, next: NextFunction) => {
+ await RouterUtils.handleServerAction(EMSPTariffsService.handleGetTariff.bind(this), ServerAction.OCPI_EMSP_GET_TARIFF, req, res, next);
+ });
+ }
+}

MODIFY
src/server/ocpi/service/common/v2.1.1/CPOEMSPCredentialsService.ts
src/server/ocpi/service/common/v2.1.1/CPOEMSPCredentialsService.ts
import { NextFunction, Request, Response } from 'express';
-import AbstractOCPIService from '../../../AbstractOCPIService';
import AppError from '../../../../../exception/AppError';
import AxiosFactory from '../../../../../utils/AxiosFactory';
import BackendError from '../../../../../exception/BackendError';
@@ -180,7 +179,7 @@ export default class CPOEMSPCredentialsService {
// Save ocpi endpoint
await OCPIEndpointStorage.saveOcpiEndpoint(tenant, ocpiEndpoint);
// Get base url
- const versionUrl = OCPIUtilsService.getServiceUrl(req, ocpiEndpoint.role.toLocaleLowerCase()) + AbstractOCPIService.VERSIONS_PATH;
+ const versionUrl = OCPIUtilsService.getServiceUrl(req, ocpiEndpoint.role.toLocaleLowerCase()) + '/versions';
// Build credential object
const respCredential = await OCPIUtils.buildOCPICredentialObject(tenant, ocpiEndpoint.localToken, ocpiEndpoint.role, versionUrl);
// Log available OCPI Versions

RENAME
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPTariffsEndpoint.ts
src/server/ocpi/service/emsp/v2.1.1/EMSPTariffsService.ts
import { NextFunction, Request, Response } from 'express';
-import AbstractEndpoint from '../../AbstractEndpoint';
-import AbstractOCPIService from '../../../AbstractOCPIService';
import AppError from '../../../../../exception/AppError';
import { HTTPError } from '../../../../../types/HTTPError';
-import OCPIEndpoint from '../../../../../types/ocpi/OCPIEndpoint';
-import { OCPIResponse } from '../../../../../types/ocpi/OCPIResponse';
import { OCPIStatusCode } from '../../../../../types/ocpi/OCPIStatusCode';
import { OCPITariff } from '../../../../../types/ocpi/OCPITariff';
import OCPIUtils from '../../../OCPIUtils';
@@ -13,24 +9,13 @@ import { PricingSettingsType } from '../../../../../types/Setting';
import { ServerAction } from '../../../../../types/Server';
import SettingStorage from '../../../../../storage/mongodb/SettingStorage';
import { StatusCodes } from 'http-status-codes';
-import Tenant from '../../../../../types/Tenant';
import Utils from '../../../../../utils/Utils';
-const MODULE_NAME = 'EMSPTariffsEndpoint';
+const MODULE_NAME = 'EMSPTariffsService';
-export default class EMSPTariffsEndpoint extends AbstractEndpoint {
- public constructor(ocpiService: AbstractOCPIService) {
- super(ocpiService, 'tariffs');
- }
-
- public async process(req: Request, res: Response, next: NextFunction, tenant: Tenant, ocpiEndpoint: OCPIEndpoint): Promise<OCPIResponse> {
- switch (req.method) {
- case 'GET':
- return this.getTariffRequest(req, res, next, tenant);
- }
- }
-
- private async getTariffRequest(req: Request, res: Response, next: NextFunction, tenant: Tenant): Promise<OCPIResponse> {
+export default class EMSPTariffsService {
+ public static async handleGetTariff(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ const { tenant } = req;
const urlSegment = req.path.substring(1).split('/');
// Remove action
urlSegment.shift();
@@ -40,8 +25,7 @@ export default class EMSPTariffsEndpoint extends AbstractEndpoint {
const tariffId = urlSegment.shift();
if (!countryCode || !partyId || !tariffId) {
throw new AppError({
- action: ServerAction.OCPI_EMSP_GET_TARIFF,
- module: MODULE_NAME, method: 'getTariffRequest',
+ module: MODULE_NAME, method: 'handleGetTariff', action,
errorCode: HTTPError.GENERAL_ERROR,
message: 'Missing request parameters',
ocpiError: OCPIStatusCode.CODE_2001_INVALID_PARAMETER_ERROR
@@ -55,15 +39,15 @@ export default class EMSPTariffsEndpoint extends AbstractEndpoint {
tariff = OCPIUtils.convertSimplePricingSetting2OCPITariff(pricingSettings.simple);
} else {
throw new AppError({
- module: MODULE_NAME, method: 'getTariffRequest',
- action: ServerAction.OCPI_EMSP_GET_TARIFF,
+ module: MODULE_NAME, method: 'handleGetTariff', action,
errorCode: StatusCodes.BAD_REQUEST,
message: `Simple Pricing setting not found in Tenant ${Utils.buildTenantName(tenant)}`,
ocpiError: OCPIStatusCode.CODE_3000_GENERIC_SERVER_ERROR
});
}
}
- return OCPIUtils.success(tariff);
+ res.json(OCPIUtils.success(tariff));
+ next();
}
}

Migrated EMSP Tariffs endpoint
Clean all EMSP old endpoints