From 44ebd1ff9948c5232cd0ad20bffd2fd5373cddaf Mon Sep 17 00:00:00 2001 From: Wisotzky Sven Date: Thu, 13 Jun 2024 14:19:00 +0200 Subject: [PATCH] rel2.1.1 --- .eslintrc.js | 22 ++ .gitignore | 5 +- .vscodeignore | 8 + CHANGELOG.md | 13 + README.md | 52 +++- package.json | 71 ++--- src/extension.ts | 16 +- src/providers/IntentManagerProvider.ts | 364 ++++++++++++------------- 8 files changed, 318 insertions(+), 233 deletions(-) create mode 100644 .eslintrc.js create mode 100644 .vscodeignore diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..2298d4f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,22 @@ +/**@type {import('eslint').Linter.Config} */ +// eslint-disable-next-line no-undef +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint', + ], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + ], + rules: { + 'semi': [2, "always"], + '@typescript-eslint/no-unused-vars': 0, + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/explicit-module-boundary-types': 0, + '@typescript-eslint/no-non-null-assertion': 0, + 'no-inner-declarations': 0, + '@typescript-eslint/no-namespace': 0 + } +}; diff --git a/.gitignore b/.gitignore index cd20cb5..6c1fb44 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ -.vscode +.vscode/ node_modules/ out/ +dist/ *.vsix +package-lock.json +.DS_Store/ diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..504326e --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,8 @@ +.git/** +.vscode/** +src/** +out/** +tsconfig.json +.gitignore +.DS_Store +.eslintrc.js \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 150d75d..c480c43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -116,3 +116,16 @@ Experimental: Known limitations: * Intent decoration is automatically updated by audit/sync methods to reduce server. In some error scenarios the displayed alignment state does not reflect the server state. + +## [2.1.1] + +Fixes: +* Upload intents and views from local machine (drag'n drop, copy/paste) +* Sanitized code for lint conformance +* Optimized build script + +Updates: +* Allow updating `meta-info.json` for signed intent-types. Change was requested to remove label `ArtifactAdmin` + after an intent-type was cloned or a new version have been created. +* New command to upload intents from local system. + diff --git a/README.md b/README.md index 8f6f9e4..6947771 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,55 @@ limited number of intent-types and instances, but it is not designed to work in Extensive logging is available for all communication between this vsCode plugin and NSP IM. Check "OUTPUT" for more details. +## Build VSIX + +Please make sure, you've got the following installed in your environment: + +``` +# npm install -g typescript +# npm install -g @vscode/vsce +``` + +Installation can be validated like this: + +``` +% npm list -g +/usr/local/lib/node_modules/node/lib +├── @vscode/vsce@2.27.0 +└── typescript@5.4.5 +``` + +Before you compile and build the distribution, make sure all depended modules +are installed: + +``` +% npm install . +% npm list +nokia-intent-manager@2.1.1 ~/dev/vscode-intent-manager +├── @types/node@18.19.34 +├── @types/vscode@1.90.0 +├── @typescript-eslint/eslint-plugin@6.21.0 +├── @typescript-eslint/parser@6.21.0 +├── @vscode/codicons@0.0.36 +├── base-64@1.0.0 +├── esbuild@0.21.5 +├── eslint@8.57.0 +├── lodash@4.17.21 +├── node-fetch@2.7.0 +├── nunjucks@3.2.4 +├── typescript@5.4.5 +├── vscode-uri@3.0.8 +├── vse@0.5.1 +└── yaml@2.4.5 +``` + +To see all dependencies, you can run `npm list --all`. +In cases of any issues, visit the `npm doctor`. + +To compile and generate the VSIX for installation, run: + + vsce package + ## Extension Settings To make the extension work, make sure you configure the following attributes in the extension settings: @@ -66,6 +115,7 @@ See release changes in Changelog. ## Important links -* [Developer portal](https://network.developer.nokia.com/learn/23_4/network-programmability-automation-frameworks) +### NOKIA | Network Developer Portal +* [NSP Programmable Automation Frameworks](https://network.developer.nokia.com/learn/24_4/network-programmability-automation-frameworks) **Enjoy!** diff --git a/package.json b/package.json index fbaf1db..5df17eb 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "nokia-intent-manager", "displayName": "NOKIA_IM", "description": "NOKIA IM vsCode Developer Plugin", - "version": "2.1.0", + "version": "2.1.1", "icon": "media/NSP_Logo.png", "publisher": "Nokia", "repository": "http://github.com/nokia/vscode-intent-manager", @@ -16,7 +16,7 @@ "onFileSystem:im", "onStartupFinished" ], - "main": "./out/main.js", + "main": "./dist/main.js", "contributes": { "snippets": [ { @@ -62,11 +62,17 @@ "icon": "$(home)" }, { - "command": "nokia-intent-manager.uploadLocal", + "command": "nokia-intent-manager.uploadIntentType", "category": "IntentManager", "title": "Upload intent-type", "icon": "$(cloud-upload)" }, + { + "command": "nokia-intent-manager.uploadIntents", + "category": "IntentManager", + "title": "Upload intent(s)", + "icon": "$(cloud-upload)" + }, { "command": "workbench.files.action.compareWithSaved", "category": "IntentManager", @@ -99,7 +105,7 @@ "editor/title": [ { "when": "resourceScheme != im && resourceFilename == meta-info.json || resourceFilename == script-content.js || resourceFilename == script-content.mjs", - "command": "nokia-intent-manager.uploadLocal", + "command": "nokia-intent-manager.uploadIntentType", "group": "navigation" }, { @@ -140,10 +146,15 @@ ], "explorer/context": [ { - "command": "nokia-intent-manager.uploadLocal", + "command": "nokia-intent-manager.uploadIntentType", "group": "nokia_im", "when": "resource =~ /^file:.*\\/intent\\-[a-z][a-z0-9_\\-]*\\-v\\d+/ || resource =~ /^file:.*\\/[a-z][a-z0-9_\\-]*_v\\d+/" }, + { + "command": "nokia-intent-manager.uploadIntents", + "group": "nokia_im", + "when": "resource =~ /^file:.*\\/[a-z][a-z0-9_\\-]*_v\\d+\\/intents\\/.+\\.json/" + }, { "command": "nokia-intent-manager.newIntentType", "group": "nokia_im", @@ -251,47 +262,39 @@ "scope": "application", "default": false, "description": "Enable parallel intent operations (EXPERIMENTAL)" - } + } } } }, "scripts": { - "vscode:prepublish": "npm run esbuild-base -- --minify", - "build": "./node_modules/typescript/bin/tsc", - "compile": "./node_modules/typescript/bin/tsc -p ./", - "watch": "./node_modules/typescript/bin/tsc -watch -p ./", - "pretest": "npm run compile && npm run lint", - "lint": "eslint src --ext ts", - "test": "node ./out/test/runTest.js", - "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --packages=external", - "esbuild": "npm run esbuild-base -- --sourcemap", + "vscode:prepublish": "npm run esbuild -- --minify", + "compile": "tsc -p ./ --sourceMap", + "lint": "eslint \"src/**/*.ts\"", + "clean": "rm -rf node_modules out", + "watch": "tsc -watch -p ./", + "build": "tsc", + "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=dist/main.js --external:vscode --format=cjs --platform=node --packages=external", "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch", - "test-compile": "./node_modules/typescript/bin/tsc -p ./" + "esbuild": "npm run esbuild-base -- --sourcemap" }, "devDependencies": { - "@types/glob": "^8.0.0", - "@types/mocha": "^10.0.1", - "@types/node": "16.x", + "@types/node": "18.x", "@types/vscode": "^1.75.0", - "@typescript-eslint/eslint-plugin": "^5.45.0", - "@typescript-eslint/parser": "^5.45.0", - "@vscode/test-electron": "^2.2.0", - "eslint": "^8.28.0", - "glob": "^8.0.3", - "mocha": "^10.1.0", - "typescript": "^4.9.3" + "@typescript-eslint/eslint-plugin": "^6.7.0", + "@typescript-eslint/parser": "^6.7.0", + "esbuild": "^0.21.5", + "eslint": "^8.26.0", + "typescript": "5.x" }, "dependencies": { - "@vscode/codicons": "0.0.20", - "@vscode/vsce": "^2.25.0", + "@vscode/codicons": "^0.0.36", "base-64": "^1.0.0", - "esbuild": "^0.20.2", - "lodash": "^4.17.0", - "node-fetch": "2.6.6", + "lodash": "^4.17.21", + "node-fetch": "^2.7.0", + "npm": "^10.8.1", "nunjucks": "^3.2.0", - "vsce": "^2.15.0", - "vscode-uri": "^3.0.0", + "vscode-uri": "^3.0.8", "vse": "^0.5.1", - "yaml": "^2.2.0" + "yaml": "^2.4.5" } } diff --git a/src/extension.ts b/src/extension.ts index 790135e..3191521 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -23,10 +23,11 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.openInBrowser', async (...args) => imProvider.openInBrowser(args))); context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.newIntent', async (...args) => imProvider.newIntent(args))); - context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.uploadLocal', async (...args) => imProvider.uploadLocal(args))); - context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.clone', async (...args) => imProvider.clone(args))); - context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.newVersion', async (...args) => imProvider.newVersion(args))); - context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.newIntentType', async (...args) => imProvider.newIntentTypeFromTemplate(args))); + context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.uploadIntentType', async (...args) => imProvider.uploadIntentType(args))); + context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.uploadIntents', async (...args) => imProvider.uploadIntents(args))); + context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.clone', async (...args) => imProvider.clone(args))); + context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.newVersion', async (...args) => imProvider.newVersion(args))); + context.subscriptions.push(vscode.commands.registerCommand('nokia-intent-manager.newIntentType', async (...args) => imProvider.newIntentTypeFromTemplate(args))); vscode.commands.registerCommand('nokia-intent-manager.setPassword', async () => { const passwordInput: string = await vscode.window.showInputBox({password: true, title: "Password"}) ?? ''; @@ -36,7 +37,7 @@ export function activate(context: vscode.ExtensionContext) { function updateStatusBarItem(){ const editor = vscode.window.activeTextEditor; - let sbar = imProvider.getStatusBarItem(); + const sbar = imProvider.getStatusBarItem(); if (editor) { const document = editor.document; @@ -67,10 +68,9 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(imProvider.getStatusBarItem()); context.subscriptions.push(vscode.languages.registerCodeLensProvider({scheme: 'im'}, imProvider)); - let fileAssociations : {[key: string]: string} = vscode.workspace.getConfiguration('files').get('associations') || {}; + const fileAssociations : {[key: string]: string} = vscode.workspace.getConfiguration('files').get('associations') || {}; fileAssociations["/*_v*/views/*"] = "json"; - fileAssociations["/*_v*/intents/*"] = "json"; vscode.workspace.getConfiguration('files').update('associations', fileAssociations); - + vscode.workspace.updateWorkspaceFolders(vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders.length : 0, null, { uri: vscode.Uri.parse('im:/'), name: "Intent Manager" }); } \ No newline at end of file diff --git a/src/providers/IntentManagerProvider.ts b/src/providers/IntentManagerProvider.ts index 60dd5ea..6abab50 100644 --- a/src/providers/IntentManagerProvider.ts +++ b/src/providers/IntentManagerProvider.ts @@ -1,4 +1,12 @@ import * as vscode from 'vscode'; +import * as fs from 'fs'; + +// @ts-expect-error module node-fetch does not have a declaration file +import fetch = require('node-fetch'); +// @ts-expect-error module node-fetch does not have a declaration file +import base64 = require('base-64'); +// @ts-expect-error module nunjucks does not have a declaration file +import nunjucks = require('nunjucks'); const DECORATION_WORKSPACE: vscode.FileDecoration = new vscode.FileDecoration( '', 'Intent Manager Plugin', new vscode.ThemeColor('list.focusForeground') @@ -36,8 +44,7 @@ const DECORATION_VIEWS: vscode.FileDecoration = new vscode.FileDecoration( '', 'UI Customization', new vscode.ThemeColor('list.highlightForeground') ); -let myStatusBarItem: vscode.StatusBarItem; -myStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); +const myStatusBarItem: vscode.StatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); myStatusBarItem.command = 'nokia-intent-manager.intentStatus'; /* @@ -71,12 +78,12 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. signed: boolean; timestamp: number; data: {[key: string]: any}; - intents: {[target: string]: Object}; + intents: {[target: string]: object}; desired: {[target: string]: string}; // intent desired state: active, suspend, delete, saved, planned, deployed aligned: {[target: string]: boolean}; // intent aligned or misaligned views: {[filename: string]: string}; } - } + }; public onDidChangeFileDecorations: vscode.Event; private _eventEmiter: vscode.EventEmitter; @@ -151,9 +158,6 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. this.pluginLogs.warn("No valid auth-token; Getting a new one..."); process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; - const fetch = require('node-fetch'); - const base64 = require('base-64'); - // for getting the auth-token, we are using a reduced timeout of 10sec const timeout = new AbortController(); setTimeout(() => timeout.abort(), 10000); @@ -203,9 +207,6 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. this.pluginLogs.debug("_revokeAuthToken("+token+")"); this.authToken = undefined; - const fetch = require('node-fetch'); - const base64 = require('base-64'); - const url = "https://"+this.nspAddr+"/rest-gateway/rest/api/v1/auth/revocation"; fetch(url, { method: "POST", @@ -226,17 +227,15 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. * Common error handling and logging is centralized here. * * @param {string} url API endpoint for http request - * @param {any} options HTTP method, header, body + * @param {{method: string, body: string, headers: object, signal: AbortSignal}} options HTTP method, header, body */ - private async _callNSP(url:string, options:any): Promise { - const fetch = require('node-fetch'); - + private async _callNSP(url:string, options:{method: string, body?: string, headers?: object, signal?: AbortSignal}): Promise { const timeout = new AbortController(); setTimeout(() => timeout.abort(), this.timeout); - options['signal'] = timeout.signal; + options.signal = timeout.signal; - if (!options.hasOwnProperty("headers")) { + if (!('headers' in options)) { await this._getAuthToken(); const token = await this.authToken; if (!token) @@ -247,13 +246,13 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. "Content-Type": "application/yang-data+json", "Accept": "application/yang-data+json", "Authorization": "Bearer " + token - } + }; else options.headers = { 'Content-Type': "application/json", 'Accept': "application/json", "Authorization": "Bearer " + token - } + }; } if (!url.startsWith('https://')) { @@ -271,15 +270,12 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } const startTS = Date.now(); - let response: any = new Promise((resolve, reject) => { - fetch(url, options).then((response:any) => { + const response: any = new Promise((resolve, reject) => { + fetch(url, options).then((response:any) => { response.clone().text().then((body:string) => { const duration = Date.now()-startTS; - if ("body" in options) - this.pluginLogs.info(options.method, url, options.body, "finished within", duration, "ms"); - else - this.pluginLogs.info(options.method, url, "finished within", duration, "ms"); + this.pluginLogs.info(options.method, url, options.body??"", "finished within", duration, "ms"); if (response.status >= 400) this.pluginLogs.warn("NSP response:", response.status, body); @@ -295,7 +291,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. }) .catch((error:any) => { const duration = Date.now()-startTS; - var errmsg = options.method+" "+url+" failed with "+error.message+" after "+duration.toString()+"ms!"; + let errmsg = options.method+" "+url+" failed with "+error.message+" after "+duration.toString()+"ms!"; if (error.message.includes("user aborted")) errmsg = "No response for "+options.method+" "+url+". Call terminated after "+duration.toString()+"ms."; @@ -353,13 +349,13 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. this.pluginLogs.info("Requesting NSP version"); const url = "https://"+this.nspAddr+"/internal/shared-app-banner-utils/rest/api/v1/appBannerUtils/release-version"; - let response: any = await this._callNSP(url, {method: "GET"}); + const response: any = await this._callNSP(url, {method: "GET"}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) this._raiseRestconfError("Getting NSP release failed!", await response.json()); - let json = await response.json(); + const json = await response.json(); const version = json["response"]["data"]["nspOSVersion"]; this.nspVersion = version.match(/\d+\.\d+\.\d+/)[0].split('.').map(parseInt); @@ -422,23 +418,22 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ private async _auditReport(intent_type: string, target: string, report: {[key: string]: any}): Promise { - var nunjucks = require("nunjucks"); - nunjucks.configure(vscode.Uri.joinPath(this.extensionUri, 'media').fsPath); + const j2media = nunjucks.configure(vscode.Uri.joinPath(this.extensionUri, 'media').fsPath); - if (report.hasOwnProperty("misaligned-attribute")) - for (let object of report["misaligned-attribute"]) + if ('misaligned-attribute' in report) + for (const object of report["misaligned-attribute"]) object["name"] = this._modelPathHTML(object["name"]); - if (report.hasOwnProperty("misaligned-object")) - for (let object of report["misaligned-object"]) + if ('misaligned-object' in report) + for (const object of report["misaligned-object"]) object["object-id"] = this._modelPathHTML(object["object-id"]); - if (report.hasOwnProperty("undesired-object")) - for (let object of report["undesired-object"]) + if ('undesired-object' in report) + for (const object of report["undesired-object"]) object["object-id"] = this._modelPathHTML(object["object-id"]); const panel = vscode.window.createWebviewPanel('auditReport', 'Audit '+intent_type+'/'+target, vscode.ViewColumn.Active, {enableScripts: true}); - panel.webview.html = nunjucks.render('report.html.njk', {intent_type: intent_type, target: target, report: report}); + panel.webview.html = j2media.render('report.html.njk', {intent_type: intent_type, target: target, report: report}); this.pluginLogs.info(panel.webview.html); } @@ -510,14 +505,14 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const url = "/restconf/operations/ibn-administration:search-intent-types"; const body = {"ibn-administration:input": {"page-number": 0, "page-size": 1000}}; - let response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) this._raiseRestconfError("Getting list of intent-types failed!", await response.json()); - let json = await response.json(); + const json = await response.json(); if (json["ibn-administration:output"]["total-count"]>1000) { vscode.window.showWarningMessage("NSP has more than 1000 intent-types. Only showing the first 1000."); } @@ -532,7 +527,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. // Create missing intentType entries in cache for (const entry of intentTypes) { const intent_type_folder = entry.name+"_v"+entry.version; - if (!this.intentTypes.hasOwnProperty(intent_type_folder)) + if (!(intent_type_folder in this.intentTypes)) this.intentTypes[intent_type_folder] = { signed: entry.label.includes('ArtifactAdmin'), timestamp: Date.now(), // We don't have the real timestamp yet! @@ -541,7 +536,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. aligned: {}, desired: {}, views: {} - } + }; } } else { const parts = path.split('/').map(decodeURIComponent); @@ -555,16 +550,16 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const url = "/restconf/data/ibn-administration:ibn-administration/intent-type-catalog/intent-type="+intent_type+","+intent_type_version; - let response: any = await this._callNSP(url, {method: "GET"}); + const response: any = await this._callNSP(url, {method: "GET"}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) this._raiseRestconfError("Getting intent-type details failed!", await response.json()); - let json = await response.json(); - let data = json["ibn-administration:intent-type"]; + const json = await response.json(); + const data = json["ibn-administration:intent-type"]; - if (this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (intent_type_folder in this.intentTypes) { // update intent-type cache entry with data and timestamp this.intentTypes[intent_type_folder].data = data; this.intentTypes[intent_type_folder].timestamp = Date.parse(data.date); @@ -608,19 +603,19 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } }; - let response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) this._raiseRestconfError("Getting list of intents failed!", await response.json()); - let json = await response.json(); + const json = await response.json(); const output = json["ibn:output"]; if (output["total-count"]>1000) vscode.window.showWarningMessage("More than 1000 intents found. Only showing the first 1000."); - if (output.intents.hasOwnProperty('intent')) { + if ('intent' in output.intents) { this.intentTypes[intent_type_folder].intents = output.intents.intent.reduce((intents: any, item: { [key: string]: any; target: string; }) => { return {...intents, [item.target]: item["intent-specific-data"]}; }, {}); @@ -653,13 +648,13 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. // the selected intent-type/version const url = "/restconf/data/nsp-intent-type-config-store:intent-type-config/intent-type-configs="+intent_type+","+intent_type_version; - let response: any = await this._callNSP(url, {method: "GET"}); + const response: any = await this._callNSP(url, {method: "GET"}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) this._raiseRestconfError("Getting list of views failed!", await response.json()); - let json = await response.json(); + const json = await response.json(); this.intentTypes[intent_type_folder].views={}; for (const view of json["nsp-intent-type-config-store:intent-type-configs"][0]["views"]) { @@ -674,8 +669,8 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. else if (parts[2]==='intent-type-resources') { const intentTypeData = this.intentTypes[intent_type_folder].data; - if (intentTypeData.hasOwnProperty('resource')) { - let folders = new Set(); + if ('resource' in intentTypeData) { + const folders = new Set(); const prefix = parts.slice(3).join("/")+'/'; for (const resource of intentTypeData.resource) @@ -685,7 +680,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. if (relparts.length===1) result.push([relparts[0], vscode.FileType.File]); else - folders.add(relparts[0]) + folders.add(relparts[0]); } for (const folder of folders) @@ -716,7 +711,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. async readFile(uri: vscode.Uri): Promise { const path = uri.toString(); const parts = path.split('/').map(decodeURIComponent); - const pattern = /^([a-z][a-z0-9_\-]+)_v\d+$/; + const pattern = /^([a-z][a-z0-9_-]+)_v\d+$/; if (pattern.test(parts[1])) { const intent_type_folder = parts[1]; @@ -724,15 +719,15 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const intent_type = intent_type_folder.split('_v')[0]; this.pluginLogs.debug("readFile("+path+")"); - if (!this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (!(intent_type_folder in this.intentTypes)) { this.pluginLogs.info("Intent-type",intent_type_folder, "not yet(?) in cache! Calling readDirectory(im:/) to populate/update cache."); await this.readDirectory(vscode.Uri.parse('im:/')); } - if (this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (intent_type_folder in this.intentTypes) { if (parts[2]==="meta-info.json") { // meta is deep-copy of cache-entry - let meta = JSON.parse(JSON.stringify(this.intentTypes[intent_type_folder].data)); + const meta = JSON.parse(JSON.stringify(this.intentTypes[intent_type_folder].data)); meta["intent-type"] = intent_type; meta["version"] = intent_type_version; @@ -762,7 +757,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. if (parts[2]==="intents") { const target = decodeURIComponent(parts[3].slice(0,-5)); - if (this.intentTypes[intent_type_folder].intents.hasOwnProperty(target)) + if (target in this.intentTypes[intent_type_folder].intents) return Buffer.from(JSON.stringify(this.intentTypes[intent_type_folder].intents[target], null, ' ')); } @@ -786,7 +781,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. async stat(uri: vscode.Uri): Promise { const path = uri.toString(); const parts = path.split('/').map(decodeURIComponent); - const pattern = /^([a-z][a-z0-9_\-]+)_v\d+$/; + const pattern = /^([a-z][a-z0-9_-]+)_v\d+$/; if (path==="im:/") { this.pluginLogs.debug("stat(im:/)"); @@ -797,17 +792,17 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. this.pluginLogs.debug("stat("+path+")"); const intent_type_folder = parts[1]; - if (!this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (!(intent_type_folder in this.intentTypes)) { this.pluginLogs.info("Intent-type", intent_type_folder, "not yet(?) in cache! Calling readDirectory(im:/) to populate/update cache."); await this.readDirectory(vscode.Uri.parse('im:/')); } - if (this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (intent_type_folder in this.intentTypes) { const timestamp = this.intentTypes[intent_type_folder].timestamp; const access = this.intentTypes[intent_type_folder].signed? vscode.FilePermission.Readonly : undefined; if (parts.length===2) - return { type: vscode.FileType.Directory, ctime: 0, mtime: timestamp, size: 0 } + return { type: vscode.FileType.Directory, ctime: 0, mtime: timestamp, size: 0 }; if (parts[2]==="yang-modules") { if (parts.length===3) @@ -817,7 +812,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. if (module.name === parts[3]) return {type: vscode.FileType.File, ctime: 0, mtime: timestamp, size: 0, permissions: access}; - this.pluginLogs.warn("Module "+parts[3]+" not found!") + this.pluginLogs.warn("Module "+parts[3]+" not found!"); } if (parts[2]==="intent-type-resources") { @@ -831,14 +826,14 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. if (resource.name.startsWith(resourcename)) return {type: vscode.FileType.Directory, ctime: 0, mtime: timestamp, size: 0, permissions: vscode.FilePermission.Readonly}; } - this.pluginLogs.warn("Resource "+resourcename+" not found!") + this.pluginLogs.warn("Resource "+resourcename+" not found!"); } if (parts[2]==="views") { if (parts.length===3) return {type: vscode.FileType.Directory, ctime: 0, mtime: timestamp, size: 0, permissions: vscode.FilePermission.Readonly}; - if (this.intentTypes[intent_type_folder].views.hasOwnProperty(parts[3])) + if (parts[3] in this.intentTypes[intent_type_folder].views) if (parts[3].endsWith(".viewConfig")) return {type: vscode.FileType.File, ctime: 0, mtime: timestamp, size: 0}; else @@ -850,7 +845,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. else if (parts[2]==="intents") { if (parts.length===3) return {type: vscode.FileType.Directory, ctime: 0, mtime: Date.now(), size: 0, permissions: vscode.FilePermission.Readonly}; - else if (this.intentTypes[intent_type_folder].intents.hasOwnProperty(decodeURIComponent(parts[3].slice(0,-5)))) + else if (decodeURIComponent(parts[3].slice(0,-5)) in this.intentTypes[intent_type_folder].intents) return {type: vscode.FileType.File, ctime: 0, mtime: Date.now(), size: 0}; else this.pluginLogs.warn('Unknown intent', uri.toString()); @@ -877,44 +872,44 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. * * @param {vscode.Uri} uri URI of the file to create/update * @param {Uint8Array} content content of the file - * @param {Object} options allow/enforce to create/overwrite + * @param {object} options allow/enforce to create/overwrite */ async writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): Promise { const path = uri.toString(); const parts = path.split('/').map(decodeURIComponent); - const pattern = /^([a-z][a-z0-9_\-]+)_v\d+$/; + const pattern = /^([a-z][a-z0-9_-]+)_v\d+$/; if (pattern.test(parts[1])) { const intent_type_folder = parts[1]; const intent_type_version = intent_type_folder.split('_v')[1]; const intent_type = intent_type_folder.split('_v')[0]; - var name_changed = false; + let name_changed = false; this.pluginLogs.debug("writeFile("+path+")"); - if (!this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (!(intent_type_folder in this.intentTypes)) { this.pluginLogs.info("Intent-type",intent_type_folder, "not yet(?) in cache! Calling readDirectory(im:/) to populate/update cache."); await this.readDirectory(vscode.Uri.parse('im:/')); } // Note: We can only write files if the intent-type exists and is loaded. - if (this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (intent_type_folder in this.intentTypes) { if (parts[2]==="intents") { if (parts[3].endsWith('.json')) { let target = decodeURIComponent(parts[3].slice(0,-5)); - if (target.endsWith(" copy") && this.intentTypes[intent_type_folder].intents.hasOwnProperty(target.slice(0,-5))) { + if (target.endsWith(" copy") && target.slice(0,-5) in this.intentTypes[intent_type_folder].intents) { name_changed = true; target = target.slice(0,-5); } - if (this.intentTypes[intent_type_folder].intents.hasOwnProperty(target)) { + if (target in this.intentTypes[intent_type_folder].intents) { this.pluginLogs.info("update intent", intent_type, target); const url = "/restconf/data/ibn:ibn/intent="+encodeURIComponent(target)+","+intent_type+"/intent-specific-data"; const body = {"ibn:intent-specific-data": JSON.parse(content.toString())}; - let response: any = await this._callNSP(url, {method: "PUT", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "PUT", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -950,7 +945,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } }; - let response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -984,7 +979,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. }] }] }; - let response: any = await this._callNSP(url, {method: "PATCH", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "PATCH", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -1024,7 +1019,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. else if (parts[2]==="intent-type-resources") { let updated = false; const resourcename = parts.slice(3).join("/"); - for (var resource of data.resource) { + for (const resource of data.resource) { if (resource.name === resourcename) { resource.value=content.toString(); updated = true; @@ -1036,7 +1031,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } else if (parts[2]==="yang-modules") { let updated = false; - for (var module of data.module) { + for (const module of data.module) { if (module.name === parts[3]) { module['yang-content']=content.toString(); updated = true; @@ -1047,12 +1042,12 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } } - let forCleanup = ["default-version"]; + const forCleanup = ["default-version"]; for (const parameter of forCleanup) delete data[parameter]; } - let url = "/restconf/data/ibn-administration:ibn-administration/intent-type-catalog/intent-type="+intent_type+","+intent_type_version; - let response: any = await this._callNSP(url, {method: "PUT", body: JSON.stringify({"ibn-administration:intent-type": data})}); + const url = "/restconf/data/ibn-administration:ibn-administration/intent-type-catalog/intent-type="+intent_type+","+intent_type_version; + const response: any = await this._callNSP(url, {method: "PUT", body: JSON.stringify({"ibn-administration:intent-type": data})}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -1101,7 +1096,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. async delete(uri: vscode.Uri): Promise { const path = uri.toString(); const parts = path.split('/').map(decodeURIComponent); - const pattern = /^([a-z][a-z0-9_\-]+)_v\d+$/; + const pattern = /^([a-z][a-z0-9_-]+)_v\d+$/; if (pattern.test(parts[1])) { const intent_type_folder = parts[1]; @@ -1109,12 +1104,12 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const intent_type = intent_type_folder.split('_v')[0]; this.pluginLogs.debug("delete("+path+")"); - if (!this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (!(intent_type_folder in this.intentTypes)) { this.pluginLogs.info("Intent-type",intent_type_folder, "not yet(?) in cache! Calling readDirectory(im:/) to populate/update cache."); await this.readDirectory(vscode.Uri.parse('im:/')); } - if (this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (intent_type_folder in this.intentTypes) { if (parts.length===3) { throw vscode.FileSystemError.NoPermissions("Deletion of "+path+" is prohibited"); } @@ -1155,7 +1150,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. this.pluginLogs.info("delete intent", intent_type, target); const url = "/restconf/data/ibn:ibn/intent="+encodeURIComponent(target)+","+intent_type; - let response: any = await this._callNSP(url, {method: "DELETE"}); + const response: any = await this._callNSP(url, {method: "DELETE"}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -1168,7 +1163,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } } - let response: any = await this._callNSP(url, {method: "DELETE"}); + const response: any = await this._callNSP(url, {method: "DELETE"}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) @@ -1225,7 +1220,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const path = uri.toString(); const parts = path.split('/').map(decodeURIComponent); - const pattern = /^([a-z][a-z0-9_\-]+)(_v\d+)?$/; + const pattern = /^([a-z][a-z0-9_-]+)(_v\d+)?$/; if (parts.length===2 && pattern.test(parts[1])) { await this.newIntentTypeFromTemplate([uri]); @@ -1245,7 +1240,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. async provideFileDecoration( uri: vscode.Uri): Promise { const path = uri.toString(); const parts = path.split('/').map(decodeURIComponent); - const pattern = /^([a-z][a-z0-9_\-]+)_v\d+$/; + const pattern = /^([a-z][a-z0-9_-]+)_v\d+$/; if (path==="im:/") { this.pluginLogs.debug("provideFileDecoration(im:/)"); @@ -1256,12 +1251,12 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. this.pluginLogs.debug("provideFileDecoration("+path+")"); const intent_type_folder = parts[1]; - if (!this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (!(intent_type_folder in this.intentTypes)) { this.pluginLogs.info("Intent-type",intent_type_folder, "not yet(?) in cache! Calling readDirectory(im:/) to populate/update cache."); await this.readDirectory(vscode.Uri.parse('im:/')); } - if (this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (intent_type_folder in this.intentTypes) { if (parts.length===2) if (this.intentTypes[intent_type_folder].signed) return DECORATION_SIGNED; @@ -1344,7 +1339,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. "saved": "Saved", "planned": "Planned", "deployed": "Deployed" - } + }; const parts = path.split('/').map(decodeURIComponent); const intent_type_folder = parts[1]; @@ -1362,7 +1357,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async setState(args:any[]): Promise { - let uriList:vscode.Uri[] = this._getUriList(args); + const uriList:vscode.Uri[] = this._getUriList(args); const states: {[value: string]: string} = { "Active": "active", @@ -1371,14 +1366,14 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. "Saved": "saved", "Planned": "planned", "Deployed": "deployed" - } + }; - var actual = new Set(); + const actual = new Set(); for (const entry of uriList) { actual.add(this.getState(entry)); } - let items = []; + const items = []; for (const state of Object.keys(states)) if (actual.has(state)) items.push({label:state, description:"✔"}); @@ -1404,7 +1399,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. if (this.intentTypes[intent_type_folder].desired[target] !== state) { const url = "/restconf/data/ibn:ibn/intent="+encodeURIComponent(target)+","+intent_type; - let response: any = await this._callNSP(url, {method: "PATCH", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "PATCH", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -1415,7 +1410,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } await vscode.commands.executeCommand("nokia-intent-manager.updateStatusBar"); } - }) + }); } /** @@ -1440,14 +1435,12 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async uploadIntentType(args:any[]): Promise { - var fs = require('fs'); - - const matchIntentType = /^([a-z][a-z0-9_\-]+_v\d+)$/; - const matchImportedIntentType = /^intent\-([a-z][a-z0-9_\-]+\-v\d+)$/; + const matchIntentType = /^([a-z][a-z0-9_-]+_v\d+)$/; + const matchImportedIntentType = /^intent-([a-z][a-z0-9_-]+-v\d+)$/; const uri:vscode.Uri = this._getUriList(args)[0]; const allparts = uri.toString().split('/'); - let parts:string[] = [] + const parts:string[] = []; for (const part of allparts) { parts.push(part); if (matchIntentType.test(part) || matchImportedIntentType.test(part)) @@ -1485,7 +1478,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. throw vscode.FileSystemError.FileNotFound("script-content not found"); } - let modules:string[] = []; + const modules:string[] = []; if (fs.existsSync(vscode.Uri.joinPath(path, "yang-modules").fsPath)) { fs.readdirSync(vscode.Uri.joinPath(path, "yang-modules").fsPath).forEach((filename: string) => { if (fs.lstatSync(vscode.Uri.joinPath(path, "yang-modules", filename).fsPath).isFile() && !filename.startsWith('.')) modules.push(filename); @@ -1496,8 +1489,9 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. throw vscode.FileSystemError.FileNotFound("YANG modules not found"); } - let resources:string[] = []; + const resources:string[] = []; if (fs.existsSync(vscode.Uri.joinPath(path, "intent-type-resources").fsPath)) { + // @ts-expect-error fs.readdirSync() returns string[] for utf-8 encoding (default) fs.readdirSync(vscode.Uri.joinPath(path, "intent-type-resources").fsPath, {recursive: true}).forEach((filename: string) => { if (fs.lstatSync(vscode.Uri.joinPath(path, "intent-type-resources", filename).fsPath).isFile() && !filename.startsWith('.') && !filename.includes('/.')) resources.push(filename); @@ -1506,7 +1500,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } else vscode.window.showWarningMessage("Intent-type has no resources"); - let views:string[] = []; + const views:string[] = []; if (fs.existsSync(vscode.Uri.joinPath(path, "views").fsPath)) { fs.readdirSync(vscode.Uri.joinPath(path, "views").fsPath).forEach((filename: string) => { if (filename.endsWith(".viewConfig")) views.push(filename); @@ -1514,10 +1508,10 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. this.pluginLogs.info("views: " + JSON.stringify(views)); } - let intents:string[] = []; + const intents:string[] = []; if (fs.existsSync(vscode.Uri.joinPath(path, "intents").fsPath)) { fs.readdirSync(vscode.Uri.joinPath(path, "intents").fsPath).forEach((filename: string) => { - if (filename.endsWith(".json")) intents.push(filename) + if (filename.endsWith(".json")) intents.push(filename); }); this.pluginLogs.info("intents: " + JSON.stringify(intents)); } @@ -1530,7 +1524,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const intent_type = intent_type_folder.split('_v')[0]; const intent_type_version = intent_type_folder.split('_v')[1]; - if (meta.hasOwnProperty("intent-type") && meta.hasOwnProperty("version") && intent_type_folder!==meta["intent-type"]+"_v"+meta.version) + if ('intent-type' in meta && 'version' in meta && intent_type_folder!==meta["intent-type"]+"_v"+meta.version) vscode.window.showWarningMessage("Mismatch with meta-info: "+meta["intent-type"]+"_v"+meta.version+"! Uploading under: "+intent_type_folder); delete meta["intent-type"]; @@ -1538,10 +1532,10 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. meta.version = parseInt(intent_type_version); // IBN expects targetted-device to contain an index as key, which is not contained in exported intent-types (ZIP) - if (meta.hasOwnProperty("targetted-device")) { + if ('targetted-device' in meta) { let index=0; for (const entry of meta["targetted-device"]) { - if (!entry.hasOwnProperty("index")) + if (!('index' in entry)) entry.index = index; index+=1; } @@ -1569,12 +1563,12 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. if ('custom-field' in meta) meta["custom-field"] = JSON.stringify(meta["custom-field"]); - if (this.intentTypes.hasOwnProperty(intent_type_folder)) { + if (intent_type_folder in this.intentTypes) { const body = {"ibn-administration:intent-type": meta}; const url = "/restconf/data/ibn-administration:ibn-administration/intent-type-catalog/intent-type="+intent_type+","+intent_type_version; this.pluginLogs.info("update intent-type", intent_type); - let response: any = await this._callNSP(url, {method: "PUT", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "PUT", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) @@ -1588,7 +1582,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const url = "/restconf/data/ibn-administration:ibn-administration/intent-type-catalog"; this.pluginLogs.info("create intent-type", intent_type); - let response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) @@ -1603,7 +1597,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. aligned: {}, desired: {}, views: {} - } + }; } vscode.window.showInformationMessage("Intent-Type "+intent_type_folder+" successfully uploaded"); @@ -1631,7 +1625,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. }] }; this.pluginLogs.info("upload view ", intent_type, viewname); - let response: any = await this._callNSP(url, {method: "PATCH", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "PATCH", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -1646,11 +1640,11 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const target = decodeURIComponent(filename.slice(0,-5)); const content = fs.readFileSync(vscode.Uri.joinPath(path, "intents", filename).fsPath, {encoding:'utf8', flag:'r'}); - if (this.intentTypes[intent_type_folder].intents.hasOwnProperty(target)) { + if (target in this.intentTypes[intent_type_folder].intents) { const url = "/restconf/data/ibn:ibn/intent="+encodeURIComponent(target)+","+intent_type+"/intent-specific-data"; const body = {"ibn:intent-specific-data": JSON.parse(content)}; this.pluginLogs.info("update intent", intent_type, target); - let response: any = await this._callNSP(url, {method: "PUT", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "PUT", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -1671,7 +1665,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } }; this.pluginLogs.info("create intent", intent_type, target); - let response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -1694,13 +1688,11 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async uploadIntents(args:any[]): Promise { - var fs = require('fs'); - - const matchIntentType = /^([a-z][a-z0-9_\-]+_v\d+)$/; + const matchIntentType = /^([a-z][a-z0-9_-]+_v\d+)$/; - let uriList:vscode.Uri[] = this._getUriList(args); + const uriList:vscode.Uri[] = this._getUriList(args); for (const entry of uriList) { - let parts = entry.toString().split('/'); + const parts = entry.toString().split('/'); const filename = parts.pop(); const intent_folder = parts.pop(); const intent_type_folder = parts.pop(); @@ -1711,11 +1703,11 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const target = decodeURIComponent(decodeURIComponent(filename.slice(0,-5))); const content = fs.readFileSync(entry.fsPath, {encoding:'utf8', flag:'r'}); - if (this.intentTypes[intent_type_folder].intents.hasOwnProperty(target)) { + if (target in this.intentTypes[intent_type_folder].intents) { const url = "/restconf/data/ibn:ibn/intent="+encodeURIComponent(target)+","+intent_type+"/intent-specific-data"; const body = {"ibn:intent-specific-data": JSON.parse(content)}; this.pluginLogs.info("update intent", intent_type, target); - let response: any = await this._callNSP(url, {method: "PUT", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "PUT", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -1736,7 +1728,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } }; this.pluginLogs.info("create intent", intent_type, target); - let response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -1778,13 +1770,13 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. public async logs(args:any[]): Promise { this.pluginLogs.debug("logs()"); - let query : {[key: string]: any} = {"bool": {"must": [ + const query : {[key: string]: any} = {"bool": {"must": [ {"range": {"@datetime": {"gte": "now-10m"}}}, {"match_phrase": {"log": "\"category\":\"com.nokia.fnms.controller.ibn.impl.ScriptedEngine\""}}, {"bool": {"should": []}} ]}}; - let uriList:vscode.Uri[] = this._getUriList(args); + const uriList:vscode.Uri[] = this._getUriList(args); for (const entry of uriList) { const parts = entry.toString().split('/').map(decodeURIComponent); @@ -1794,7 +1786,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const intent_type_version = intent_type_folder.split('_v')[1]; const intent_type = intent_type_folder.split('_v')[0]; - let qentry = {"bool": {"must": [ + const qentry = {"bool": {"must": [ {"match_phrase": {"log": "\"intent_type\":\""+intent_type+"\""}}, {"match_phrase": {"log": "\"intent_type_version\":\""+intent_type_version+"\""}} ]}}; @@ -1819,7 +1811,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. let osdver = "2.6.0"; if (this._fromRelease(23,11)) osdver="2.10.0"; - let response: any = await this._callNSP(url, { + const response: any = await this._callNSP(url, { method: "POST", headers: { "Content-Type": "application/json", @@ -1834,21 +1826,19 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) this._raiseRestconfError("Getting logs failed!", await response.json(), true); - let json: any = await response.json(); - - // let data: Array = json["hits"]["hits"]; - let data : {[key: string]: any}[] = json["hits"]["hits"]; + const json: any = await response.json(); + const data : {[key: string]: any}[] = json["hits"]["hits"]; if (data.length === 0 ) { vscode.window.showWarningMessage("No intent operation logs for the last 10 minutes"); } else { - let logs : Array = []; + const logs : Array = []; for (const entry of data) { logs.push(JSON.parse(entry['_source'].log)); } logs.sort((a,b) => a['date']-b['date']); - this.serverLogs.clear() + this.serverLogs.clear(); this.serverLogs.show(true); let pdate = logs[0]['date']; @@ -1884,7 +1874,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async audit(args:any[]): Promise { - let uriList:vscode.Uri[] = this._getUriList(args); + const uriList:vscode.Uri[] = this._getUriList(args); for (const entry of uriList) { const parts = entry.toString().split('/').map(decodeURIComponent); @@ -1915,17 +1905,17 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. vscode.window.showInformationMessage("Intent "+intent_type+"/"+target+" is aligned!"); } this._eventEmiter.fire(entry); - }) + }); else response.json().then((json:any) => this._printRestconfError("Audit intent failed!", json)); }) .catch((error:any) => { throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); - }) + }); } else { this.pluginLogs.debug("audit(", entry.toString(), ")"); - let response: any = await this._callNSP(url, {method: "POST", body: ""}); + const response: any = await this._callNSP(url, {method: "POST", body: ""}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -1959,7 +1949,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async lastAuditReport(args:any[]): Promise { - let uriList:vscode.Uri[] = this._getUriList(args); + const uriList:vscode.Uri[] = this._getUriList(args); for (const entry of uriList) { this.pluginLogs.debug("lastAuditReport(", entry.toString(), ")"); const parts = entry.toString().split('/').map(decodeURIComponent); @@ -1970,7 +1960,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const intent_type = intent_type_folder.split('_v')[0]; const url = "/restconf/data/ibn:ibn/intent="+encodeURIComponent(target)+","+intent_type; - let response: any = await this._callNSP(url, {method: "GET"}); + const response: any = await this._callNSP(url, {method: "GET"}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) @@ -1996,7 +1986,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async sync(args:any[]): Promise { - let uriList:vscode.Uri[] = this._getUriList(args); + const uriList:vscode.Uri[] = this._getUriList(args); for (const entry of uriList) { const parts = entry.toString().split('/').map(decodeURIComponent); @@ -2006,8 +1996,6 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. const intent_type = intent_type_folder.split('_v')[0]; const url = "/restconf/data/ibn:ibn/intent="+encodeURIComponent(target)+","+intent_type+"/synchronize"; - throw(Error("blabla blabla")); - if (this.parallelOps && uriList.length>1) { this.pluginLogs.warn("parallel sync(", entry.toString(), "), EXPERIMENTAL!"); @@ -2022,11 +2010,11 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. }) .catch((error:any) => { throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); - }) + }); } else { this.pluginLogs.debug("sync(", entry.toString(), ")"); - let response: any = await this._callNSP(url, {method: "POST", body: ""}); + const response: any = await this._callNSP(url, {method: "POST", body: ""}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (response.ok) { @@ -2049,7 +2037,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async openInBrowser(args:any[]): Promise { - let uriList:vscode.Uri[] = this._getUriList(args); + const uriList:vscode.Uri[] = this._getUriList(args); if (uriList.length>0) { const path = uriList[0].toString(); @@ -2092,7 +2080,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async newIntent(args:any[]): Promise { - let uriList:vscode.Uri[] = this._getUriList(args); + const uriList:vscode.Uri[] = this._getUriList(args); if (uriList.length>0) { const path = uriList[0].toString(); @@ -2118,7 +2106,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async newVersion(args:any[]): Promise { - let uriList:vscode.Uri[] = this._getUriList(args); + const uriList:vscode.Uri[] = this._getUriList(args); if (uriList.length>0) { const path = uriList[0].toString(); @@ -2129,8 +2117,8 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. this.pluginLogs.debug("newVersion(", path, ")"); - let url = "/mdt/rest/ibn/save/"+intent_type+"/"+intent_type_version; - let response: any = await this._callNSP(url, {method: "POST", body: "{}"}); + const url = "/mdt/rest/ibn/save/"+intent_type+"/"+intent_type_version; + const response: any = await this._callNSP(url, {method: "POST", body: "{}"}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) @@ -2148,7 +2136,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async clone(args:any[]): Promise { - let uriList:vscode.Uri[] = this._getUriList(args); + const uriList:vscode.Uri[] = this._getUriList(args); if (uriList.length>0) { const path = uriList[0].toString(); @@ -2166,13 +2154,13 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. }); if (new_intent_type) { - if (this.intentTypes.hasOwnProperty(new_intent_type+"_v1")) { + if ((new_intent_type+"_v1") in this.intentTypes) { vscode.window.showErrorMessage("The intent-type "+new_intent_type+" already exists!"); throw vscode.FileSystemError.FileExists("The intent-type "+new_intent_type+" already exists!"); } - let url = "/mdt/rest/ibn/save/"+intent_type+"/"+intent_type_version+"?newIntentTypeName="+new_intent_type; - let response: any = await this._callNSP(url, {method: "POST", body: "{}"}); + const url = "/mdt/rest/ibn/save/"+intent_type+"/"+intent_type_version+"?newIntentTypeName="+new_intent_type; + const response: any = await this._callNSP(url, {method: "POST", body: "{}"}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) @@ -2191,18 +2179,15 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. */ public async newIntentTypeFromTemplate(args:any[]): Promise { - const fs = require('fs'); - var nunjucks = require("nunjucks"); - const path = this._getUriList(args)[0].toString(); const parts = path.split('/').map(decodeURIComponent); - const pattern = /^([a-z][a-z0-9_\-]+)(_v\d+)?$/; + const pattern = /^([a-z][a-z0-9_-]+)(_v\d+)?$/; let intent_type_name = "default"; if (parts.length===2 && pattern.test(parts[1])) intent_type_name = parts[1].replace(/_v\d+$/, ""); - let data:{ + const data:{ intent_type: string|undefined, author:string|undefined, template:string|undefined, @@ -2212,7 +2197,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. author: "NSP DevOps", template: "none", date: new Date().toISOString().slice(0,10) - } + }; // todo: Using multi-step input using QuickPick and InputBox // https://github.com/microsoft/vscode-extension-samples/tree/main/quickinput-sample @@ -2228,7 +2213,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. }); if (!data.intent_type) return; - if (this.intentTypes.hasOwnProperty(data.intent_type+"_v1")) + if ((data.intent_type+"_v1") in this.intentTypes) throw vscode.FileSystemError.FileExists("Intent-type already exists! Use unique intent-type name!"); data.author = await vscode.window.showInputBox({ @@ -2249,11 +2234,10 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. vscode.window.showErrorMessage("meta-info.json not found"); return; } - let meta:{[key:string]: any}; - nunjucks.configure(templatePath.fsPath); - meta = JSON.parse(nunjucks.render('meta-info.json', data)); + const j2root = nunjucks.configure(templatePath.fsPath); + const meta:{[key:string]: any} = JSON.parse(j2root.render('meta-info.json', data)); - if (!meta.hasOwnProperty("mapping-engine")) + if (!('mapping-engine' in meta)) meta["mapping-engine"] = 'js-scripted'; let script: string|undefined; @@ -2273,7 +2257,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. vscode.window.showErrorMessage(script+" not found"); return; } - meta["script-content"] = nunjucks.render(script, data); + meta["script-content"] = j2root.render(script, data); if (!fs.existsSync(vscode.Uri.joinPath(templatePath, "yang-modules").fsPath)) { vscode.window.showErrorMessage("YANG modules not found"); @@ -2285,35 +2269,36 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. } const modulesPath = vscode.Uri.joinPath(templatePath, "yang-modules").fsPath; - nunjucks.configure(modulesPath); + const j2modules = nunjucks.configure(modulesPath); - if (!meta.hasOwnProperty('module')) meta.module=[]; + if (!('module' in meta)) meta.module=[]; fs.readdirSync(modulesPath).forEach((filename: string) => { const fullpath = vscode.Uri.joinPath(templatePath, "yang-modules", filename).fsPath; if (!fs.lstatSync(fullpath).isFile()) - this.pluginLogs.info("ignore "+filename+" (not a file)") + this.pluginLogs.info("ignore "+filename+" (not a file)"); else if (filename.startsWith('.')) - this.pluginLogs.info("ignore hidden file "+filename) + this.pluginLogs.info("ignore hidden file "+filename); else if (filename!="[intent_type].yang") meta.module.push({name: filename, "yang-content": fs.readFileSync(fullpath, {encoding: 'utf8', flag: 'r'})}); else - meta.module.push({name: data.intent_type+".yang", "yang-content": nunjucks.render(filename, data)}); + meta.module.push({name: data.intent_type+".yang", "yang-content": j2modules.render(filename, data)}); }); const resourcesPath = vscode.Uri.joinPath(templatePath, "intent-type-resources").fsPath; - nunjucks.configure(resourcesPath); + const j2resources = nunjucks.configure(resourcesPath); - let resourcefiles:string[] = []; - if (!meta.hasOwnProperty('resource')) meta.resource=[]; + const resourcefiles:string[] = []; + if (!('resource' in meta)) meta.resource=[]; if (fs.existsSync(resourcesPath)) + // @ts-expect-error fs.readdirSync() returns string[] for utf-8 encoding (default) fs.readdirSync(resourcesPath, {recursive: true}).forEach((filename: string) => { const fullpath = vscode.Uri.joinPath(templatePath, "intent-type-resources", filename).fsPath; if (!fs.lstatSync(fullpath).isFile()) - this.pluginLogs.info("ignore "+filename+" (not a file)") + this.pluginLogs.info("ignore "+filename+" (not a file)"); else if (filename.startsWith('.') || filename.includes('/.')) - this.pluginLogs.info("ignore hidden file/folder "+filename) + this.pluginLogs.info("ignore hidden file/folder "+filename); else - meta.resource.push({name: filename, value: nunjucks.render(filename, data)}); + meta.resource.push({name: filename, value: j2resources.render(filename, data)}); resourcefiles.push(filename); }); @@ -2323,18 +2308,19 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. if (fs.existsSync(vscode.Uri.joinPath(templatePath, "merge_common_resources").fsPath)) { const commonsPath = vscode.Uri.joinPath(this.extensionUri, 'templates', 'common-resources'); - nunjucks.configure(commonsPath.fsPath); + const j2resources = nunjucks.configure(commonsPath.fsPath); + // @ts-expect-error fs.readdirSync() returns string[] for utf-8 encoding (default) fs.readdirSync(commonsPath.fsPath, {recursive: true}).forEach((filename: string) => { const fullpath = vscode.Uri.joinPath(commonsPath, filename).fsPath; if (!fs.lstatSync(fullpath).isFile()) - this.pluginLogs.info("ignore "+filename+" (not a file)") + this.pluginLogs.info("ignore "+filename+" (not a file)"); else if (filename.startsWith('.') || filename.includes('/.')) - this.pluginLogs.info("ignore hidden file/folder "+filename) + this.pluginLogs.info("ignore hidden file/folder "+filename); else if (resourcefiles.includes(filename)) - this.pluginLogs.info(filename+" (common) skipped, overwritten in template") + this.pluginLogs.info(filename+" (common) skipped, overwritten in template"); else - meta.resource.push({name: filename, value: nunjucks.render(filename, data)}); + meta.resource.push({name: filename, value: j2resources.render(filename, data)}); }); } @@ -2346,10 +2332,10 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. delete meta["intent-type"]; // IBN expects targetted-device to contain an index as key, which is not contained in exported intent-types (ZIP) - if (meta.hasOwnProperty("targetted-device")) { + if ('targetted-device' in meta) { let index=0; for (const entry of meta["targetted-device"]) { - if (!entry.hasOwnProperty("index")) entry.index = index; + if (!('index' in entry)) entry.index = index; index+=1; } } @@ -2357,7 +2343,7 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. vscode.window.showInformationMessage("Creating new Intent-Type"); const body = {"ibn-administration:intent-type": meta}; const url = "/restconf/data/ibn-administration:ibn-administration/intent-type-catalog"; - let response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); + const response: any = await this._callNSP(url, {method: "POST", body: JSON.stringify(body)}); if (!response) throw vscode.FileSystemError.Unavailable("Lost connection to NSP"); if (!response.ok) @@ -2374,14 +2360,14 @@ export class IntentManagerProvider implements vscode.FileSystemProvider, vscode. // --- SECTION: vscode.CodeLensProvider implementation ---------------- async provideCodeLenses(document: vscode.TextDocument): Promise { - let topOfDocument = new vscode.Range(0, 0, 0, 0); + const topOfDocument = new vscode.Range(0, 0, 0, 0); - let command = { + const command = { title: 'Intent Manager at '+this.nspAddr+' by NOKIA', command: 'nokia-intent-manager.openInBrowser' }; - let codeLens = new vscode.CodeLens(topOfDocument, command); + const codeLens = new vscode.CodeLens(topOfDocument, command); return [codeLens]; }