]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
refactor: consolidate enforceMessageLimits types with generic R and RejectionReason
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 27 Mar 2026 13:23:14 +0000 (14:23 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 27 Mar 2026 13:23:14 +0000 (14:23 +0100)
- Add generic type parameter R to enforceMessageLimits and
  enforcePostCalculationBytesLimit, eliminating 4 'as typeof' casts
- Extract RejectionReason interface (additionalInfo + reasonCode) to
  replace inline anonymous types, aligned with StatusInfoType fields
- Remove all enum→string→enum round-trips in callers

src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts
src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts
tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-enforceMessageLimits.test.ts

index a7739c36196278bfbeb07c829c8aab1d8f02276c..fe1d2e2e5a3e76d81555da0b08a0251e9b3cff09 100644 (file)
@@ -584,8 +584,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
       (v, reason) => ({
         attributeStatus: GetVariableStatusEnumType.Rejected,
         attributeStatusInfo: {
-          additionalInfo: reason.info,
-
+          additionalInfo: reason.additionalInfo,
           reasonCode: reason.reasonCode,
         },
         attributeType: v.attributeType,
@@ -595,8 +594,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
       logger
     )
     if (preEnforcement.rejected) {
-      getVariablesResponse.getVariableResult =
-        preEnforcement.results as typeof getVariablesResponse.getVariableResult
+      getVariablesResponse.getVariableResult = preEnforcement.results
       return getVariablesResponse
     }
 
@@ -613,16 +611,15 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
       (v, reason) => ({
         attributeStatus: GetVariableStatusEnumType.Rejected,
         attributeStatusInfo: {
-          additionalInfo: reason.info,
-
-          reasonCode: ReasonCodeEnumType[reason.reasonCode as keyof typeof ReasonCodeEnumType],
+          additionalInfo: reason.additionalInfo,
+          reasonCode: reason.reasonCode,
         },
         attributeType: v.attributeType,
         component: v.component,
         variable: v.variable,
       }),
       logger
-    ) as typeof getVariablesResponse.getVariableResult
+    )
 
     logger.debug(
       `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetVariables: Processed ${commandPayload.getVariableData.length.toString()} variable requests, returning ${results.length.toString()} results`
@@ -655,8 +652,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
       (v, reason) => ({
         attributeStatus: SetVariableStatusEnumType.Rejected,
         attributeStatusInfo: {
-          additionalInfo: reason.info,
-
+          additionalInfo: reason.additionalInfo,
           reasonCode: reason.reasonCode,
         },
         attributeType: v.attributeType ?? AttributeEnumType.Actual,
@@ -666,8 +662,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
       logger
     )
     if (preEnforcement.rejected) {
-      setVariablesResponse.setVariableResult =
-        preEnforcement.results as typeof setVariablesResponse.setVariableResult
+      setVariablesResponse.setVariableResult = preEnforcement.results
       return setVariablesResponse
     }
 
@@ -682,16 +677,15 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
       (v, reason) => ({
         attributeStatus: SetVariableStatusEnumType.Rejected,
         attributeStatusInfo: {
-          additionalInfo: reason.info,
-
-          reasonCode: ReasonCodeEnumType[reason.reasonCode as keyof typeof ReasonCodeEnumType],
+          additionalInfo: reason.additionalInfo,
+          reasonCode: reason.reasonCode,
         },
         attributeType: v.attributeType ?? AttributeEnumType.Actual,
         component: v.component,
         variable: v.variable,
       }),
       logger
-    ) as typeof setVariablesResponse.setVariableResult
+    )
 
     logger.debug(
       `${chargingStation.logPrefix()} ${moduleName}.handleRequestSetVariables: Processed ${commandPayload.setVariableData.length.toString()} variable requests, returning ${results.length.toString()} results`
index a29dc1dab7e451a34f72be37103f2f2c7ee976a8..9070127ca63d36bdd3c4a402413a3cb5088aa0fa 100644 (file)
@@ -49,6 +49,11 @@ import { OCPP20VariableManager } from './OCPP20VariableManager.js'
 
 const moduleName = 'OCPP20ServiceUtils'
 
+export interface RejectionReason {
+  additionalInfo: string
+  reasonCode: ReasonCodeEnumType
+}
+
 export class OCPP20ServiceUtils extends OCPPServiceUtils {
   private static readonly incomingRequestSchemaNames: readonly [
     OCPP20IncomingRequestCommand,
@@ -182,7 +187,8 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
     ])
 
   public static enforceMessageLimits<
-    T extends { attributeType?: unknown; component: unknown; variable: unknown }
+    T extends { attributeType?: unknown; component: unknown; variable: unknown },
+    R
   >(
     chargingStation: { logPrefix: () => string },
     moduleName: string,
@@ -190,13 +196,13 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
     data: T[],
     itemsLimit: number,
     bytesLimit: number,
-    buildRejected: (item: T, reason: { info: string; reasonCode: ReasonCodeEnumType }) => unknown,
+    buildRejected: (item: T, reason: RejectionReason) => R,
     logger: { debug: (...args: unknown[]) => void }
-  ): { rejected: boolean; results: unknown[] } {
+  ): { rejected: boolean; results: R[] } {
     if (itemsLimit > 0 && data.length > itemsLimit) {
       const results = data.map(d =>
         buildRejected(d, {
-          info: `ItemsPerMessage limit ${itemsLimit.toString()} exceeded (${data.length.toString()} requested)`,
+          additionalInfo: `ItemsPerMessage limit ${itemsLimit.toString()} exceeded (${data.length.toString()} requested)`,
           reasonCode: ReasonCodeEnumType.TooManyElements,
         })
       )
@@ -210,7 +216,7 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
       if (estimatedSize > bytesLimit) {
         const results = data.map(d =>
           buildRejected(d, {
-            info: `BytesPerMessage limit ${bytesLimit.toString()} exceeded (estimated ${estimatedSize.toString()} bytes)`,
+            additionalInfo: `BytesPerMessage limit ${bytesLimit.toString()} exceeded (estimated ${estimatedSize.toString()} bytes)`,
             reasonCode: ReasonCodeEnumType.TooLargeElement,
           })
         )
@@ -224,24 +230,25 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
   }
 
   public static enforcePostCalculationBytesLimit<
-    T extends { attributeType?: unknown; component: unknown; variable: unknown }
+    T extends { attributeType?: unknown; component: unknown; variable: unknown },
+    R
   >(
     chargingStation: { logPrefix: () => string },
     moduleName: string,
     context: string,
     originalData: T[],
-    currentResults: unknown[],
+    currentResults: R[],
     bytesLimit: number,
-    buildRejected: (item: T, reason: { info: string; reasonCode: ReasonCodeEnumType }) => unknown,
+    buildRejected: (item: T, reason: RejectionReason) => R,
     logger: { debug: (...args: unknown[]) => void }
-  ): unknown[] {
+  ): R[] {
     if (bytesLimit > 0) {
       try {
         const actualSize = Buffer.byteLength(JSON.stringify(currentResults), 'utf8')
         if (actualSize > bytesLimit) {
           const results = originalData.map(d =>
             buildRejected(d, {
-              info: `BytesPerMessage limit ${bytesLimit.toString()} exceeded (actual ${actualSize.toString()} bytes)`,
+              additionalInfo: `BytesPerMessage limit ${bytesLimit.toString()} exceeded (actual ${actualSize.toString()} bytes)`,
               reasonCode: ReasonCodeEnumType.TooLargeElement,
             })
           )
index 22650077cae9337d1396aa088d467306e8bdc5fb..88a76361c03b8ac23b7f0e394cb39c0a1eaa5d80 100644 (file)
@@ -6,7 +6,10 @@
 import assert from 'node:assert/strict'
 import { afterEach, describe, it } from 'node:test'
 
-import { OCPP20ServiceUtils } from '../../../../src/charging-station/ocpp/2.0/OCPP20ServiceUtils.js'
+import {
+  OCPP20ServiceUtils,
+  type RejectionReason,
+} from '../../../../src/charging-station/ocpp/2.0/OCPP20ServiceUtils.js'
 import { ReasonCodeEnumType } from '../../../../src/types/index.js'
 import { standardCleanup } from '../../../helpers/TestLifecycleHelpers.js'
 
@@ -16,7 +19,7 @@ interface MockLogger {
 }
 
 interface RejectedResult {
-  info: string
+  additionalInfo: string
   original: TestItem
   reasonCode: ReasonCodeEnumType
 }
@@ -58,11 +61,8 @@ function makeMockStation () {
 
 /** @returns A builder function that creates rejected result objects */
 function makeRejectedBuilder () {
-  return (
-    item: TestItem,
-    reason: { info: string; reasonCode: ReasonCodeEnumType }
-  ): RejectedResult => ({
-    info: reason.info,
+  return (item: TestItem, reason: RejectionReason): RejectedResult => ({
+    additionalInfo: reason.additionalInfo,
     original: item,
     reasonCode: reason.reasonCode,
   })
@@ -173,9 +173,9 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => {
 
       assert.strictEqual(result.rejected, true)
       assert.strictEqual(result.results.length, 3)
-      for (const r of result.results as RejectedResult[]) {
+      for (const r of result.results) {
         assert.strictEqual(r.reasonCode, ReasonCodeEnumType.TooManyElements)
-        assert.ok(r.info.includes('ItemsPerMessage limit 2'))
+        assert.ok(r.additionalInfo.includes('ItemsPerMessage limit 2'))
       }
     })
 
@@ -197,7 +197,7 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => {
 
       assert.strictEqual(result.rejected, true)
       assert.strictEqual(result.results.length, 2)
-      for (const r of result.results as RejectedResult[]) {
+      for (const r of result.results) {
         assert.strictEqual(r.reasonCode, ReasonCodeEnumType.TooManyElements)
       }
     })
@@ -262,9 +262,9 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => {
 
       assert.strictEqual(result.rejected, true)
       assert.strictEqual(result.results.length, 1)
-      const r = (result.results as RejectedResult[])[0]
+      const r = result.results[0]
       assert.strictEqual(r.reasonCode, ReasonCodeEnumType.TooLargeElement)
-      assert.ok(r.info.includes('BytesPerMessage limit 1'))
+      assert.ok(r.additionalInfo.includes('BytesPerMessage limit 1'))
     })
 
     await it('should reject all items with TooLargeElement for multiple items over bytes limit', () => {
@@ -285,7 +285,7 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => {
 
       assert.strictEqual(result.rejected, true)
       assert.strictEqual(result.results.length, 2)
-      for (const r of result.results as RejectedResult[]) {
+      for (const r of result.results) {
         assert.strictEqual(r.reasonCode, ReasonCodeEnumType.TooLargeElement)
       }
     })
@@ -329,7 +329,7 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => {
       )
 
       assert.strictEqual(result.rejected, true)
-      for (const r of result.results as RejectedResult[]) {
+      for (const r of result.results) {
         assert.strictEqual(r.reasonCode, ReasonCodeEnumType.TooManyElements)
       }
     })
@@ -364,7 +364,7 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => {
       const station = makeMockStation()
       const logger = makeMockLogger()
       const item = makeItem('WebSocketPingInterval', 'xyz')
-      const capturedReasons: { info: string; reasonCode: ReasonCodeEnumType }[] = []
+      const capturedReasons: RejectionReason[] = []
 
       OCPP20ServiceUtils.enforceMessageLimits(
         station,
@@ -382,8 +382,8 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => {
 
       assert.strictEqual(capturedReasons.length, 1)
       assert.strictEqual(capturedReasons[0].reasonCode, ReasonCodeEnumType.TooLargeElement)
-      assert.strictEqual(typeof capturedReasons[0].info, 'string')
-      assert.ok(capturedReasons[0].info.length > 0)
+      assert.strictEqual(typeof capturedReasons[0].additionalInfo, 'string')
+      assert.ok(capturedReasons[0].additionalInfo.length > 0)
     })
   })
 })