From b643077e8d2efa11a697ab5661c8be25dc39da06 Mon Sep 17 00:00:00 2001 From: Kevin L'Hereec Date: Mon, 20 Jul 2026 16:27:38 +0200 Subject: [PATCH] fix: correct orphan references after TR-069 typing refactor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tests/device.spec.ts: fix 6× organizeParameters() calls passing wrapped response object instead of flat ParameterInfoStruct[] (runtime break) - tests/device.spec.ts: fix setTmpParamValues([{x:1}]) with valid shape - immediate_command_state_machine.ts: type handleResponse message param as CWMPTaskResultData (was any, gap in typing contract) - configuration_evaluation_state.ts: replace fragile ../../../app/ dynamic import paths with proper static imports from ../../protocol/tr069_types.js - main_state_machine.ts: replace all inline dynamic import() type casts with static imports (GetParameterNamesResponse, GetParameterValuesResponse, ParameterValueStruct, SetParameterAttribute, DownloadParameter, etc.) --- .../cwmp/configuration_evaluation_state.ts | 5 +-- .../cwmp/immediate_command_state_machine.ts | 3 +- app/services/cwmp/main_state_machine.ts | 29 +++++++++++----- tests/unit/domain/device.spec.ts | 34 ++++++------------- 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/app/services/cwmp/configuration_evaluation_state.ts b/app/services/cwmp/configuration_evaluation_state.ts index 728f685..b239e84 100644 --- a/app/services/cwmp/configuration_evaluation_state.ts +++ b/app/services/cwmp/configuration_evaluation_state.ts @@ -3,6 +3,7 @@ import type { EngineResult } from '../configuration_engine.js' import type { DeviceStateResult, DeviceContext } from './main_state_machine.js' import { PartialOperationalState } from './main_state_machine.js' import DeviceModel from '#models/device' +import type { GetParameterNamesResponse, GetParameterValuesResponse } from '../../protocol/tr069_types.js' import logger from '@adonisjs/core/services/logger' import { ComplianceStatus } from '../../domain/configuration/compliance.js' import { buildProvisioningCode } from '../../utils/provisioning_code.js' @@ -95,7 +96,7 @@ export class ConfigurationEvaluationState { if (this.awaitingPhase === 'gpn_response') { this.awaitingPhase = null if (context.message?.success && context.message.data) { - const gpnResp = context.message.data as import('../../../app/protocol/tr069_types.js').GetParameterNamesResponse + const gpnResp = context.message.data as GetParameterNamesResponse const infoList = gpnResp?.ParameterList?.ParameterInfoStruct ?? [] const leafNames: string[] = infoList .map((info) => { @@ -116,7 +117,7 @@ export class ConfigurationEvaluationState { } else if (this.awaitingPhase === 'gpv_response') { this.awaitingPhase = null if (context.message?.success && context.message.data) { - const gpvResp = context.message.data as import('../../../app/protocol/tr069_types.js').GetParameterValuesResponse + const gpvResp = context.message.data as GetParameterValuesResponse const list = gpvResp?.ParameterList?.ParameterValueStruct ?? [] if (list.length > 0) { const normalized = list.map((pv) => ({ diff --git a/app/services/cwmp/immediate_command_state_machine.ts b/app/services/cwmp/immediate_command_state_machine.ts index e78bf86..0f95031 100644 --- a/app/services/cwmp/immediate_command_state_machine.ts +++ b/app/services/cwmp/immediate_command_state_machine.ts @@ -2,6 +2,7 @@ import { Device } from '../../domain/device/device.js' import { CWMPTaskQueue } from './task_queue.js' import { DeviceActionExecutor } from './device_action_executor.js' import { ExecutingCommandsState, DeviceContext, DeviceStateResult } from './main_state_machine.js' +import type { CWMPTaskResultData } from '../../protocol/tr069_types.js' import logger from '@adonisjs/core/services/logger' /** @@ -30,7 +31,7 @@ export class ImmediateCommandStateMachine { return this.execute() } - async handleResponse(success: boolean, message: any): Promise { + async handleResponse(success: boolean, message: CWMPTaskResultData): Promise { this.context.message = { success, data: message } return this.execute() } diff --git a/app/services/cwmp/main_state_machine.ts b/app/services/cwmp/main_state_machine.ts index 40438b8..9418ba5 100644 --- a/app/services/cwmp/main_state_machine.ts +++ b/app/services/cwmp/main_state_machine.ts @@ -9,7 +9,18 @@ import { ModeType } from '../../domain/shared/mode.js' import logger from '@adonisjs/core/services/logger' import { ConfigurationEvaluationState } from './configuration_evaluation_state.js' import { reclassifyDeviceFromCatalog } from '../parameter_writability.js' -import type { CWMPTaskResultData } from '../../protocol/tr069_types.js' +import type { + CWMPTaskResultData, + GetParameterNamesResponse, + GetParameterValuesResponse, + ParameterValueStruct, +} from '../../protocol/tr069_types.js' +import type { + SetParameterAttribute, + DownloadParameter, + UploadParameter, + RebootParameter, +} from './device_action_executor.js' import { ObjectId } from 'mongodb' // ─── Interfaces ──────────────────────────────────────────────────────────────── @@ -181,7 +192,7 @@ class DiscoveringParametersState extends DeviceState { // GPN success — process the response if (context.message?.success && context.message.data) { try { - const gpnResp = context.message.data as import('../../protocol/tr069_types.js').GetParameterNamesResponse + const gpnResp = context.message.data as GetParameterNamesResponse const infoList = gpnResp?.ParameterList?.ParameterInfoStruct ?? [] context.device.organizeParameters(infoList, new Date()) context.device.setGpnStatus('supported') @@ -245,7 +256,7 @@ class CriticalReadsState extends DeviceState { criticalPaths, async (result) => { if (result.status === 'success') { - const resp = result.data as import('../../protocol/tr069_types.js').GetParameterValuesResponse + const resp = result.data as GetParameterValuesResponse const list = resp?.ParameterList?.ParameterValueStruct ?? [] if (list.length > 0) { context.device.setTmpParamValues(list) @@ -260,7 +271,7 @@ class CriticalReadsState extends DeviceState { // After GPV response: process values from the response, then reclassify if (context.message?.success && context.message.data) { try { - const resp = context.message.data as import('../../protocol/tr069_types.js').GetParameterValuesResponse + const resp = context.message.data as GetParameterValuesResponse const list = resp?.ParameterList?.ParameterValueStruct ?? [] if (list.length > 0) { // Normalize Name fields: XMLParser may return {_: "path", "$xsi:type": "..."} when Name has attributes @@ -337,7 +348,7 @@ export class ExecutingCommandsState extends DeviceState { // Optimistic update for SPV if (this.currentCommandRecord.action === CommandType.SET_PARAMETER_VALUES) { const rawList = Array.isArray(this.currentCommandRecord.parameters) ? this.currentCommandRecord.parameters as Array<{ name?: string; value?: unknown }> : [] - const pvStructs: import('../../protocol/tr069_types.js').ParameterValueStruct[] = rawList.map((p) => ({ + const pvStructs: ParameterValueStruct[] = rawList.map((p) => ({ '@class': '.ParameterValueStruct', Name: p?.name ?? '', Value: { '@class': '.ValueObject', _: String(p?.value ?? ''), $: { '@class': '.ValueObjectAttribute', Type: 'xsd:string' } }, @@ -433,19 +444,19 @@ export class ExecutingCommandsState extends DeviceState { case CommandType.SET_PARAMETER_ATTRIBUTES: { const p = params as Record | unknown[] | undefined const spaParams = Array.isArray(p) ? p : ((p as Record)?.attributes as unknown[] || (p as Record)?.parameterAttributes as unknown[] || []) - return context.services.deviceAction.applySetParameterAttributes(context.device, spaParams as import('../../services/cwmp/device_action_executor.js').SetParameterAttribute[], () => Promise.resolve('')) + return context.services.deviceAction.applySetParameterAttributes(context.device, spaParams as SetParameterAttribute[], () => Promise.resolve('')) } case CommandType.DOWNLOAD: { const p = (params as Record) || {} - return context.services.deviceAction.applyDownload(context.device, p as unknown as import('../../services/cwmp/device_action_executor.js').DownloadParameter, () => Promise.resolve('')) + return context.services.deviceAction.applyDownload(context.device, p as unknown as DownloadParameter, () => Promise.resolve('')) } case CommandType.UPLOAD: { const p = (params as Record) || {} - return context.services.deviceAction.applyUpload(context.device, p as unknown as import('../../services/cwmp/device_action_executor.js').UploadParameter, () => Promise.resolve('')) + return context.services.deviceAction.applyUpload(context.device, p as unknown as UploadParameter, () => Promise.resolve('')) } case CommandType.REBOOT: { const p = (params as Record) || {} - return context.services.deviceAction.applyReboot(context.device, p as import('../../services/cwmp/device_action_executor.js').RebootParameter, () => Promise.resolve('')) + return context.services.deviceAction.applyReboot(context.device, p as RebootParameter, () => Promise.resolve('')) } case CommandType.FACTORY_RESET: { return context.services.deviceAction.applyFactoryReset(context.device, {}, () => Promise.resolve('')) diff --git a/tests/unit/domain/device.spec.ts b/tests/unit/domain/device.spec.ts index 454143e..ec25d0c 100644 --- a/tests/unit/domain/device.spec.ts +++ b/tests/unit/domain/device.spec.ts @@ -89,14 +89,10 @@ describe('Device', () => { d.setProperty(new DeviceProperty('Device.ManagementServer.PeriodicInformInterval', '60')) d.setProperty(new DeviceProperty('Device.DeviceInfo.Manufacturer', 'Sagemcom')) d.organizeParameters( - { - ParameterList: { - ParameterInfoStruct: [ - { Name: 'Device.ManagementServer.PeriodicInformInterval', Writable: '1' }, - { Name: 'Device.DeviceInfo.Manufacturer', Writable: '0' }, - ], - }, - }, + [ + { Name: 'Device.ManagementServer.PeriodicInformInterval', Writable: '1' }, + { Name: 'Device.DeviceInfo.Manufacturer', Writable: '0' }, + ], new Date() ) // Writable=1 → parameter, Writable=0 → property @@ -153,7 +149,7 @@ describe('Device', () => { it('clearTmpValues resets temporary state', () => { const d = makeDevice() - d.setTmpParamValues([{ x: 1 }]) + d.setTmpParamValues([{ Name: 'Device.X', Value: { _: '1' } }]) d.tmpPC = 'code' d.clearTmpValues() expect(d.getTmpParamValues()).toEqual([]) @@ -163,9 +159,7 @@ describe('Device', () => { it('organizeParameters moves writable property to parameter', () => { const d = makeDevice() d.setProperty(new DeviceProperty('Device.X', 'val')) - d.organizeParameters({ - ParameterList: { ParameterInfoStruct: [{ Name: 'Device.X', Writable: '1' }] }, - }, new Date()) + d.organizeParameters([{ Name: 'Device.X', Writable: '1' }], new Date()) expect(d.getParameter('Device.X')).toBeDefined() expect(d.getProperty('Device.X')).toBeNull() }) @@ -173,34 +167,26 @@ describe('Device', () => { it('organizeParameters moves non-writable parameter to property', () => { const d = makeDevice() d.setParameter(new DeviceParameter('Device.Y', 'val')) - d.organizeParameters({ - ParameterList: { ParameterInfoStruct: [{ Name: 'Device.Y', Writable: 0 }] }, - }, new Date()) + d.organizeParameters([{ Name: 'Device.Y', Writable: 0 }], new Date()) expect(d.getProperty('Device.Y')).toBeDefined() expect(d.getParameter('Device.Y')).toBeNull() }) it('organizeParameters adds new writable as parameter', () => { const d = makeDevice() - d.organizeParameters({ - ParameterList: { ParameterInfoStruct: [{ Name: 'Device.New.Param', Writable: '1' }] }, - }, new Date()) + d.organizeParameters([{ Name: 'Device.New.Param', Writable: '1' }], new Date()) expect(d.getParameter('Device.New.Param')).toBeDefined() }) it('organizeParameters adds new non-writable as property', () => { const d = makeDevice() - d.organizeParameters({ - ParameterList: { ParameterInfoStruct: [{ Name: 'Device.New.Prop', Writable: '0' }] }, - }, new Date()) + d.organizeParameters([{ Name: 'Device.New.Prop', Writable: '0' }], new Date()) expect(d.getProperty('Device.New.Prop')).toBeDefined() }) it('organizeParameters skips object paths (ending with dot)', () => { const d = makeDevice() - d.organizeParameters({ - ParameterList: { ParameterInfoStruct: [{ Name: 'Device.Info.', Writable: '1' }] }, - }, new Date()) + d.organizeParameters([{ Name: 'Device.Info.', Writable: '1' }], new Date()) expect(d.getParameter('Device.Info.')).toBeNull() expect(d.getProperty('Device.Info.')).toBeNull() })