diff --git a/packages/binding-netconf/src/async-node-netconf.ts b/packages/binding-netconf/src/async-node-netconf.ts index ee3798f4d..d05541c4b 100644 --- a/packages/binding-netconf/src/async-node-netconf.ts +++ b/packages/binding-netconf/src/async-node-netconf.ts @@ -16,9 +16,9 @@ import * as nodeNetconf from "node-netconf"; import * as xpath2json from "./xpath2json"; import { promises as fsPromises } from "fs"; import { NetConfCredentials, RpcMethod } from "./netconf"; -import { createDebugLogger } from "@node-wot/core"; +import { createLoggers } from "@node-wot/core"; -const debug = createDebugLogger("binding-netconf", "async-node-netconf"); +const { debug, warn } = createLoggers("binding-netconf", "async-node-netconf"); type RouterParams = { host: string; @@ -47,18 +47,18 @@ const METHOD_OBJ = { RPC: {}, }; export class Client { - private router: nodeNetconf.Client; + private router: nodeNetconf.Client | null; private connected: boolean; - private routerParams: RouterParams; + private routerParams?: RouterParams; constructor() { this.router = null; this.connected = false; } - getRouter(): nodeNetconf.Client { + getRouter(): nodeNetconf.Client | null { return this.router; } @@ -92,13 +92,22 @@ export class Client { // close the old one this.closeRouter(); } + + if (!this.routerParams) { + reject(new Error("Router params not initialized")); + return; + } + this.router = new nodeNetconf.Client(this.routerParams); this.router.open((err?: string) => { if (err) { reject(err); } else { debug( - `New NetConf router opened connection with host ${this.routerParams.host}, port ${this.routerParams.port}, username ${this.routerParams.username}` + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + `New NetConf router opened connection with host ${this.routerParams!.host}, port ${ + this.routerParams!.port + }, username ${this.routerParams!.username}` ); this.connected = true; resolve(undefined); @@ -142,6 +151,11 @@ export class Client { break; } } + if (this.router === null) { + reject(new Error("Router not initialized")); + return; + } + this.router.rpc(finalRequest, (err: string, results: unknown) => { if (err) { reject(err); @@ -152,7 +166,10 @@ export class Client { } closeRouter(): void { - this.router.close(); + if (this.router === null) { + warn("Closing an already cleared router."); + } + this.router?.close(); this.connected = false; } } diff --git a/packages/binding-netconf/src/codecs/netconf-codec.ts b/packages/binding-netconf/src/codecs/netconf-codec.ts index f8acbefbb..370a00e59 100644 --- a/packages/binding-netconf/src/codecs/netconf-codec.ts +++ b/packages/binding-netconf/src/codecs/netconf-codec.ts @@ -84,8 +84,8 @@ export default class NetconfCodec { } catch (err) { if (err instanceof SyntaxError) { if (bytes.byteLength === 0) { - // empty payload -> void/undefined - return undefined; + // empty payload -> void/null + return null; } else { // be relaxed about what is received -> string without quotes return bytes.toString(); diff --git a/packages/binding-netconf/src/netconf-client.ts b/packages/binding-netconf/src/netconf-client.ts index f72bb493c..ebd6281c0 100644 --- a/packages/binding-netconf/src/netconf-client.ts +++ b/packages/binding-netconf/src/netconf-client.ts @@ -33,7 +33,7 @@ export default class NetconfClient implements ProtocolClient { private credentials: NetConfCredentials; constructor() { this.client = new AsyncNodeNetcon.Client(); - this.credentials = null; + this.credentials = { username: "" }; } public toString(): string { @@ -132,7 +132,7 @@ export default class NetconfClient implements ProtocolClient { payload = payload.payload; result = JSON.stringify(await this.client.rpc(xpathQuery, method, NSs, target, payload)); } catch (err) { - debug(err.toString()); + debug(JSON.stringify(err)); throw err; } @@ -155,7 +155,7 @@ export default class NetconfClient implements ProtocolClient { complete?: () => void ): Promise { const unimplementedError = new Error(`NetconfClient does not implement subscribe`); - error(unimplementedError); + error?.(unimplementedError); throw unimplementedError; } diff --git a/packages/binding-netconf/src/netconf.ts b/packages/binding-netconf/src/netconf.ts index d4052e423..7715d5e64 100644 --- a/packages/binding-netconf/src/netconf.ts +++ b/packages/binding-netconf/src/netconf.ts @@ -33,6 +33,7 @@ export interface NetConfCredentials { privateKey?: string; } -export function isRpcMethod(method: string): method is RpcMethod { +export function isRpcMethod(method?: string): method is RpcMethod { + if (!method) return false; return ["GET-CONFIG", "EDIT-CONFIG", "COMMIT", "RPC"].includes(method); } diff --git a/packages/binding-netconf/src/xpath2json.ts b/packages/binding-netconf/src/xpath2json.ts index fc02ebf6b..ad336b30a 100644 --- a/packages/binding-netconf/src/xpath2json.ts +++ b/packages/binding-netconf/src/xpath2json.ts @@ -13,13 +13,13 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ -export function isObject(a: unknown): boolean { - return !!a && a.constructor === Object; +export function isPlainObject(a: unknown): boolean { + return typeof a === "object" && a !== null && !Array.isArray(a) && !(a instanceof Date); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any export function json2xpath(json: any, index: number, str: Array): string[] { - if (!isObject(json)) { + if (!isPlainObject(json)) { return str; } const keys = Object.keys(json); @@ -31,7 +31,7 @@ export function json2xpath(json: any, index: number, str: Array): string str.splice(index - 3, 0, ns + ":"); index++; continue; - } else if (json[key] && !isObject(json[key])) { + } else if (json[key] && !isPlainObject(json[key])) { // if next child is not an object, final leaf with value const val = json[key]; if (j === 0) { @@ -64,8 +64,8 @@ export function xpath2json(xpath: string, namespaces: Record): R if (sub === "") { continue; } - let rootNamespace: string = null; - let key: string = null; + let rootNamespace: string | null = null; + let key: string | null = null; tmpObj = {}; const reg = /\[(.*?)\]/g; if (sub.replace(reg, "").split(":").length > 1 && i === 1) { @@ -81,17 +81,16 @@ export function xpath2json(xpath: string, namespaces: Record): R tmpObj[key] = {}; tmpObj[key].$ = $; // attach all the required namespaces } - - if (sub.match(reg)) { + const values = sub.match(reg); + if (values) { // handle elements with values for leaves - const values = sub.match(reg); sub = sub.replace(/\[[^\]]*\]/g, ""); if (!tmpObj[sub]) { // create the parent tmpObj[sub] = {}; } for (let j = 0; j < values.length; j++) { - let val = values[j]; + let val: string = values[j]; val = val.replace(/[[\]']+/g, ""); key = val.split("=")[0]; val = val.split("=")[1]; @@ -140,7 +139,7 @@ export function xpath2json(xpath: string, namespaces: Record): R } export function addLeaves(xpath: string, payload: unknown): string { - if (!isObject(payload)) { + if (!isPlainObject(payload)) { return xpath; } diff --git a/packages/binding-netconf/tsconfig.json b/packages/binding-netconf/tsconfig.json index e9fa7c062..17fe8a159 100644 --- a/packages/binding-netconf/tsconfig.json +++ b/packages/binding-netconf/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "strict": true, "outDir": "dist", "rootDir": "src" },