Add tenant ID in Axios
MODIFY
src/client/ocpi/OCPIClient.ts
src/client/ocpi/OCPIClient.ts
@@ -34,7 +34,7 @@ export default abstract class OCPIClient {
module: MODULE_NAME, method: 'constructor',
});
}
- this.axiosInstance = AxiosFactory.getAxiosInstance();
+ this.axiosInstance = AxiosFactory.getAxiosInstance(tenant.id);
this.tenant = tenant;
this.settings = settings;
this.ocpiEndpoint = ocpiEndpoint;

MODIFY
src/integration/asset/schneider/SchneiderAssetIntegration.ts
src/integration/asset/schneider/SchneiderAssetIntegration.ts
@@ -19,7 +19,7 @@ export default class SchneiderAssetIntegration extends AssetIntegration<AssetSet
public constructor(tenantID: string, settings: AssetSetting, connection: AssetConnectionSetting) {
super(tenantID, settings, connection);
- this.axiosInstance = AxiosFactory.getAxiosInstance();
+ this.axiosInstance = AxiosFactory.getAxiosInstance(tenantID);
}
public async checkConnection(): Promise<void> {

MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
@@ -32,7 +32,7 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
constructor(tenantId: string, settings: StripeBillingSetting) {
super(tenantId, settings);
- this.axiosInstance = AxiosFactory.getAxiosInstance();
+ this.axiosInstance = AxiosFactory.getAxiosInstance(this.tenantID);
this.settings.currency = settings.currency;
if (this.settings.secretKey) {
this.settings.secretKey = Cypher.decrypt(settings.secretKey);

MODIFY
src/integration/car/ev-database/EVDabaseCarIntegration.ts
src/integration/car/ev-database/EVDabaseCarIntegration.ts
@@ -17,7 +17,7 @@ export default class EVDabaseCarIntegration extends CarIntegration {
constructor() {
super();
- this.axiosInstance = AxiosFactory.getAxiosInstance();
+ this.axiosInstance = AxiosFactory.getAxiosInstance(Constants.DEFAULT_TENANT);
}
public async getCarCatalogs(): Promise<CarCatalog[]> {

MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/CPOCommandsEndpoint.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/CPOCommandsEndpoint.ts
-import { AxiosInstance, AxiosResponse } from 'axios';
-import ChargingStation, { Connector, RemoteAuthorization } from '../../../../types/ChargingStation';
import { NextFunction, Request, Response } from 'express';
-import { OCPICommandResponse, OCPICommandResponseType } from '../../../../types/ocpi/OCPICommandResponse';
-
-import AbstractEndpoint from '../AbstractEndpoint';
-import AbstractOCPIService from '../../AbstractOCPIService';
-import AppError from '../../../../exception/AppError';
-import AxiosFactory from '../../../../utils/AxiosFactory';
-import BackendError from '../../../../exception/BackendError';
-import { ChargePointStatus } from '../../../../types/ocpp/OCPPServer';
+import HttpStatusCodes from 'http-status-codes';
+import moment from 'moment';
import ChargingStationClientFactory from '../../../../client/ocpp/ChargingStationClientFactory';
+import AppError from '../../../../exception/AppError';
import ChargingStationStorage from '../../../../storage/mongodb/ChargingStationStorage';
-import Constants from '../../../../utils/Constants';
-import HttpStatusCodes from 'http-status-codes';
-import Logging from '../../../../utils/Logging';
+import TransactionStorage from '../../../../storage/mongodb/TransactionStorage';
+import UserStorage from '../../../../storage/mongodb/UserStorage';
+import ChargingStation, { Connector, RemoteAuthorization } from '../../../../types/ChargingStation';
+import { OCPICommandResponse, OCPICommandResponseType } from '../../../../types/ocpi/OCPICommandResponse';
import { OCPICommandType } from '../../../../types/ocpi/OCPICommandType';
import OCPIEndpoint from '../../../../types/ocpi/OCPIEndpoint';
import { OCPIResponse } from '../../../../types/ocpi/OCPIResponse';
import { OCPIStartSession } from '../../../../types/ocpi/OCPIStartSession';
import { OCPIStatusCode } from '../../../../types/ocpi/OCPIStatusCode';
import { OCPIStopSession } from '../../../../types/ocpi/OCPIStopSession';
-import OCPITokensService from './OCPITokensService';
-import OCPIUtils from '../../OCPIUtils';
import { OCPPRemoteStartStopStatus } from '../../../../types/ocpp/OCPPClient';
+import { ChargePointStatus } from '../../../../types/ocpp/OCPPServer';
import { ServerAction } from '../../../../types/Server';
import Tenant from '../../../../types/Tenant';
-import TransactionStorage from '../../../../storage/mongodb/TransactionStorage';
-import UserStorage from '../../../../storage/mongodb/UserStorage';
+import AxiosFactory from '../../../../utils/AxiosFactory';
+import Constants from '../../../../utils/Constants';
+import Logging from '../../../../utils/Logging';
import Utils from '../../../../utils/Utils';
-import moment from 'moment';
+import AbstractOCPIService from '../../AbstractOCPIService';
+import OCPIUtils from '../../OCPIUtils';
+import AbstractEndpoint from '../AbstractEndpoint';
+import OCPITokensService from './OCPITokensService';
+
const EP_IDENTIFIER = 'commands';
const MODULE_NAME = 'CPOCommandsEndpoint';
@@ -37,12 +35,10 @@ const MODULE_NAME = 'CPOCommandsEndpoint';
* EMSP Tokens Endpoint
*/
export default class CPOCommandsEndpoint extends AbstractEndpoint {
- private axiosInstance: AxiosInstance;
// Create OCPI Service
constructor(ocpiService: AbstractOCPIService) {
super(ocpiService, EP_IDENTIFIER);
- this.axiosInstance = AxiosFactory.getAxiosInstance();
}
/**
@@ -330,7 +326,7 @@ export default class CPOCommandsEndpoint extends AbstractEndpoint {
});
// Call IOP
try {
- await this.axiosInstance.post(responseUrl, payload,
+ await AxiosFactory.getAxiosInstance(tenant.id).post(responseUrl, payload,
{
headers: {
Authorization: `Token ${ocpiEndpoint.token}`,

MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/CredentialsEndpoint.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/CredentialsEndpoint.ts
-import { AxiosInstance, AxiosResponse } from 'axios';
+import { AxiosResponse } from 'axios';
import { NextFunction, Request, Response } from 'express';
-
-import AbstractEndpoint from '../AbstractEndpoint';
-import AbstractOCPIService from '../../AbstractOCPIService';
import AppError from '../../../../exception/AppError';
-import AxiosFactory from '../../../../utils/AxiosFactory';
import BackendError from '../../../../exception/BackendError';
-import Constants from '../../../../utils/Constants';
+import OCPIEndpointStorage from '../../../../storage/mongodb/OCPIEndpointStorage';
import { HTTPError } from '../../../../types/HTTPError';
-import Logging from '../../../../utils/Logging';
import OCPICredential from '../../../../types/ocpi/OCPICredential';
import OCPIEndpoint from '../../../../types/ocpi/OCPIEndpoint';
-import OCPIEndpointStorage from '../../../../storage/mongodb/OCPIEndpointStorage';
-import OCPIMapping from './OCPIMapping';
import { OCPIRegistrationStatus } from '../../../../types/ocpi/OCPIRegistrationStatus';
import { OCPIResponse } from '../../../../types/ocpi/OCPIResponse';
import { OCPIStatusCode } from '../../../../types/ocpi/OCPIStatusCode';
-import OCPIUtils from '../../OCPIUtils';
import { ServerAction } from '../../../../types/Server';
import Tenant from '../../../../types/Tenant';
+import AxiosFactory from '../../../../utils/AxiosFactory';
+import Constants from '../../../../utils/Constants';
+import Logging from '../../../../utils/Logging';
import Utils from '../../../../utils/Utils';
+import AbstractOCPIService from '../../AbstractOCPIService';
+import OCPIUtils from '../../OCPIUtils';
+import AbstractEndpoint from '../AbstractEndpoint';
+import OCPIMapping from './OCPIMapping';
+
const EP_IDENTIFIER = 'credentials';
const MODULE_NAME = 'CredentialsEndpoint';
@@ -28,11 +28,8 @@ const MODULE_NAME = 'CredentialsEndpoint';
* Credentials Endpoint
*/
export default class CredentialsEndpoint extends AbstractEndpoint {
- private axiosInstance: AxiosInstance;
-
constructor(ocpiService: AbstractOCPIService) {
super(ocpiService, EP_IDENTIFIER);
- this.axiosInstance = AxiosFactory.getAxiosInstance();
}
/**
@@ -160,7 +157,7 @@ export default class CredentialsEndpoint extends AbstractEndpoint {
let response: AxiosResponse;
try {
// Access versions API
- response = await this.axiosInstance.get(ocpiEndpoint.baseUrl, {
+ response = await AxiosFactory.getAxiosInstance(tenant.id).get(ocpiEndpoint.baseUrl, {
headers: {
'Authorization': `Token ${ocpiEndpoint.token}`
},
@@ -218,7 +215,7 @@ export default class CredentialsEndpoint extends AbstractEndpoint {
// Try to read endpoints
try {
// Access versions API
- response = await this.axiosInstance.get(ocpiEndpoint.versionUrl, {
+ response = await AxiosFactory.getAxiosInstance(tenant.id).get(ocpiEndpoint.versionUrl, {
headers: {
'Authorization': `Token ${ocpiEndpoint.token}`
}

MODIFY
src/server/rest/service/AuthService.ts
src/server/rest/service/AuthService.ts
@@ -48,8 +48,6 @@ if (_centralSystemRestConfig) {
const MODULE_NAME = 'AuthService';
export default class AuthService {
- private static axiosInstance = AxiosFactory.getAxiosInstance();
-
public static initialize(): Handler {
return passport.initialize();
}
@@ -198,7 +196,7 @@ export default class AuthService {
let response: AxiosResponse;
const recaptchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${_centralSystemRestConfig.captchaSecretKey}&response=${filteredRequest.captcha}&remoteip=${req.connection.remoteAddress}`;
try {
- response = await AuthService.axiosInstance.get(recaptchaURL);
+ response = await AxiosFactory.getAxiosInstance(tenantID).get(recaptchaURL);
} catch (error) {
// Handle errors
Utils.handleAxiosError(error, recaptchaURL, action, MODULE_NAME, 'handleRegisterUser');
@@ -338,7 +336,7 @@ export default class AuthService {
let response: AxiosResponse;
const recaptchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${_centralSystemRestConfig.captchaSecretKey}&response=${filteredRequest.captcha}&remoteip=${req.connection.remoteAddress}`;
try {
- response = await AuthService.axiosInstance.get(recaptchaURL);
+ response = await AxiosFactory.getAxiosInstance(tenantID).get(recaptchaURL);
} catch (error) {
// Handle errors
Utils.handleAxiosError(error, recaptchaURL, action, MODULE_NAME, 'handleRegisterUser');
@@ -730,7 +728,7 @@ export default class AuthService {
const recaptchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${_centralSystemRestConfig.captchaSecretKey}&response=${filteredRequest.captcha}&remoteip=${req.connection.remoteAddress}`;
let response: AxiosResponse;
try {
- response = await AuthService.axiosInstance.get(recaptchaURL);
+ response = await AxiosFactory.getAxiosInstance(tenantID).get(recaptchaURL);
} catch (error) {
// Handle errors
Utils.handleAxiosError(error, recaptchaURL, action, MODULE_NAME, 'handleResendVerificationEmail');

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -356,4 +356,7 @@ export enum ServerAction {
SMART_CHARGING = 'SmartCharging',
INSTANTIATE_DUMMY_MODULE = 'InstantiateDummyModule',
+
+ AXIOS_REQUEST = 'AxiosRequest',
+ AXIOS_RESPONSE = 'AxiosResponse',
}

MODIFY
src/utils/AxiosFactory.ts
src/utils/AxiosFactory.ts
-import axios, { AxiosInstance } from 'axios';
+import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import axiosRetry from 'axios-retry';
+import { ServerAction } from '../types/Server';
import Constants from './Constants';
+import Logging from './Logging';
+const MODULE_NAME = 'AxiosFactory';
export default class AxiosFactory {
- private static axiosInstance: AxiosInstance;
+ private static axiosInstances: Map<string, AxiosInstance> = new Map();
private static readonly maxRetries: number = 3;
private constructor() {}
- public static getAxiosInstance(): AxiosInstance {
- AxiosFactory.setupAxiosInstance();
- if (!AxiosFactory.axiosInstance) {
+ // All could have been done at 'axios' level normally!
+ public static getAxiosInstance(tenantID: string): AxiosInstance {
+ // Get from map
+ let axiosInstance = this.axiosInstances.get(tenantID);
+ if (!axiosInstance) {
// Create
- AxiosFactory.axiosInstance = axios.create();
+ axiosInstance = axios.create();
// Time out
- AxiosFactory.axiosInstance.defaults.timeout = Constants.AXIOS_TIMEOUT;
+ axiosInstance.defaults.timeout = Constants.AXIOS_TIMEOUT;
+ // Add retry
+ axiosRetry(axiosInstance, {
+ retries: this.maxRetries,
+ retryDelay: axiosRetry.exponentialDelay.bind(this)
+ });
+ // Add a Request interceptor
+ axiosInstance.interceptors.request.use((request: AxiosRequestConfig) => {
+ Logging.logDebug({
+ tenantID: tenantID,
+ action: ServerAction.AXIOS_REQUEST,
+ message: `Http Request sent: '${request.url}'`,
+ module: MODULE_NAME, method: 'interceptor',
+ detailedMessages: { request }
+ });
+ return request;
+ }, async (error: AxiosError) =>
+ // Error handling is done outside to get the proper module/stack trace
+ Promise.reject(error)
+ );
+ // Add a Response interceptor
+ axiosInstance.interceptors.response.use((response: AxiosResponse) => {
+ Logging.logDebug({
+ tenantID: tenantID,
+ action: ServerAction.AXIOS_RESPONSE,
+ message: `Http Response received: '${response.config.url}'`,
+ module: MODULE_NAME, method: 'interceptor',
+ detailedMessages: {
+ status: response.status,
+ request: response.config,
+ response: response.data
}
- return AxiosFactory.axiosInstance;
+ });
+ return response;
+ }, async (error: AxiosError) =>
+ // Error handling is done outside to get the proper module/stack trace
+ Promise.reject(error)
+ );
+ // Add
+ this.axiosInstances.set(tenantID, axiosInstance);
}
-
- private static setupAxiosInstance() {
- axiosRetry(axios, { retries: this.maxRetries, retryDelay: axiosRetry.exponentialDelay.bind(this) });
+ return axiosInstance;
}
}

MODIFY
src/utils/Utils.ts
src/utils/Utils.ts
@@ -94,6 +94,10 @@ export default class Utils {
}
}
+ public static isDevMode(): boolean {
+ return process.env.NODE_ENV === 'development';
+ }
+
public static isTransactionInProgressOnThreePhases(chargingStation: ChargingStation, transaction: Transaction): boolean {
const currentType = Utils.getChargingStationCurrentType(chargingStation, null, transaction.connectorId);
let threePhases = true;

MODIFY
test/api/client/utils/BaseApi.ts
test/api/client/utils/BaseApi.ts
@@ -6,15 +6,14 @@ import querystring from 'querystring';
export default class BaseApi {
private baseURL: string;
- private axiosInstance: AxiosInstance;
public constructor(baseURL: string) {
- this.axiosInstance = AxiosFactory.getAxiosInstance();
this.baseURL = baseURL;
}
public async send(httpRequest: AxiosRequestConfig): Promise<any> {
let httpResponse;
+ const axiosInstance = AxiosFactory.getAxiosInstance('default');
// Set the base URL
httpRequest.baseURL = this.baseURL;
// Set the Query String
@@ -26,7 +25,7 @@ export default class BaseApi {
try {
t0 = performance.now();
// Execute with Axios
- httpResponse = await this.axiosInstance(httpRequest);
+ httpResponse = await axiosInstance(httpRequest);
t1 = performance.now();
} catch (error) {
// Handle errors

Add tenant ID in Axios