diff --git a/package-lock.json b/package-lock.json
index 6169de723..d33142a71 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,8 +15,8 @@
"@azure/arm-resources": "^4.2.2",
"@azure/container-registry": "1.0.0-beta.5",
"@azure/ms-rest-js": "^2.2.1",
- "@microsoft/vscode-azext-azureutils": "^0.3.4",
- "@microsoft/vscode-azext-utils": "^0.3.14",
+ "@microsoft/vscode-azext-azureutils": "^0.3.7",
+ "@microsoft/vscode-azext-utils": "^0.4.0",
"dayjs": "^1.11.3",
"dotenv": "^16.0.0",
"open": "^8.0.4",
@@ -25,7 +25,7 @@
},
"devDependencies": {
"@microsoft/eslint-config-azuretools": "^0.1.0",
- "@microsoft/vscode-azext-dev": "^0.1.2",
+ "@microsoft/vscode-azext-dev": "^0.1.5",
"@types/fs-extra": "^8.1.1",
"@types/git-url-parse": "^9.0.0",
"@types/gulp": "^4.0.6",
@@ -43,15 +43,15 @@
"mocha": "^10.1.0",
"mocha-junit-reporter": "^2.0.0",
"mocha-multi-reporters": "^1.1.7",
- "ts-node": "^7.0.1",
- "typescript": "^4.3.5",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.9.4",
"vsce": "^1.87.1",
"vscode-test": "^1.5.2",
"webpack": "^5.28.0",
"webpack-cli": "^4.6.0"
},
"engines": {
- "vscode": "^1.66.0"
+ "vscode": "^1.74.0"
}
},
"node_modules/@azure/abort-controller": {
@@ -590,6 +590,28 @@
"node": ">=4"
}
},
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
@@ -769,9 +791,9 @@
}
},
"node_modules/@microsoft/vscode-azext-azureutils": {
- "version": "0.3.4",
- "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureutils/-/vscode-azext-azureutils-0.3.4.tgz",
- "integrity": "sha512-ABzi4b4UJcD0IuRGCjpJXyp5Z3yvS5Pqki9ZEvZxcowuqfHl6yCRe2oMZ1xrwkMDhuY8ox81eiarLCOSYkju6A==",
+ "version": "0.3.7",
+ "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureutils/-/vscode-azext-azureutils-0.3.7.tgz",
+ "integrity": "sha512-GZVHRWD1V5ACVH3r6/4RvPc5fStoR3dBVn2A9azdvWtODVt+ZasApED8nOfEbMNfbXYdop9iWtSvF9+FMXmu1g==",
"dependencies": {
"@azure/arm-resources": "^5.0.0",
"@azure/arm-resources-profile-2020-09-01-hybrid": "^2.0.0",
@@ -779,7 +801,7 @@
"@azure/arm-storage": "^17.0.0",
"@azure/arm-storage-profile-2020-09-01-hybrid": "^2.0.0",
"@azure/ms-rest-js": "^2.2.1",
- "@microsoft/vscode-azext-utils": "^0.3.3",
+ "@microsoft/vscode-azext-utils": "^0.4.0",
"semver": "^7.3.7",
"vscode-nls": "^5.0.1"
}
@@ -807,14 +829,14 @@
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/@microsoft/vscode-azext-azureutils/node_modules/vscode-nls": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.1.tgz",
- "integrity": "sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A=="
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz",
+ "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng=="
},
"node_modules/@microsoft/vscode-azext-dev": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-dev/-/vscode-azext-dev-0.1.2.tgz",
- "integrity": "sha512-6uZDiaNiW5TGcyO5RgmMWvzE3ozNuCW+eoKPKP7GCBMm2oM7AgVcaDOxeVUfeps1IAhvo9S0j/Mu7hbgSkQ7TA==",
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-dev/-/vscode-azext-dev-0.1.5.tgz",
+ "integrity": "sha512-HpDVKmqL/hRWVtMKpVfiW1ZKkhR4WmkauFA8Vo/vzUNpfSrWX543bAhCVLnCO22/5FjLudsxm3V8sgN0tKMuJA==",
"dev": true,
"dependencies": {
"@azure/arm-subscriptions": "^2.0.0",
@@ -825,7 +847,7 @@
"copy-webpack-plugin": "^6.0.0",
"fs-extra": "^8.0.0",
"terser-webpack-plugin": "^5.0.0",
- "ts-loader": "^5.3.3",
+ "ts-loader": "^9.4.2",
"webpack": "5.28.0"
},
"bin": {
@@ -996,34 +1018,12 @@
"node": ">=0.4.0"
}
},
- "node_modules/@microsoft/vscode-azext-dev/node_modules/enhanced-resolve": {
- "version": "5.9.2",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
- "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
- "dev": true,
- "dependencies": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
"node_modules/@microsoft/vscode-azext-dev/node_modules/es-module-lexer": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz",
"integrity": "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==",
"dev": true
},
- "node_modules/@microsoft/vscode-azext-dev/node_modules/tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@microsoft/vscode-azext-dev/node_modules/webpack": {
"version": "5.28.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.28.0.tgz",
@@ -1084,9 +1084,9 @@
}
},
"node_modules/@microsoft/vscode-azext-utils": {
- "version": "0.3.14",
- "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-utils/-/vscode-azext-utils-0.3.14.tgz",
- "integrity": "sha512-ic9pzUA3hpP8NLnlMYn3ebrz33w27AuFxJmTY5t7Z7PY026/A/+T4SPTBaWsyMRnE2KLbr5iti6ByMhhCm0MMw==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-utils/-/vscode-azext-utils-0.4.0.tgz",
+ "integrity": "sha512-1mnYfVah6pULYXExR7nkDqFdU1oA8bddtWlPANcWatSjB5qZ4qKFWrnkR3DqGvVItPptiNzKHYRmNBwJ+NAG9g==",
"dependencies": {
"@vscode/extension-telemetry": "^0.6.2",
"dayjs": "^1.11.2",
@@ -1212,6 +1212,30 @@
"node": ">= 6"
}
},
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+ "dev": true
+ },
"node_modules/@types/eslint": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz",
@@ -1332,12 +1356,6 @@
"integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
"dev": true
},
- "node_modules/@types/tapable": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
- "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==",
- "dev": true
- },
"node_modules/@types/uglify-js": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz",
@@ -1425,6 +1443,12 @@
"node": ">= 8"
}
},
+ "node_modules/@types/webpack/node_modules/@types/tapable": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
+ "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==",
+ "dev": true
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "4.33.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz",
@@ -1847,6 +1871,15 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
+ "node_modules/acorn-walk": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/adal-node": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.2.3.tgz",
@@ -2047,6 +2080,12 @@
"readable-stream": "^2.0.6"
}
},
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
"node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -2245,15 +2284,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/arrify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
- "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/assign-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
@@ -3303,6 +3333,12 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true
},
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3798,17 +3834,16 @@
}
},
"node_modules/enhanced-resolve": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
- "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
+ "version": "5.12.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
+ "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
"dev": true,
"dependencies": {
- "graceful-fs": "^4.1.2",
- "memory-fs": "^0.5.0",
- "tapable": "^1.0.0"
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
},
"engines": {
- "node": ">=6.9.0"
+ "node": ">=10.13.0"
}
},
"node_modules/enquirer": {
@@ -3843,18 +3878,6 @@
"node": ">=4"
}
},
- "node_modules/errno": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
- "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
- "dev": true,
- "dependencies": {
- "prr": "~1.0.1"
- },
- "bin": {
- "errno": "cli.js"
- }
- },
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -7292,19 +7315,6 @@
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
"dev": true
},
- "node_modules/memory-fs": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
- "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
- "dev": true,
- "dependencies": {
- "errno": "^0.1.3",
- "readable-stream": "^2.0.1"
- },
- "engines": {
- "node": ">=4.3.0 <5.0.0 || >=5.10"
- }
- },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -8836,12 +8846,6 @@
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true
},
- "node_modules/prr": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
- "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
- "dev": true
- },
"node_modules/psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
@@ -10274,9 +10278,9 @@
}
},
"node_modules/tapable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
"dev": true,
"engines": {
"node": ">=6"
@@ -10608,368 +10612,168 @@
}
},
"node_modules/ts-loader": {
- "version": "5.4.5",
- "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz",
- "integrity": "sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw==",
+ "version": "9.4.2",
+ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz",
+ "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==",
"dev": true,
"dependencies": {
- "chalk": "^2.3.0",
- "enhanced-resolve": "^4.0.0",
- "loader-utils": "^1.0.2",
- "micromatch": "^3.1.4",
- "semver": "^5.0.1"
+ "chalk": "^4.1.0",
+ "enhanced-resolve": "^5.0.0",
+ "micromatch": "^4.0.0",
+ "semver": "^7.3.4"
},
"engines": {
- "node": ">=6.11.5"
+ "node": ">=12.0.0"
},
"peerDependencies": {
- "typescript": "*"
+ "typescript": "*",
+ "webpack": "^5.0.0"
}
},
- "node_modules/ts-loader/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "node_modules/ts-node": {
+ "version": "10.9.1",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+ "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
"dev": true,
"dependencies": {
- "color-convert": "^1.9.0"
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
},
- "engines": {
- "node": ">=4"
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
}
},
- "node_modules/ts-loader/node_modules/braces": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "node_modules/ts-node/node_modules/acorn": {
+ "version": "8.8.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
+ "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
"dev": true,
- "dependencies": {
- "arr-flatten": "^1.1.0",
- "array-unique": "^0.3.2",
- "extend-shallow": "^2.0.1",
- "fill-range": "^4.0.0",
- "isobject": "^3.0.1",
- "repeat-element": "^1.1.2",
- "snapdragon": "^0.8.1",
- "snapdragon-node": "^2.0.1",
- "split-string": "^3.0.2",
- "to-regex": "^3.0.1"
+ "bin": {
+ "acorn": "bin/acorn"
},
"engines": {
- "node": ">=0.10.0"
+ "node": ">=0.4.0"
}
},
- "node_modules/ts-loader/node_modules/braces/node_modules/extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "node_modules/ts-node/node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
- "dependencies": {
- "is-extendable": "^0.1.0"
- },
"engines": {
- "node": ">=0.10.0"
+ "node": ">=0.3.1"
}
},
- "node_modules/ts-loader/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "node_modules/tsconfig-paths": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz",
+ "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==",
"dev": true,
"dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.0",
+ "strip-bom": "^3.0.0"
}
},
- "node_modules/ts-loader/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ },
+ "node_modules/tsutils": {
+ "version": "3.21.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+ "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
"dev": true,
"dependencies": {
- "color-name": "1.1.3"
+ "tslib": "^1.8.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
}
},
- "node_modules/ts-loader/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
- "dev": true
- },
- "node_modules/ts-loader/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true,
+ "node_modules/tunnel": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+ "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"engines": {
- "node": ">=0.8.0"
+ "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
- "node_modules/ts-loader/node_modules/fill-range": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"dependencies": {
- "extend-shallow": "^2.0.1",
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1",
- "to-regex-range": "^2.1.0"
+ "safe-buffer": "^5.0.1"
},
"engines": {
- "node": ">=0.10.0"
+ "node": "*"
}
},
- "node_modules/ts-loader/node_modules/fill-range/node_modules/extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "node_modules/type": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+ "dev": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
"dependencies": {
- "is-extendable": "^0.1.0"
+ "prelude-ls": "^1.2.1"
},
"engines": {
- "node": ">=0.10.0"
+ "node": ">= 0.8.0"
}
},
- "node_modules/ts-loader/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true,
"engines": {
- "node": ">=4"
- }
- },
- "node_modules/ts-loader/node_modules/is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ts-loader/node_modules/is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
- "dev": true,
- "dependencies": {
- "kind-of": "^3.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ts-loader/node_modules/is-number/node_modules/kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ts-loader/node_modules/kind-of": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ts-loader/node_modules/loader-utils": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
- "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
- "dev": true,
- "dependencies": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- },
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/ts-loader/node_modules/micromatch": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
- "dev": true,
- "dependencies": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "braces": "^2.3.1",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "extglob": "^2.0.4",
- "fragment-cache": "^0.2.1",
- "kind-of": "^6.0.2",
- "nanomatch": "^1.2.9",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ts-loader/node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true,
- "bin": {
- "semver": "bin/semver"
- }
- },
- "node_modules/ts-loader/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/ts-loader/node_modules/to-regex-range": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
- "dev": true,
- "dependencies": {
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/ts-node": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
- "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==",
- "dev": true,
- "dependencies": {
- "arrify": "^1.0.0",
- "buffer-from": "^1.1.0",
- "diff": "^3.1.0",
- "make-error": "^1.1.1",
- "minimist": "^1.2.0",
- "mkdirp": "^0.5.1",
- "source-map-support": "^0.5.6",
- "yn": "^2.0.0"
- },
- "bin": {
- "ts-node": "dist/bin.js"
- },
- "engines": {
- "node": ">=4.2.0"
- }
- },
- "node_modules/ts-node/node_modules/diff": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
- "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
- "dev": true,
- "engines": {
- "node": ">=0.3.1"
- }
- },
- "node_modules/tsconfig-paths": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz",
- "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==",
- "dev": true,
- "dependencies": {
- "@types/json5": "^0.0.29",
- "json5": "^1.0.1",
- "minimist": "^1.2.0",
- "strip-bom": "^3.0.0"
- }
- },
- "node_modules/tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
- },
- "node_modules/tsutils": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
- "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
- "dev": true,
- "dependencies": {
- "tslib": "^1.8.1"
- },
- "engines": {
- "node": ">= 6"
- },
- "peerDependencies": {
- "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
- }
- },
- "node_modules/tunnel": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
- "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
- "engines": {
- "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
- }
- },
- "node_modules/tunnel-agent": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
- "dev": true,
- "dependencies": {
- "safe-buffer": "^5.0.1"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/type": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
- "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
- "dev": true
- },
- "node_modules/type-check": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
- "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "dev": true,
- "dependencies": {
- "prelude-ls": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typed-rest-client": {
@@ -10990,9 +10794,9 @@
"dev": true
},
"node_modules/typescript": {
- "version": "4.4.4",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
- "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
+ "version": "4.9.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
+ "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -11262,6 +11066,12 @@
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true
},
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true
+ },
"node_modules/v8flags": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
@@ -11698,28 +11508,6 @@
"acorn": "^8"
}
},
- "node_modules/webpack/node_modules/enhanced-resolve": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
- "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==",
- "dev": true,
- "dependencies": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/webpack/node_modules/tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/webpack/node_modules/webpack-sources": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.2.tgz",
@@ -11999,12 +11787,12 @@
}
},
"node_modules/yn": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
- "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"engines": {
- "node": ">=4"
+ "node": ">=6"
}
},
"node_modules/yocto-queue": {
@@ -12515,6 +12303,27 @@
}
}
},
+ "@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "dependencies": {
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ }
+ }
+ },
"@discoveryjs/json-ext": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
@@ -12667,9 +12476,9 @@
}
},
"@microsoft/vscode-azext-azureutils": {
- "version": "0.3.4",
- "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureutils/-/vscode-azext-azureutils-0.3.4.tgz",
- "integrity": "sha512-ABzi4b4UJcD0IuRGCjpJXyp5Z3yvS5Pqki9ZEvZxcowuqfHl6yCRe2oMZ1xrwkMDhuY8ox81eiarLCOSYkju6A==",
+ "version": "0.3.7",
+ "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-azureutils/-/vscode-azext-azureutils-0.3.7.tgz",
+ "integrity": "sha512-GZVHRWD1V5ACVH3r6/4RvPc5fStoR3dBVn2A9azdvWtODVt+ZasApED8nOfEbMNfbXYdop9iWtSvF9+FMXmu1g==",
"requires": {
"@azure/arm-resources": "^5.0.0",
"@azure/arm-resources-profile-2020-09-01-hybrid": "^2.0.0",
@@ -12677,7 +12486,7 @@
"@azure/arm-storage": "^17.0.0",
"@azure/arm-storage-profile-2020-09-01-hybrid": "^2.0.0",
"@azure/ms-rest-js": "^2.2.1",
- "@microsoft/vscode-azext-utils": "^0.3.3",
+ "@microsoft/vscode-azext-utils": "^0.4.0",
"semver": "^7.3.7",
"vscode-nls": "^5.0.1"
},
@@ -12702,16 +12511,16 @@
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"vscode-nls": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.1.tgz",
- "integrity": "sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A=="
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz",
+ "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng=="
}
}
},
"@microsoft/vscode-azext-dev": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-dev/-/vscode-azext-dev-0.1.2.tgz",
- "integrity": "sha512-6uZDiaNiW5TGcyO5RgmMWvzE3ozNuCW+eoKPKP7GCBMm2oM7AgVcaDOxeVUfeps1IAhvo9S0j/Mu7hbgSkQ7TA==",
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-dev/-/vscode-azext-dev-0.1.5.tgz",
+ "integrity": "sha512-HpDVKmqL/hRWVtMKpVfiW1ZKkhR4WmkauFA8Vo/vzUNpfSrWX543bAhCVLnCO22/5FjLudsxm3V8sgN0tKMuJA==",
"dev": true,
"requires": {
"@azure/arm-subscriptions": "^2.0.0",
@@ -12722,7 +12531,7 @@
"copy-webpack-plugin": "^6.0.0",
"fs-extra": "^8.0.0",
"terser-webpack-plugin": "^5.0.0",
- "ts-loader": "^5.3.3",
+ "ts-loader": "^9.4.2",
"webpack": "5.28.0"
},
"dependencies": {
@@ -12884,28 +12693,12 @@
"integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
"dev": true
},
- "enhanced-resolve": {
- "version": "5.9.2",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
- "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- }
- },
"es-module-lexer": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz",
"integrity": "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==",
"dev": true
},
- "tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true
- },
"webpack": {
"version": "5.28.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.28.0.tgz",
@@ -12950,9 +12743,9 @@
}
},
"@microsoft/vscode-azext-utils": {
- "version": "0.3.14",
- "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-utils/-/vscode-azext-utils-0.3.14.tgz",
- "integrity": "sha512-ic9pzUA3hpP8NLnlMYn3ebrz33w27AuFxJmTY5t7Z7PY026/A/+T4SPTBaWsyMRnE2KLbr5iti6ByMhhCm0MMw==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@microsoft/vscode-azext-utils/-/vscode-azext-utils-0.4.0.tgz",
+ "integrity": "sha512-1mnYfVah6pULYXExR7nkDqFdU1oA8bddtWlPANcWatSjB5qZ4qKFWrnkR3DqGvVItPptiNzKHYRmNBwJ+NAG9g==",
"requires": {
"@vscode/extension-telemetry": "^0.6.2",
"dayjs": "^1.11.2",
@@ -13049,6 +12842,30 @@
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
},
+ "@tsconfig/node10": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "dev": true
+ },
+ "@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true
+ },
+ "@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true
+ },
+ "@tsconfig/node16": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+ "dev": true
+ },
"@types/eslint": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz",
@@ -13169,12 +12986,6 @@
"integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
"dev": true
},
- "@types/tapable": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
- "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==",
- "dev": true
- },
"@types/uglify-js": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz",
@@ -13240,6 +13051,14 @@
"@types/webpack-sources": "*",
"anymatch": "^3.0.0",
"source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "@types/tapable": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
+ "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==",
+ "dev": true
+ }
}
},
"@types/webpack-sources": {
@@ -13572,6 +13391,12 @@
"dev": true,
"requires": {}
},
+ "acorn-walk": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "dev": true
+ },
"adal-node": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.2.3.tgz",
@@ -13731,6 +13556,12 @@
"readable-stream": "^2.0.6"
}
},
+ "arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -13876,12 +13707,6 @@
"es-abstract": "^1.19.0"
}
},
- "arrify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
- "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
- "dev": true
- },
"assign-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
@@ -14695,6 +14520,12 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true
},
+ "create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true
+ },
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -15074,14 +14905,13 @@
}
},
"enhanced-resolve": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
- "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
+ "version": "5.12.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
+ "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
"dev": true,
"requires": {
- "graceful-fs": "^4.1.2",
- "memory-fs": "^0.5.0",
- "tapable": "^1.0.0"
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
}
},
"enquirer": {
@@ -15104,15 +14934,6 @@
"integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
"dev": true
},
- "errno": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
- "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
- "dev": true,
- "requires": {
- "prr": "~1.0.1"
- }
- },
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -17807,16 +17628,6 @@
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
"dev": true
},
- "memory-fs": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
- "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
- "dev": true,
- "requires": {
- "errno": "^0.1.3",
- "readable-stream": "^2.0.1"
- }
- },
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -18985,12 +18796,6 @@
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true
},
- "prr": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
- "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
- "dev": true
- },
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
@@ -20104,9 +19909,9 @@
}
},
"tapable": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
- "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
"dev": true
},
"tar": {
@@ -20366,228 +20171,48 @@
"dev": true
},
"ts-loader": {
- "version": "5.4.5",
- "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz",
- "integrity": "sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw==",
+ "version": "9.4.2",
+ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz",
+ "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==",
"dev": true,
"requires": {
- "chalk": "^2.3.0",
- "enhanced-resolve": "^4.0.0",
- "loader-utils": "^1.0.2",
- "micromatch": "^3.1.4",
- "semver": "^5.0.1"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "braces": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
- "dev": true,
- "requires": {
- "arr-flatten": "^1.1.0",
- "array-unique": "^0.3.2",
- "extend-shallow": "^2.0.1",
- "fill-range": "^4.0.0",
- "isobject": "^3.0.1",
- "repeat-element": "^1.1.2",
- "snapdragon": "^0.8.1",
- "snapdragon-node": "^2.0.1",
- "split-string": "^3.0.2",
- "to-regex": "^3.0.1"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "fill-range": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
- "dev": true,
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1",
- "to-regex-range": "^2.1.0"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- }
- }
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
- "dev": true
- },
- "is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
- "dev": true,
- "requires": {
- "kind-of": "^3.0.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "^1.1.5"
- }
- }
- }
- },
- "kind-of": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
- "dev": true
- },
- "loader-utils": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
- "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
- "dev": true,
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- },
- "micromatch": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
- "dev": true,
- "requires": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "braces": "^2.3.1",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "extglob": "^2.0.4",
- "fragment-cache": "^0.2.1",
- "kind-of": "^6.0.2",
- "nanomatch": "^1.2.9",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.2"
- }
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "requires": {
- "has-flag": "^3.0.0"
- }
- },
- "to-regex-range": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
- "dev": true,
- "requires": {
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1"
- }
- }
+ "chalk": "^4.1.0",
+ "enhanced-resolve": "^5.0.0",
+ "micromatch": "^4.0.0",
+ "semver": "^7.3.4"
}
},
"ts-node": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
- "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==",
+ "version": "10.9.1",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+ "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
"dev": true,
"requires": {
- "arrify": "^1.0.0",
- "buffer-from": "^1.1.0",
- "diff": "^3.1.0",
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
"make-error": "^1.1.1",
- "minimist": "^1.2.0",
- "mkdirp": "^0.5.1",
- "source-map-support": "^0.5.6",
- "yn": "^2.0.0"
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
},
"dependencies": {
+ "acorn": {
+ "version": "8.8.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
+ "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
+ "dev": true
+ },
"diff": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
- "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
}
}
@@ -20671,9 +20296,9 @@
"dev": true
},
"typescript": {
- "version": "4.4.4",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
- "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
+ "version": "4.9.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
+ "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
"dev": true
},
"uc.micro": {
@@ -20900,6 +20525,12 @@
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true
},
+ "v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true
+ },
"v8flags": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
@@ -21170,22 +20801,6 @@
"dev": true,
"requires": {}
},
- "enhanced-resolve": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
- "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- }
- },
- "tapable": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true
- },
"webpack-sources": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.2.tgz",
@@ -21482,9 +21097,9 @@
}
},
"yn": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
- "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true
},
"yocto-queue": {
diff --git a/package.json b/package.json
index 53cb66872..bbddc5c7d 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"icon": "resources/azure-containerapps.png",
"aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255",
"engines": {
- "vscode": "^1.66.0"
+ "vscode": "^1.74.0"
},
"repository": {
"type": "git",
@@ -27,31 +27,17 @@
"multi-root ready"
],
"preview": true,
- "activationEvents": [
- "onCommand:containerApps.viewProperties",
- "onCommand:containerApps.openInPortal",
- "onCommand:containerApps.reportIssue",
- "onCommand:containerApps.createContainerApp",
- "onCommand:containerApps.deployImage",
- "onCommand:containerApps.deleteManagedEnvironment",
- "onCommand:containerApps.deleteContainerApp",
- "onCommand:containerApps.disableIngress",
- "onCommand:containerApps.enableIngress",
- "onCommand:containerApps.toggleVisibility",
- "onCommand:containerApps.editTargetPort",
- "onCommand:containerApps.chooseRevisionMode",
- "onCommand:containerApps.activateRevision",
- "onCommand:containerApps.deactivateRevision",
- "onCommand:containerApps.restartRevision",
- "onCommand:containerApps.createManagedEnvironment",
- "onCommand:containerApps.browse",
- "onCommand:containerApps.openConsoleInPortal",
- "onCommand:containerApps.editScalingRange",
- "onCommand:containerApps.addScaleRule"
- ],
+ "activationEvents": [],
"main": "./main.js",
"contributes": {
"x-azResources": {
+ "azure": {
+ "branches": [
+ {
+ "type": "ContainerAppsEnvironment"
+ }
+ ]
+ },
"activation": {
"onResolve": [
"microsoft.app/managedenvironments"
@@ -67,16 +53,6 @@
]
},
"commands": [
- {
- "command": "containerApps.viewProperties",
- "title": "%containerApps.viewProperties%",
- "category": "Azure Container Apps"
- },
- {
- "command": "containerApps.openInPortal",
- "title": "%containerApps.openInPortal%",
- "category": "Azure Container Apps"
- },
{
"command": "containerApps.reportIssue",
"title": "%containerApps.reportIssue%",
@@ -171,16 +147,6 @@
],
"menus": {
"view/item/context": [
- {
- "command": "containerApps.viewProperties",
- "when": "view == azureResourceGroups && viewItem =~ /azResource/i && viewItem =~ /^(?!.*containerEnvironment).*/i",
- "group": "z@1"
- },
- {
- "command": "containerApps.openInPortal",
- "when": "view == azureResourceGroups && viewItem =~ /containerApp[^s]/i",
- "group": "z@2"
- },
{
"command": "containerApps.createManagedEnvironment",
"when": "view == azureResourceGroups && viewItem =~ /azureResourceTypeGroup/i && viewItem =~ /containerAppsEnvironment/i",
@@ -266,16 +232,6 @@
"when": "view == azureResourceGroups && viewItem =~ /scaleRules/i",
"group": "1@1"
}
- ],
- "commandPalette": [
- {
- "command": "containerApps.viewProperties",
- "when": "never"
- },
- {
- "command": "containerApps.openInPortal",
- "when": "never"
- }
]
},
"configuration": [
@@ -327,7 +283,7 @@
},
"devDependencies": {
"@microsoft/eslint-config-azuretools": "^0.1.0",
- "@microsoft/vscode-azext-dev": "^0.1.2",
+ "@microsoft/vscode-azext-dev": "^0.1.5",
"@types/fs-extra": "^8.1.1",
"@types/git-url-parse": "^9.0.0",
"@types/gulp": "^4.0.6",
@@ -345,8 +301,8 @@
"mocha": "^10.1.0",
"mocha-junit-reporter": "^2.0.0",
"mocha-multi-reporters": "^1.1.7",
- "ts-node": "^7.0.1",
- "typescript": "^4.3.5",
+ "ts-node": "^10.9.1",
+ "typescript": "^4.9.4",
"vsce": "^1.87.1",
"vscode-test": "^1.5.2",
"webpack": "^5.28.0",
@@ -359,8 +315,8 @@
"@azure/arm-resources": "^4.2.2",
"@azure/container-registry": "1.0.0-beta.5",
"@azure/ms-rest-js": "^2.2.1",
- "@microsoft/vscode-azext-azureutils": "^0.3.4",
- "@microsoft/vscode-azext-utils": "^0.3.14",
+ "@microsoft/vscode-azext-azureutils": "^0.3.7",
+ "@microsoft/vscode-azext-utils": "^0.4.0",
"dayjs": "^1.11.3",
"dotenv": "^16.0.0",
"open": "^8.0.4",
diff --git a/resources/managedEnvironment.svg b/resources/managedEnvironment.svg
new file mode 100644
index 000000000..d6186a366
--- /dev/null
+++ b/resources/managedEnvironment.svg
@@ -0,0 +1,33 @@
+
+
+
diff --git a/src/ContainerAppsResolver.ts b/src/ContainerAppsResolver.ts
deleted file mode 100644
index b0e7f1922..000000000
--- a/src/ContainerAppsResolver.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-/*---------------------------------------------------------------------------------------------
-* Copyright (c) Microsoft Corporation. All rights reserved.
-* Licensed under the MIT License. See License.txt in the project root for license information.
-*--------------------------------------------------------------------------------------------*/
-
-import { ContainerAppsAPIClient, ManagedEnvironment } from "@azure/arm-appcontainers";
-import { getResourceGroupFromId } from "@microsoft/vscode-azext-azureutils";
-import { callWithTelemetryAndErrorHandling, IActionContext, ISubscriptionContext } from "@microsoft/vscode-azext-utils";
-import { AppResource, AppResourceResolver, ResolvedAppResourceBase } from "@microsoft/vscode-azext-utils/hostapi";
-import { managedEnvironmentsAppProvider } from "./constants";
-import { ResolvedContainerEnvironmentResource } from "./tree/ResolvedContainerAppsResource";
-import { createContainerAppsAPIClient } from "./utils/azureClients";
-
-export class ContainerAppsResolver implements AppResourceResolver {
- public async resolveResource(subContext: ISubscriptionContext, resource: AppResource): Promise {
- return await callWithTelemetryAndErrorHandling('resolveResource', async (context: IActionContext) => {
- const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, subContext]);
- const rgName: string = getResourceGroupFromId(resource.id);
- const me: ManagedEnvironment = await client.managedEnvironments.get(rgName, resource.name);
- return new ResolvedContainerEnvironmentResource(subContext, me);
- }) ?? null;
- }
-
- public matchesResource(resource: AppResource): boolean {
- return resource.type.toLowerCase() === managedEnvironmentsAppProvider.toLowerCase();
- }
-}
diff --git a/src/commands/api/revealTreeItem.ts b/src/commands/api/revealTreeItem.ts
deleted file mode 100644
index d13589351..000000000
--- a/src/commands/api/revealTreeItem.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { AzExtTreeItem, callWithTelemetryAndErrorHandling, IActionContext } from "@microsoft/vscode-azext-utils";
-import { ext } from "../../extensionVariables";
-
-export async function revealTreeItem(resourceId: string): Promise {
- return await callWithTelemetryAndErrorHandling('api.revealTreeItem', async (context: IActionContext) => {
- const node: AzExtTreeItem | undefined = await ext.rgApi.appResourceTree.findTreeItem(resourceId, { ...context, loadAll: true });
- if (node) {
- await ext.rgApi.appResourceTreeView.reveal(node, { select: true, focus: true, expand: true });
- }
- });
-}
diff --git a/src/commands/browse.ts b/src/commands/browse.ts
deleted file mode 100644
index 7344d4651..000000000
--- a/src/commands/browse.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { IActionContext } from '@microsoft/vscode-azext-utils';
-import { rootFilter } from '../constants';
-import { ext } from '../extensionVariables';
-import { ContainerAppTreeItem } from '../tree/ContainerAppTreeItem';
-
-export async function browse(context: IActionContext, node?: ContainerAppTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: ContainerAppTreeItem.contextValueRegExp
- });
- }
-
- await node.browse();
-}
diff --git a/src/commands/browseContainerApp.ts b/src/commands/browseContainerApp.ts
new file mode 100644
index 000000000..3c6e518d1
--- /dev/null
+++ b/src/commands/browseContainerApp.ts
@@ -0,0 +1,24 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ContainerApp } from '@azure/arm-appcontainers';
+import { IActionContext } from '@microsoft/vscode-azext-utils';
+import { ContainerAppItem, isIngressEnabled } from '../tree/ContainerAppItem';
+import { localize } from '../utils/localize';
+import { openUrl } from '../utils/openUrl';
+import { pickContainerApp } from '../utils/pickContainerApp';
+
+export async function browseContainerAppNode(context: IActionContext, node?: ContainerAppItem): Promise {
+ node ??= await pickContainerApp(context)
+ await browseContainerApp(node.containerApp);
+}
+
+export async function browseContainerApp(containerApp: ContainerApp): Promise {
+ if (isIngressEnabled(containerApp)) {
+ return await openUrl(`https://${containerApp.configuration.ingress.fqdn}`);
+ }
+
+ throw new Error(localize('enableIngress', 'Enable ingress to perform this action.'));
+}
diff --git a/src/commands/chooseRevisionMode.ts b/src/commands/chooseRevisionMode.ts
index 58540bdf7..f79ce32fb 100644
--- a/src/commands/chooseRevisionMode.ts
+++ b/src/commands/chooseRevisionMode.ts
@@ -3,51 +3,61 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
+import { KnownActiveRevisionsMode } from "@azure/arm-appcontainers";
import { IActionContext, IAzureQuickPickItem } from "@microsoft/vscode-azext-utils";
import { ProgressLocation, window } from "vscode";
-import { RevisionConstants, rootFilter } from "../constants";
import { ext } from "../extensionVariables";
-import { ContainerAppTreeItem } from "../tree/ContainerAppTreeItem";
-import { RevisionsTreeItem } from "../tree/RevisionsTreeItem";
+import { ContainerAppModel, refreshContainerApp } from "../tree/ContainerAppItem";
+import { ContainerAppsItem } from "../tree/ContainerAppsBranchDataProvider";
import { localize } from "../utils/localize";
-import { nonNullValue } from "../utils/nonNull";
+import { pickContainerApp } from "../utils/pickContainerApp";
import { updateContainerApp } from "./updateContainerApp";
-export async function chooseRevisionMode(context: IActionContext, node?: ContainerAppTreeItem | RevisionsTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: ContainerAppTreeItem.contextValueRegExp
- });
- }
+export async function chooseRevisionMode(context: IActionContext, node?: ContainerAppsItem): Promise {
+ const { subscription, containerApp } = node ?? await pickContainerApp(context);
- if (node instanceof RevisionsTreeItem) {
- node = node.parent;
- }
+ const pickedRevisionMode = await pickRevisionsMode(context, containerApp);
+ // only update it if it's actually different
+ if (containerApp.revisionsMode !== pickedRevisionMode) {
+ const updating = localize('updatingRevision', 'Updating revision mode of "{0}" to "{1}"...', containerApp.name, pickedRevisionMode);
+ ext.outputChannel.appendLog(updating);
- const picks: IAzureQuickPickItem[] = [RevisionConstants.single, RevisionConstants.multiple];
- const placeHolder = localize('chooseRevision', 'Choose revision mode');
+ await window.withProgress({ location: ProgressLocation.Notification, title: updating }, async (): Promise => {
+ await updateContainerApp(context, subscription, containerApp, { configuration: { activeRevisionsMode: pickedRevisionMode } });
+ refreshContainerApp(containerApp.id);
+ });
- for (const pick of picks) {
- pick.description = pick.data === node.getRevisionMode() ? localize('current', ' current') : undefined;
+ const updated = localize('updatedRevision', 'Updated revision mode of "{0}" to "{1}".', containerApp.name, pickedRevisionMode);
+ void window.showInformationMessage(updated);
+ ext.outputChannel.appendLog(updated);
}
+}
- const result = await context.ui.showQuickPick(picks, { placeHolder, suppressPersistence: true });
-
- // only update it if it's actually different
- if (node.getRevisionMode() !== result.data) {
- const updating = localize('updatingRevision', 'Updating revision mode of "{0}" to "{1}"...', node.name, result.data);
- const updated = localize('updatedRevision', 'Updated revision mode of "{0}" to "{1}".', node.name, result.data);
+function getRevisionsModePicks(containerApp: ContainerAppModel): IAzureQuickPickItem[] {
- await window.withProgress({ location: ProgressLocation.Notification, title: updating }, async (): Promise => {
- const pNode = nonNullValue(node) as ContainerAppTreeItem;
- ext.outputChannel.appendLog(updating);
+ function appendCurrent(description: string, revisionsMode: KnownActiveRevisionsMode): string {
+ return revisionsMode === containerApp.revisionsMode ? `${description} (current)` : description;
+ }
- await updateContainerApp(context, pNode, { configuration: { activeRevisionsMode: result.data } });
- await node?.parent?.refresh(context);
+ return [
+ {
+ label: localize('multiple', 'Multiple'),
+ description: appendCurrent(localize('multipleDesc', 'Several revisions active simultaneously'), KnownActiveRevisionsMode.Multiple),
+ data: KnownActiveRevisionsMode.Multiple,
+ },
+ {
+ label: localize('single', 'Single'),
+ description: appendCurrent(localize('singleDesc', 'One active revision at a time'), KnownActiveRevisionsMode.Single),
+ data: KnownActiveRevisionsMode.Single,
+ },
+ ];
+}
- void window.showInformationMessage(updated);
- ext.outputChannel.appendLog(updated);
- });
- }
+async function pickRevisionsMode(context: IActionContext, containerApp: ContainerAppModel): Promise {
+ const placeHolder = localize('chooseRevision', 'Choose revision mode');
+ const result = await context.ui.showQuickPick(getRevisionsModePicks(containerApp), {
+ placeHolder,
+ suppressPersistence: true,
+ });
+ return result.data;
}
diff --git a/src/commands/createContainerApp/ContainerAppCreateStep.ts b/src/commands/createContainerApp/ContainerAppCreateStep.ts
index 1f22532b6..941b93696 100644
--- a/src/commands/createContainerApp/ContainerAppCreateStep.ts
+++ b/src/commands/createContainerApp/ContainerAppCreateStep.ts
@@ -3,12 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { ContainerAppsAPIClient, Ingress, RegistryCredentials, Secret } from "@azure/arm-appcontainers";
+import { ContainerAppsAPIClient, Ingress, KnownActiveRevisionsMode, RegistryCredentials, Secret } from "@azure/arm-appcontainers";
import { LocationListStep } from "@microsoft/vscode-azext-azureutils";
import { AzureWizardExecuteStep } from "@microsoft/vscode-azext-utils";
import { Progress } from "vscode";
-import { containerAppsWebProvider, RevisionConstants } from "../../constants";
+import { containerAppsWebProvider } from "../../constants";
import { ext } from "../../extensionVariables";
+import { ContainerAppItem } from "../../tree/ContainerAppItem";
import { createContainerAppsAPIClient } from "../../utils/azureClients";
import { localize } from "../../utils/localize";
import { nonNullProp } from "../../utils/nonNull";
@@ -67,14 +68,14 @@ export class ContainerAppCreateStep extends AzureWizardExecuteStep & Partial, node?: ManagedEnvironmentTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.pickAppResource(context, { filter: rootFilter });
- }
+export async function createContainerApp(context: IActionContext & Partial & Partial, node?: ManagedEnvironmentItem): Promise {
+ node ??= await pickEnvironment(context);
- const caNode: ContainerAppTreeItem = await node.createChild(context);
- void showContainerAppCreated(caNode);
+ const wizardContext: IContainerAppWithActivityContext = {
+ ...context,
+ ...createSubscriptionContext(node.subscription),
+ managedEnvironmentId: node.managedEnvironment.id,
+ ...(await createActivityContext())
+ };
- await node.refresh(context);
- return caNode;
+ const title: string = localize('createContainerApp', 'Create Container App');
+
+ const promptSteps: AzureWizardPromptStep[] = [
+ new ContainerAppNameStep(),
+ new ContainerRegistryListStep(),
+ new EnvironmentVariablesListStep(),
+ new EnableIngressStep(),
+ ];
+
+ const executeSteps: AzureWizardExecuteStep[] = [
+ new VerifyProvidersStep([webProvider]),
+ new ContainerAppCreateStep(),
+ ];
+
+ wizardContext.newResourceGroupName = node.resource.resourceGroup;
+ await LocationListStep.setLocation(wizardContext, nonNullProp(node.resource, 'location'));
+
+ const wizard: AzureWizard = new AzureWizard(wizardContext, {
+ title,
+ promptSteps,
+ executeSteps,
+ showLoadingPrompt: true
+ });
+
+ await wizard.prompt();
+ const newContainerAppName = nonNullProp(wizardContext, 'newContainerAppName');
+
+ await ext.state.showCreatingChild(
+ node.managedEnvironment.id,
+ localize('creatingContainerApp', 'Creating Container App "{0}"...', newContainerAppName),
+ async () => {
+ wizardContext.activityTitle = localize('createNamedContainerApp', 'Create Container App "{0}"', newContainerAppName);
+ try {
+ await wizard.execute();
+ } finally {
+ // refresh this node even if create fails because container app provision failure throws an error, but still creates a container app
+ // ext.state.notifyChildrenChanged(node.managedEnvironment.id);
+ }
+ });
+
+ const createdContainerApp = nonNullProp(wizardContext, 'containerApp');
+ void showContainerAppCreated(createdContainerApp);
+ return new ContainerAppItem(node.subscription, createdContainerApp);
}
diff --git a/src/commands/createContainerApp/showContainerAppCreated.ts b/src/commands/createContainerApp/showContainerAppCreated.ts
index a31817b96..667bd4196 100644
--- a/src/commands/createContainerApp/showContainerAppCreated.ts
+++ b/src/commands/createContainerApp/showContainerAppCreated.ts
@@ -3,28 +3,32 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
+import { ContainerApp } from "@azure/arm-appcontainers";
import { callWithTelemetryAndErrorHandling, IActionContext } from "@microsoft/vscode-azext-utils";
import { MessageItem, window } from "vscode";
import { ext } from "../../extensionVariables";
-import { ContainerAppTreeItem } from "../../tree/ContainerAppTreeItem";
+import { isIngressEnabled } from "../../tree/ContainerAppItem";
import { localize } from "../../utils/localize";
+import { browseContainerApp } from "../browseContainerApp";
-export async function showContainerAppCreated(node: ContainerAppTreeItem, isUpdate: boolean = false): Promise {
+export async function showContainerAppCreated(containerApp: ContainerApp, isUpdate: boolean = false): Promise {
return await callWithTelemetryAndErrorHandling('containerApps.showCaCreated', async (context: IActionContext) => {
- const createdCa: string = localize('createdCa', 'Successfully created new container app "{0}".', node.name);
- const createdRevision = localize('createdRevision', 'Created a new revision "{1}" for container app "{0}"', node.name, node.data.latestRevisionName);
+ const createdCa: string = localize('createdCa', 'Successfully created new container app "{0}".', containerApp.name);
+ const createdRevision = localize('createdRevision', 'Created a new revision "{1}" for container app "{0}"', containerApp.name, containerApp.latestRevisionName);
const message = isUpdate ? createdRevision : createdCa;
ext.outputChannel.appendLog(message);
const browse: MessageItem = { title: localize('browse', 'Browse') };
const buttons: MessageItem[] = [];
- if (node.ingressEnabled()) { buttons.push(browse) }
- await window.showInformationMessage(message, ...buttons).then(async (result) => {
- context.telemetry.properties.clicked = 'canceled';
- if (result === browse) {
- await node.browse();
- context.telemetry.properties.clicked = 'browse';
- }
- });
+ if (isIngressEnabled(containerApp)) {
+ buttons.push(browse)
+ }
+ const result = await window.showInformationMessage(message, ...buttons)
+
+ context.telemetry.properties.clicked = 'canceled';
+ if (result === browse) {
+ await browseContainerApp(containerApp);
+ context.telemetry.properties.clicked = 'browse';
+ }
});
}
diff --git a/src/commands/createManagedEnvironment/IManagedEnvironmentContext.ts b/src/commands/createManagedEnvironment/IManagedEnvironmentContext.ts
index 12ca9c6f3..be14cea09 100644
--- a/src/commands/createManagedEnvironment/IManagedEnvironmentContext.ts
+++ b/src/commands/createManagedEnvironment/IManagedEnvironmentContext.ts
@@ -6,9 +6,9 @@
import { ManagedEnvironment } from "@azure/arm-appcontainers";
import { Workspace } from '@azure/arm-operationalinsights';
import { IResourceGroupWizardContext } from '@microsoft/vscode-azext-azureutils';
-import { ExecuteActivityContext, ICreateChildImplContext } from '@microsoft/vscode-azext-utils';
+import { ExecuteActivityContext } from '@microsoft/vscode-azext-utils';
-export interface IManagedEnvironmentContext extends IResourceGroupWizardContext, ICreateChildImplContext, ExecuteActivityContext {
+export interface IManagedEnvironmentContext extends IResourceGroupWizardContext, ExecuteActivityContext {
newManagedEnvironmentName?: string;
logAnalyticsWorkspace?: Workspace;
diff --git a/src/commands/createManagedEnvironment/createManagedEnvironment.ts b/src/commands/createManagedEnvironment/createManagedEnvironment.ts
index 7c7cbc9b5..1c6ca5d10 100644
--- a/src/commands/createManagedEnvironment/createManagedEnvironment.ts
+++ b/src/commands/createManagedEnvironment/createManagedEnvironment.ts
@@ -3,18 +3,48 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { SubscriptionTreeItemBase } from "@microsoft/vscode-azext-azureutils";
-import { AzExtParentTreeItem, IActionContext } from "@microsoft/vscode-azext-utils";
+import { LocationListStep, ResourceGroupCreateStep, VerifyProvidersStep } from "@microsoft/vscode-azext-azureutils";
+import { AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, IActionContext, nonNullProp, subscriptionExperience } from "@microsoft/vscode-azext-utils";
+import { AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
import { ext } from "../../extensionVariables";
-import { ManagedEnvironmentTreeItem } from "../../tree/ManagedEnvironmentTreeItem";
-import { SubscriptionTreeItem } from "../../tree/SubscriptionTreeItem";
+import { createSubscriptionContext } from "../../tree/ContainerAppsBranchDataProvider";
+import { createActivityContext } from "../../utils/activityUtils";
+import { localize } from "../../utils/localize";
import { IManagedEnvironmentContext } from "./IManagedEnvironmentContext";
+import { LogAnalyticsCreateStep } from "./LogAnalyticsCreateStep";
+import { ManagedEnvironmentCreateStep } from "./ManagedEnvironmentCreateStep";
+import { ManagedEnvironmentNameStep } from "./ManagedEnvironmentNameStep";
-export async function createManagedEnvironment(context: IActionContext, node?: AzExtParentTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.appResourceTree.showTreeItemPicker(SubscriptionTreeItemBase.contextValue, context);
- }
- const managedEnvironment = await SubscriptionTreeItem.createChild(context as IManagedEnvironmentContext, node as SubscriptionTreeItemBase);
- await ext.rgApi.appResourceTree.refresh(context);
- return managedEnvironment;
+export async function createManagedEnvironment(context: IActionContext, node?: { subscription: AzureSubscription }): Promise {
+ const subscription = node?.subscription ?? await subscriptionExperience(context, ext.rgApiV2.resources.azureResourceTreeDataProvider);
+
+ const wizardContext: IManagedEnvironmentContext = {
+ ...context,
+ ...createSubscriptionContext(subscription),
+ ...(await createActivityContext())
+ };
+
+ const title: string = localize('createManagedEnv', 'Create Container Apps environment');
+ const promptSteps: AzureWizardPromptStep[] = [];
+ const executeSteps: AzureWizardExecuteStep[] = [];
+
+ promptSteps.push(new ManagedEnvironmentNameStep());
+ executeSteps.push(new VerifyProvidersStep(['Microsoft.App', 'Microsoft.OperationalInsights']), new ResourceGroupCreateStep(), new LogAnalyticsCreateStep(), new ManagedEnvironmentCreateStep());
+ LocationListStep.addProviderForFiltering(wizardContext, 'Microsoft.App', 'managedEnvironments');
+ LocationListStep.addStep(wizardContext, promptSteps);
+
+ const wizard: AzureWizard = new AzureWizard(wizardContext, {
+ title,
+ promptSteps,
+ executeSteps,
+ showLoadingPrompt: true
+ });
+
+ await wizard.prompt();
+ const newManagedEnvName = nonNullProp(wizardContext, 'newManagedEnvironmentName');
+ wizardContext.newResourceGroupName = newManagedEnvName;
+ wizardContext.activityTitle = localize('createNamedManagedEnv', 'Create Container Apps environment "{0}"', newManagedEnvName);
+ await wizard.execute();
+
+ ext.branchDataProvider.refresh();
}
diff --git a/src/commands/deleteContainerApp/deleteContainerApp.ts b/src/commands/deleteContainerApp/deleteContainerApp.ts
new file mode 100644
index 000000000..7d4113164
--- /dev/null
+++ b/src/commands/deleteContainerApp/deleteContainerApp.ts
@@ -0,0 +1,12 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { IActionContext } from "@microsoft/vscode-azext-utils";
+import { ContainerAppItem } from "../../tree/ContainerAppItem";
+import { deleteNode } from "../deleteNode";
+
+export async function deleteContainerApp(context: IActionContext, node?: ContainerAppItem): Promise {
+ return deleteNode(context, ContainerAppItem.contextValueRegExp, node);
+}
diff --git a/src/commands/deleteManagedEnvironment/DeleteEnvironmentConfirmationStep.ts b/src/commands/deleteManagedEnvironment/DeleteEnvironmentConfirmationStep.ts
index f8f968d4b..1c13fdbf9 100644
--- a/src/commands/deleteManagedEnvironment/DeleteEnvironmentConfirmationStep.ts
+++ b/src/commands/deleteManagedEnvironment/DeleteEnvironmentConfirmationStep.ts
@@ -22,7 +22,7 @@ export class DeleteEnvironmentConfirmationStep extends AzureWizardPromptStep {
+ const { subscription, managedEnvironment } = node ?? await pickEnvironment(context);
+
+ const containerApps = await ContainerAppItem.List(context, subscription, managedEnvironment.id);
+ const resourceGroupName = getResourceGroupFromId(managedEnvironment.id);
+ const deleteManagedEnvironment: string = localize('deleteManagedEnvironment', 'Delete Container Apps Environment "{0}"', managedEnvironment.name);
+
+ const wizardContext: IDeleteManagedEnvironmentWizardContext = {
+ activityTitle: deleteManagedEnvironment,
+ containerAppNames: containerApps.map(ca => ca.name),
+ managedEnvironmentName: managedEnvironment.name,
+ resourceGroupName: resourceGroupName,
+ subscription: createSubscriptionContext(subscription),
+ ...context,
+ ...(await createActivityContext())
+ };
+
+ const wizard: AzureWizard = new AzureWizard(wizardContext, {
+ promptSteps: [new DeleteEnvironmentConfirmationStep()],
+ executeSteps: [new DeleteAllContainerAppsStep(), new DeleteManagedEnvironmentStep()]
+ });
+
+ if (!context.suppressPrompt) {
+ await wizard.prompt();
+ }
+
+ await ext.state.runWithTemporaryDescription(managedEnvironment.id, localize('deleting', 'Deleting...'), async () => {
+ await wizard.execute();
+ ext.branchDataProvider.refresh();
+ });
+}
diff --git a/src/commands/deleteNode.ts b/src/commands/deleteNode.ts
index c5f526c1b..02f428d99 100644
--- a/src/commands/deleteNode.ts
+++ b/src/commands/deleteNode.ts
@@ -3,17 +3,25 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { AzExtTreeItem, IActionContext } from '@microsoft/vscode-azext-utils';
-import { rootFilter } from '../constants';
+import { AzExtResourceType, azureResourceExperience, IActionContext } from '@microsoft/vscode-azext-utils';
import { ext } from '../extensionVariables';
-export async function deleteNode(context: IActionContext, expectedContextValue: string | RegExp, node?: AzExtTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.pickAppResource({ ...context, suppressCreatePick: true }, {
- filter: rootFilter,
- expectedChildContextValue: expectedContextValue
- });
+export async function deleteNode(context: IActionContext, expectedContextValue: string | RegExp, node?: IDeletable): Promise {
+ node ??= await azureResourceExperience(context, ext.rgApiV2.resources.azureResourceTreeDataProvider, AzExtResourceType.ContainerAppsEnvironment, {
+ include: [expectedContextValue]
+ });
+
+ if (!canDelete(node)) {
+ throw new Error('Node does not support delete');
}
- await node.deleteTreeItem(context);
+ await node.delete(context);
+}
+
+export interface IDeletable {
+ delete(context: IActionContext): Promise;
+}
+
+function canDelete(node: unknown): node is IDeletable {
+ return !!(node as IDeletable).delete;
}
diff --git a/src/commands/deployImage/deployImage.ts b/src/commands/deployImage/deployImage.ts
index e2b3bd3e5..4aef99e1a 100644
--- a/src/commands/deployImage/deployImage.ts
+++ b/src/commands/deployImage/deployImage.ts
@@ -3,16 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { ContainerAppsAPIClient } from "@azure/arm-appcontainers";
+import { ContainerApp, KnownActiveRevisionsMode } from "@azure/arm-appcontainers";
import { VerifyProvidersStep } from "@microsoft/vscode-azext-azureutils";
import { AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, ITreeItemPickerContext } from "@microsoft/vscode-azext-utils";
import { MessageItem, ProgressLocation, window } from "vscode";
-import { RevisionConstants, rootFilter, webProvider } from "../../constants";
+import { webProvider } from "../../constants";
import { ext } from "../../extensionVariables";
-import { ContainerAppTreeItem } from "../../tree/ContainerAppTreeItem";
-import { createContainerAppsAPIClient } from "../../utils/azureClients";
+import { ContainerAppItem, getContainerEnvelopeWithSecrets, refreshContainerApp } from "../../tree/ContainerAppItem";
+import { createSubscriptionContext } from "../../tree/ContainerAppsBranchDataProvider";
import { localize } from "../../utils/localize";
-import { nonNullValue } from "../../utils/nonNull";
+import { pickContainerApp } from "../../utils/pickContainerApp";
import { EnvironmentVariablesListStep } from "../createContainerApp/EnvironmentVariablesListStep";
import { getLoginServer } from "../createContainerApp/getLoginServer";
import { showContainerAppCreated } from "../createContainerApp/showContainerAppCreated";
@@ -21,27 +21,29 @@ import { ContainerRegistryListStep } from "./ContainerRegistryListStep";
import { getContainerNameForImage } from "./getContainerNameForImage";
import { IDeployImageContext } from "./IDeployImageContext";
-export async function deployImage(context: ITreeItemPickerContext & Partial, node?: ContainerAppTreeItem): Promise {
+export async function deployImage(context: ITreeItemPickerContext & Partial, node?: ContainerAppItem): Promise {
if (!node) {
context.suppressCreatePick = true;
- node = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: ContainerAppTreeItem.contextValueRegExp
- });
+ node = await pickContainerApp(context);
}
+ const { subscription, containerApp } = node;
- const wizardContext: IDeployImageContext = { ...context, ...node.subscription, targetContainer: node.data };
+ const wizardContext: IDeployImageContext = {
+ ...context,
+ ...createSubscriptionContext(subscription),
+ targetContainer: containerApp
+ };
- if (hasUnsupportedFeatures(node)) {
- const warning: string = node.getRevisionMode() === RevisionConstants.single.data ?
- localize('confirmDeploySingle', 'Are you sure you want to deploy to "{0}"? This will overwrite the active revision and unsupported features in VS Code will be lost.', node.name) :
- localize('confirmDeployMultiple', 'Are you sure you want to deploy to "{0}"? Unsupported features in VS Code will be lost in the new revision.', node.name);
+ if (hasUnsupportedFeatures(containerApp)) {
+ const warning: string = containerApp.revisionsMode === KnownActiveRevisionsMode.Single ?
+ localize('confirmDeploySingle', 'Are you sure you want to deploy to "{0}"? This will overwrite the active revision and unsupported features in VS Code will be lost.', containerApp.name) :
+ localize('confirmDeployMultiple', 'Are you sure you want to deploy to "{0}"? Unsupported features in VS Code will be lost in the new revision.', containerApp.name);
const items: MessageItem[] = [{ title: localize('deploy', 'Deploy') }];
await context.ui.showWarningMessage(warning, { modal: true, stepName: 'confirmDestructiveDeployment' }, ...items);
}
- const title: string = localize('updateImage', 'Update image in "{0}"', node.name);
+ const title: string = localize('updateImage', 'Update image in "{0}"', containerApp.name);
const promptSteps: AzureWizardPromptStep[] =
[new ContainerRegistryListStep(), new EnvironmentVariablesListStep()];
const executeSteps: AzureWizardExecuteStep[] = [new VerifyProvidersStep([webProvider])];
@@ -56,7 +58,7 @@ export async function deployImage(context: ITreeItemPickerContext & Partial {
+ const creatingRevision = localize('creatingRevision', 'Creating a new revision for container app "{0}"...', containerApp.name);
+ await ext.state.runWithTemporaryDescription(containerApp.id, localize('creating', 'Creating...'), async () => {
await window.withProgress({ location: ProgressLocation.Notification, title: creatingRevision }, async (): Promise => {
- node = nonNullValue(node);
- const webClient: ContainerAppsAPIClient = await createContainerAppsAPIClient([wizardContext, node]);
-
ext.outputChannel.appendLog(creatingRevision);
- node.data = await webClient.containerApps.beginCreateOrUpdateAndWait(node.resourceGroupName, node.name, containerAppEnvelope);
-
- void showContainerAppCreated(node, true);
+ const updatedContainerApp = await ContainerAppItem.Get(context, subscription, containerApp.resourceGroup, containerApp.name);
+ void showContainerAppCreated(updatedContainerApp, true);
});
- });
- await node.refresh(context);
+ refreshContainerApp(containerApp.id);
+ });
}
// check for any portal features that VS Code doesn't currently support
-function hasUnsupportedFeatures(node: ContainerAppTreeItem): boolean {
- if (node.data.template?.volumes) {
+function hasUnsupportedFeatures(containerApp: ContainerApp): boolean {
+ if (containerApp.template?.volumes) {
return true;
- } else if (node.data.template?.containers) {
- if (node.data.template.containers.length > 1) {
+ } else if (containerApp.template?.containers) {
+ if (containerApp.template.containers.length > 1) {
return true;
}
- for (const container of node.data.template.containers) {
+ for (const container of containerApp.template.containers) {
// NOTE: these are all arrays so if they are empty, this will still return true
// but these should be undefined if not being utilized
return !!container.probes || !!container.volumeMounts || !!container.args;
diff --git a/src/commands/ingress/disableIngress.ts b/src/commands/ingress/disableIngress.ts
new file mode 100644
index 000000000..97ec1ec98
--- /dev/null
+++ b/src/commands/ingress/disableIngress.ts
@@ -0,0 +1,22 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IActionContext } from '@microsoft/vscode-azext-utils';
+import { ContainerAppsItem } from "../../tree/ContainerAppsBranchDataProvider";
+import { localize } from '../../utils/localize';
+import { pickContainerApp } from "../../utils/pickContainerApp";
+import { updateIngressSettings } from "./updateIngressSettings";
+
+export async function disableIngress(context: IActionContext, node?: ContainerAppsItem): Promise {
+ const { subscription, containerApp } = node ??= await pickContainerApp(context);
+
+ await updateIngressSettings(context, {
+ ingress: null,
+ subscription: subscription,
+ containerApp: containerApp,
+ working: localize('disabling', 'Disabling ingress for container app "{0}"...', containerApp.name),
+ workCompleted: localize('disableCompleted', 'Disabled ingress for container app "{0}"', containerApp.name),
+ });
+}
diff --git a/src/commands/ingress/editTargetPort.ts b/src/commands/ingress/editTargetPort.ts
new file mode 100644
index 000000000..6e9ee19df
--- /dev/null
+++ b/src/commands/ingress/editTargetPort.ts
@@ -0,0 +1,41 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { AzureWizard, AzureWizardPromptStep, IActionContext, nonNullProp, nonNullValueAndProp } from "@microsoft/vscode-azext-utils";
+import { ContainerAppItem } from "../../tree/ContainerAppItem";
+import { createSubscriptionContext } from "../../tree/ContainerAppsBranchDataProvider";
+import { IngressItem } from "../../tree/IngressItem";
+import { localize } from "../../utils/localize";
+import { pickContainerApp } from "../../utils/pickContainerApp";
+import { IContainerAppContext } from "../createContainerApp/IContainerAppContext";
+import { TargetPortStep } from "../createContainerApp/TargetPortStep";
+import { updateIngressSettings } from "./updateIngressSettings";
+
+export async function editTargetPort(context: IActionContext, node?: IngressItem): Promise {
+ const { subscription, containerApp }: ContainerAppItem | IngressItem = node ?? await pickContainerApp(context);
+
+ const title: string = localize('updateTargetPort', 'Update Target Port');
+ const promptSteps: AzureWizardPromptStep[] = [new TargetPortStep()];
+
+ const wizardContext: IContainerAppContext = {
+ ...context,
+ ...createSubscriptionContext(subscription),
+ managedEnvironmentId: nonNullProp(containerApp, 'managedEnvironmentId'),
+ defaultPort: containerApp.configuration?.ingress?.targetPort
+ };
+
+ const wizard: AzureWizard = new AzureWizard(wizardContext, {
+ title,
+ promptSteps,
+ executeSteps: []
+ });
+
+ await wizard.prompt();
+ const ingress = nonNullValueAndProp(containerApp.configuration, 'ingress');
+ ingress.targetPort = wizardContext.targetPort;
+ const working: string = localize('updatingTargetPort', 'Updating target port to {0}...', ingress.targetPort);
+ const workCompleted: string = localize('updatedTargetPort', 'Updated target port to {0}', ingress.targetPort);
+ await updateIngressSettings(context, { ingress, subscription, containerApp, working, workCompleted });
+}
diff --git a/src/commands/ingress/enableIngress.ts b/src/commands/ingress/enableIngress.ts
new file mode 100644
index 000000000..026657d8a
--- /dev/null
+++ b/src/commands/ingress/enableIngress.ts
@@ -0,0 +1,56 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { Ingress } from "@azure/arm-appcontainers";
+import { AzureWizard, IActionContext, nonNullProp } from '@microsoft/vscode-azext-utils';
+import { AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { ContainerAppModel } from "../../tree/ContainerAppItem";
+import { ContainerAppsItem, createSubscriptionContext } from "../../tree/ContainerAppsBranchDataProvider";
+import { localize } from '../../utils/localize';
+import { pickContainerApp } from "../../utils/pickContainerApp";
+import { EnableIngressStep } from '../createContainerApp/EnableIngressStep';
+import { IContainerAppContext } from '../createContainerApp/IContainerAppContext';
+import { updateIngressSettings } from "./updateIngressSettings";
+
+export async function enableIngress(context: IActionContext, node?: ContainerAppsItem): Promise {
+ const { subscription, containerApp } = node ??= await pickContainerApp(context);
+
+ await updateIngressSettings(context, {
+ ingress: await promptForIngressConfiguration(context, subscription, containerApp),
+ subscription: subscription,
+ containerApp: containerApp,
+ working: localize('enabling', 'Enabling ingress for container app "{0}"...', containerApp.name),
+ workCompleted: localize('enableCompleted', 'Enabled ingress for container app "{0}"', containerApp.name),
+ });
+}
+
+async function promptForIngressConfiguration(context: IActionContext, subscription: AzureSubscription, containerApp: ContainerAppModel): Promise {
+ const title: string = localize('enableIngress', 'Enable Ingress');
+ const wizardContext: IContainerAppContext = {
+ ...context,
+ ...createSubscriptionContext(subscription),
+ managedEnvironmentId: nonNullProp(containerApp, 'managedEnvironmentId'),
+ };
+ const wizard: AzureWizard = new AzureWizard(wizardContext, {
+ title,
+ promptSteps: [new EnableIngressStep()]
+ });
+
+ wizardContext.enableIngress = true;
+ await wizard.prompt();
+
+ return {
+ targetPort: wizardContext.targetPort,
+ external: wizardContext.enableExternal,
+ transport: 'auto',
+ allowInsecure: false,
+ traffic: [
+ {
+ "weight": 100,
+ "latestRevision": true
+ }
+ ],
+ };
+}
diff --git a/src/commands/ingress/toggleIngressVisibility.ts b/src/commands/ingress/toggleIngressVisibility.ts
new file mode 100644
index 000000000..5698c09c3
--- /dev/null
+++ b/src/commands/ingress/toggleIngressVisibility.ts
@@ -0,0 +1,20 @@
+import { IActionContext, nonNullValueAndProp } from "@microsoft/vscode-azext-utils";
+import { IngressConstants } from "../../constants";
+import { ContainerAppItem } from "../../tree/ContainerAppItem";
+import { IngressItem } from "../../tree/IngressItem";
+import { localize } from "../../utils/localize";
+import { pickContainerApp } from "../../utils/pickContainerApp";
+import { updateIngressSettings } from "./updateIngressSettings";
+
+export async function toggleIngressVisibility(context: IActionContext, node?: IngressItem | ContainerAppItem): Promise {
+ node ??= await pickContainerApp(context);
+
+ const ingress = nonNullValueAndProp(node.containerApp.configuration, 'ingress');
+ const warningPrompt = localize('visibilityWarning', 'This will change the ingress visibility from "{0}" to "{1}".', ingress.external ? IngressConstants.external : IngressConstants.internal, !ingress.external ? IngressConstants.external : IngressConstants.internal)
+ await context.ui.showWarningMessage(warningPrompt, { modal: true }, { title: localize('continue', 'Continue') });
+
+ ingress.external = !ingress.external;
+ const working: string = localize('updatingVisibility', 'Updating ingress visibility to "{0}"...', ingress.external ? IngressConstants.external : IngressConstants.internal);
+ const workCompleted: string = localize('updatedVisibility', 'Updated ingress visibility to "{0}"', ingress.external ? IngressConstants.external : IngressConstants.internal);
+ await updateIngressSettings(context, { ingress, subscription: node.subscription, containerApp: node.containerApp, working, workCompleted });
+}
diff --git a/src/commands/ingress/updateIngressSettings.ts b/src/commands/ingress/updateIngressSettings.ts
new file mode 100644
index 000000000..c03572777
--- /dev/null
+++ b/src/commands/ingress/updateIngressSettings.ts
@@ -0,0 +1,33 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { Ingress } from "@azure/arm-appcontainers";
+import { IActionContext } from "@microsoft/vscode-azext-utils";
+import { AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { ProgressLocation, window } from "vscode";
+import { ext } from "../../extensionVariables";
+import { ContainerAppModel } from "../../tree/ContainerAppItem";
+import { updateContainerApp } from "../updateContainerApp";
+
+export async function updateIngressSettings(context: IActionContext,
+ options: {
+ ingress: Ingress | null,
+ subscription: AzureSubscription,
+ containerApp: ContainerAppModel,
+ working: string,
+ workCompleted: string
+ }): Promise {
+ const { ingress, subscription, containerApp, working, workCompleted } = options;
+
+ await window.withProgress({ location: ProgressLocation.Notification, title: working }, async (): Promise => {
+ ext.outputChannel.appendLog(working);
+ await updateContainerApp(context, subscription, containerApp, { configuration: { ingress: ingress as Ingress | undefined } })
+
+ void window.showInformationMessage(workCompleted);
+ ext.outputChannel.appendLog(workCompleted);
+ });
+
+ ext.state.notifyChildrenChanged(containerApp.managedEnvironmentId)
+}
diff --git a/src/commands/ingressCommands.ts b/src/commands/ingressCommands.ts
deleted file mode 100644
index 986e08dfe..000000000
--- a/src/commands/ingressCommands.ts
+++ /dev/null
@@ -1,138 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { Ingress } from "@azure/arm-appcontainers";
-import { AzureWizard, AzureWizardPromptStep, GenericTreeItem, IActionContext } from '@microsoft/vscode-azext-utils';
-import { ProgressLocation, window } from 'vscode';
-import { IngressConstants, rootFilter } from '../constants';
-import { ext } from '../extensionVariables';
-import { IngressDisabledTreeItem, IngressTreeItem } from '../tree/IngressTreeItem';
-import { localize } from '../utils/localize';
-import { nonNullValueAndProp } from '../utils/nonNull';
-import { EnableIngressStep } from './createContainerApp/EnableIngressStep';
-import { IContainerAppContext } from './createContainerApp/IContainerAppContext';
-import { TargetPortStep } from './createContainerApp/TargetPortStep';
-import { updateContainerApp } from "./updateContainerApp";
-
-export async function toggleIngress(context: IActionContext, node?: IngressTreeItem | IngressDisabledTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: [IngressTreeItem.contextValue, IngressDisabledTreeItem.contextValue]
- });
- }
-
- let ingress: Ingress | null = {};
-
- if (node instanceof IngressTreeItem) {
- // PATCH requires ingress be set to null exclusively to be picked up since undefined means there was no update
- ingress = null;
- } else {
- const title: string = localize('enableIngress', 'Enable Ingress');
- const promptSteps: AzureWizardPromptStep[] = [new EnableIngressStep()];
-
- const wizardContext: IContainerAppContext = { ...context, ...node.subscription, managedEnvironmentId: node.parent.managedEnvironmentId };
- const wizard: AzureWizard = new AzureWizard(wizardContext, {
- title,
- promptSteps,
- executeSteps: []
- });
-
- wizardContext.enableIngress = true;
- await wizard.prompt();
-
- ingress = {
- targetPort: wizardContext.targetPort,
- external: wizardContext.enableExternal,
- transport: 'auto',
- allowInsecure: false,
- traffic: [
- {
- "weight": 100,
- "latestRevision": true
- }
- ],
- };
- }
-
- const name = node.parent.name;
- const working = node instanceof IngressTreeItem ? localize('disabling', 'Disabling ingress for container app "{0}"...', name) : localize('enabling', 'Enabling ingress for container app "{0}"...', name);
- const workCompleted = node instanceof IngressTreeItem ? localize('disableCompleted', 'Disabled ingress for container app "{0}"', name) : localize('enableCompleted', 'Enabled ingress for container app "{0}"', name);
-
- await updateIngressSettings(context, { ingress, node, working, workCompleted });
-}
-
-export async function editTargetPort(context: IActionContext, target?: IngressTreeItem | GenericTreeItem): Promise {
- if (!target) {
- target = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: IngressTreeItem.contextValue
- });
- }
-
- // GenericTreeItem will be a targetPort node
- const node: IngressTreeItem = target instanceof IngressTreeItem ? target : target.parent as IngressTreeItem;
-
- const title: string = localize('updateTargetPort', 'Update Target Port');
- const promptSteps: AzureWizardPromptStep[] = [new TargetPortStep()];
-
- const wizardContext: IContainerAppContext = {
- ...context,
- ...node.subscription,
- managedEnvironmentId: node.parent.managedEnvironmentId,
- defaultPort: node.data.targetPort
- };
-
- const wizard: AzureWizard = new AzureWizard(wizardContext, {
- title,
- promptSteps,
- executeSteps: []
- });
-
- await wizard.prompt();
- const ingress = nonNullValueAndProp(node.parent.data.configuration, 'ingress');
- ingress.targetPort = wizardContext.targetPort;
- const working: string = localize('updatingTargetPort', 'Updating target port to {0}...', ingress.targetPort);
- const workCompleted: string = localize('updatedTargetPort', 'Updated target port to {0}', ingress.targetPort);
- await updateIngressSettings(context, { ingress, node, working, workCompleted });
-}
-
-export async function toggleIngressVisibility(context: IActionContext, node?: IngressTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: IngressTreeItem.contextValue
- });
- }
-
- const ingress = nonNullValueAndProp(node.parent.data.configuration, 'ingress');
- const warningPrompt = localize('visibilityWarning', 'This will change the ingress visibility from "{0}" to "{1}".', ingress.external ? IngressConstants.external : IngressConstants.internal, !ingress.external ? IngressConstants.external : IngressConstants.internal)
- await context.ui.showWarningMessage(warningPrompt, { modal: true }, { title: localize('continue', 'Continue') });
-
- ingress.external = !ingress.external;
- const working: string = localize('updatingVisibility', 'Updating ingress visibility to "{0}"...', ingress.external ? IngressConstants.external : IngressConstants.internal);
- const workCompleted: string = localize('updatedVisibility', 'Updated ingress visibility to "{0}"', ingress.external ? IngressConstants.external : IngressConstants.internal);
- await updateIngressSettings(context, { ingress, node, working, workCompleted });
-}
-
-async function updateIngressSettings(context: IActionContext,
- options: {
- ingress: Ingress | null,
- node: IngressTreeItem | IngressDisabledTreeItem,
- working: string,
- workCompleted: string
- }): Promise {
- const { ingress, node, working, workCompleted } = options;
-
- await window.withProgress({ location: ProgressLocation.Notification, title: working }, async (): Promise => {
- ext.outputChannel.appendLog(working);
- await updateContainerApp(context, node.parent, { configuration: { ingress: ingress as Ingress | undefined } })
-
- void window.showInformationMessage(workCompleted);
- ext.outputChannel.appendLog(workCompleted);
- });
-
- await node.parent.refresh(context);
-}
diff --git a/src/commands/openConsoleInPortal.ts b/src/commands/openConsoleInPortal.ts
new file mode 100644
index 000000000..56fbac836
--- /dev/null
+++ b/src/commands/openConsoleInPortal.ts
@@ -0,0 +1,18 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+
+import { IActionContext } from "@microsoft/vscode-azext-utils";
+import { commands } from "vscode";
+import { ContainerAppItem } from "../tree/ContainerAppItem";
+import { createPortalUrl } from "../utils/createPortalUrl";
+import { pickContainerApp } from "../utils/pickContainerApp";
+
+export async function openConsoleInPortal(context: IActionContext, node?: ContainerAppItem): Promise {
+ node ??= await pickContainerApp(context);
+ await commands.executeCommand('azureResourceGroups.openInPortal', {
+ portalUrl: createPortalUrl(node.subscription, `${node.containerApp.id}/console`),
+ });
+}
diff --git a/src/commands/openInPortal.ts b/src/commands/openInPortal.ts
deleted file mode 100644
index f2a7b7217..000000000
--- a/src/commands/openInPortal.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-import * as azUtil from '@microsoft/vscode-azext-azureutils';
-import { AzExtTreeItem, IActionContext } from '@microsoft/vscode-azext-utils';
-
-export async function openInPortal(_context: IActionContext, node: AzExtTreeItem): Promise {
- await azUtil.openInPortal(node, node.id ?? node.fullId);
-}
diff --git a/src/commands/registerCommands.ts b/src/commands/registerCommands.ts
index d862d7a9c..fc48a30f1 100644
--- a/src/commands/registerCommands.ts
+++ b/src/commands/registerCommands.ts
@@ -3,56 +3,52 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import * as azUtil from '@microsoft/vscode-azext-azureutils';
-import { IActionContext, registerCommand, registerErrorHandler, registerReportIssueCommand } from '@microsoft/vscode-azext-utils';
-import { commands } from 'vscode';
-import { rootFilter } from '../constants';
-import { ext } from '../extensionVariables';
-import { ContainerAppTreeItem } from '../tree/ContainerAppTreeItem';
-import { ManagedEnvironmentTreeItem } from '../tree/ManagedEnvironmentTreeItem';
-import { RevisionTreeItem } from '../tree/RevisionTreeItem';
-import { browse } from './browse';
+import { registerCommandWithTreeNodeUnwrapping, registerErrorHandler, registerReportIssueCommand } from '@microsoft/vscode-azext-utils';
+import { browseContainerAppNode } from './browseContainerApp';
import { chooseRevisionMode } from './chooseRevisionMode';
import { createContainerApp } from './createContainerApp/createContainerApp';
import { createManagedEnvironment } from './createManagedEnvironment/createManagedEnvironment';
-import { deleteNode } from './deleteNode';
+import { deleteContainerApp } from './deleteContainerApp/deleteContainerApp';
+import { deleteManagedEnvironment } from './deleteManagedEnvironment/deleteManagedEnvironment';
import { deployImage } from './deployImage/deployImage';
-import { editTargetPort, toggleIngress, toggleIngressVisibility } from './ingressCommands';
-import { openInPortal } from './openInPortal';
-import { changeRevisionActiveState } from './revisionCommands/changeRevisionActiveState';
+import { disableIngress } from './ingress/disableIngress';
+import { editTargetPort } from './ingress/editTargetPort';
+import { enableIngress } from './ingress/enableIngress';
+import { toggleIngressVisibility } from './ingress/toggleIngressVisibility';
+import { openConsoleInPortal } from './openConsoleInPortal';
+import { activateRevision } from './revisionCommands/activateRevision';
+import { deactivateRevision } from './revisionCommands/deactivateRevision';
+import { restartRevision } from './revisionCommands/restartRevision';
import { addScaleRule } from './scaling/addScaleRule/addScaleRule';
import { editScalingRange } from './scaling/editScalingRange';
-import { viewProperties } from './viewProperties';
export function registerCommands(): void {
- registerCommand('containerApps.viewProperties', viewProperties);
- registerCommand('containerApps.openInPortal', openInPortal);
- registerCommand('containerApps.selectSubscriptions', () => commands.executeCommand('azure-account.selectSubscriptions'));
- registerCommand('containerApps.browse', browse);
- registerCommand('containerApps.createManagedEnvironment', createManagedEnvironment);
- registerCommand('containerApps.createContainerApp', createContainerApp);
- registerCommand('containerApps.deployImage', deployImage);
- registerCommand('containerApps.deleteManagedEnvironment', async (context: IActionContext, node?: ManagedEnvironmentTreeItem) => await deleteNode(context, ManagedEnvironmentTreeItem.contextValueRegExp, node));
- registerCommand('containerApps.deleteContainerApp', async (context: IActionContext, node?: ContainerAppTreeItem) => await deleteNode(context, ContainerAppTreeItem.contextValueRegExp, node));
- registerCommand('containerApps.enableIngress', toggleIngress);
- registerCommand('containerApps.disableIngress', toggleIngress);
- registerCommand('containerApps.toggleVisibility', toggleIngressVisibility);
- registerCommand('containerApps.editTargetPort', editTargetPort);
- registerCommand('containerApps.chooseRevisionMode', chooseRevisionMode);
- registerCommand('containerApps.activateRevision', async (context: IActionContext, node?: RevisionTreeItem) => await changeRevisionActiveState(context, 'activate', node));
- registerCommand('containerApps.deactivateRevision', async (context: IActionContext, node?: RevisionTreeItem) => await changeRevisionActiveState(context, 'deactivate', node));
- registerCommand('containerApps.restartRevision', async (context: IActionContext, node?: RevisionTreeItem) => await changeRevisionActiveState(context, 'restart', node));
- registerCommand('containerApps.openConsoleInPortal', async (context: IActionContext, node?: ContainerAppTreeItem) => {
- if (!node) {
- node = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: ContainerAppTreeItem.contextValueRegExp
- });
- }
- await azUtil.openInPortal(node, `${node.id}/console`);
- });
- registerCommand('containerApps.editScalingRange', editScalingRange);
- registerCommand('containerApps.addScaleRule', addScaleRule);
+ // managed environments
+ registerCommandWithTreeNodeUnwrapping('containerApps.createManagedEnvironment', createManagedEnvironment);
+ registerCommandWithTreeNodeUnwrapping('containerApps.deleteManagedEnvironment', deleteManagedEnvironment);
+
+ // container apps
+ registerCommandWithTreeNodeUnwrapping('containerApps.createContainerApp', createContainerApp);
+ registerCommandWithTreeNodeUnwrapping('containerApps.deleteContainerApp', deleteContainerApp);
+ registerCommandWithTreeNodeUnwrapping('containerApps.deployImage', deployImage);
+ registerCommandWithTreeNodeUnwrapping('containerApps.openConsoleInPortal', openConsoleInPortal);
+ registerCommandWithTreeNodeUnwrapping('containerApps.browse', browseContainerAppNode);
+
+ // ingress
+ registerCommandWithTreeNodeUnwrapping('containerApps.enableIngress', enableIngress);
+ registerCommandWithTreeNodeUnwrapping('containerApps.disableIngress', disableIngress);
+ registerCommandWithTreeNodeUnwrapping('containerApps.toggleVisibility', toggleIngressVisibility);
+ registerCommandWithTreeNodeUnwrapping('containerApps.editTargetPort', editTargetPort);
+
+ // revisions
+ registerCommandWithTreeNodeUnwrapping('containerApps.chooseRevisionMode', chooseRevisionMode);
+ registerCommandWithTreeNodeUnwrapping('containerApps.activateRevision', activateRevision);
+ registerCommandWithTreeNodeUnwrapping('containerApps.deactivateRevision', deactivateRevision);
+ registerCommandWithTreeNodeUnwrapping('containerApps.restartRevision', restartRevision);
+
+ // scaling
+ registerCommandWithTreeNodeUnwrapping('containerApps.editScalingRange', editScalingRange);
+ registerCommandWithTreeNodeUnwrapping('containerApps.addScaleRule', addScaleRule);
// Suppress "Report an Issue" button for all errors in favor of the command
registerErrorHandler(c => c.errorHandling.suppressReportIssue = true);
diff --git a/src/commands/revisionCommands/activateRevision.ts b/src/commands/revisionCommands/activateRevision.ts
new file mode 100644
index 000000000..8f76470ac
--- /dev/null
+++ b/src/commands/revisionCommands/activateRevision.ts
@@ -0,0 +1,13 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IActionContext } from "@microsoft/vscode-azext-utils";
+import { ContainerAppItem } from "../../tree/ContainerAppItem";
+import { RevisionItem } from "../../tree/RevisionItem";
+import { executeRevisionOperation } from "./changeRevisionActiveState";
+
+export function activateRevision(context: IActionContext, node?: ContainerAppItem | RevisionItem): Promise {
+ return executeRevisionOperation(context, node, 'activateRevision');
+}
diff --git a/src/commands/revisionCommands/changeRevisionActiveState.ts b/src/commands/revisionCommands/changeRevisionActiveState.ts
index f5d522e57..de64cfbc5 100644
--- a/src/commands/revisionCommands/changeRevisionActiveState.ts
+++ b/src/commands/revisionCommands/changeRevisionActiveState.ts
@@ -5,45 +5,28 @@
import { ContainerAppsAPIClient } from "@azure/arm-appcontainers";
import { IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils";
-import { rootFilter } from "../../constants";
import { ext } from "../../extensionVariables";
-import { ContainerAppTreeItem } from '../../tree/ContainerAppTreeItem';
-import { RevisionTreeItem } from "../../tree/RevisionTreeItem";
-import { createContainerAppsAPIClient } from "../../utils/azureClients";
+import { ContainerAppItem } from "../../tree/ContainerAppItem";
+import { RevisionItem } from "../../tree/RevisionItem";
+import { createContainerAppsClient } from "../../utils/azureClients";
import { localize } from "../../utils/localize";
+import { pickContainerApp } from "../../utils/pickContainerApp";
-export async function changeRevisionActiveState(context: IActionContext, command: 'activate' | 'deactivate' | 'restart', node?: ContainerAppTreeItem | RevisionTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: ContainerAppTreeItem.contextValueRegExp
- });
- }
+export async function executeRevisionOperation(context: IActionContext, node: ContainerAppItem | RevisionItem | undefined, operation: RevisionOperation): Promise {
+ const item = node ?? await pickContainerApp(context);
- const containerAppName: string = node instanceof RevisionTreeItem ? node.parent.parent.name : node.name;
- const revisionName: string = node instanceof RevisionTreeItem ? node.name : nonNullProp(node.data, 'latestRevisionName');
- const resourceGroupName: string = node instanceof RevisionTreeItem ? node.parent.parent.resourceGroupName : node.resourceGroupName;
-
- const appClient: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, node]);
-
- const temporaryDescriptions = {
- 'activate': localize('activating', 'Activating...'),
- 'deactivate': localize('deactivating', 'Deactivating...'),
- 'restart': localize('restarting', 'Restarting...'),
- }
- await node.runWithTemporaryDescription(context, temporaryDescriptions[command], async () => {
- switch (command) {
- case 'activate':
- await appClient.containerAppsRevisions.activateRevision(resourceGroupName, containerAppName, revisionName);
- break;
- case 'deactivate':
- await appClient.containerAppsRevisions.deactivateRevision(resourceGroupName, containerAppName, revisionName);
- break;
- case 'restart':
- await appClient.containerAppsRevisions.restartRevision(resourceGroupName, containerAppName, revisionName);
- break;
- }
+ await ext.state.runWithTemporaryDescription(item.id, revisionOperationDescriptions[operation], async () => {
+ const appClient: ContainerAppsAPIClient = await createContainerAppsClient(context, item.subscription);
+ const revisionName: string = item instanceof RevisionItem ? nonNullProp(item.revision, 'name') : nonNullProp(item.containerApp, 'latestRevisionName');
+ await appClient.containerAppsRevisions[operation](item.containerApp.resourceGroup, item.containerApp.name, revisionName);
+ ext.state.notifyChildrenChanged(item.containerApp.id);
});
-
- await node.refresh(context);
}
+
+const revisionOperationDescriptions = {
+ activateRevision: localize('activating', 'Activating...'),
+ deactivateRevision: localize('deactivating', 'Deactivating...'),
+ restartRevision: localize('restarting', 'Restarting...'),
+} satisfies Partial>;
+
+export type RevisionOperation = keyof typeof revisionOperationDescriptions;
diff --git a/src/commands/revisionCommands/deactivateRevision.ts b/src/commands/revisionCommands/deactivateRevision.ts
new file mode 100644
index 000000000..a6ebb9f76
--- /dev/null
+++ b/src/commands/revisionCommands/deactivateRevision.ts
@@ -0,0 +1,13 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IActionContext } from "@microsoft/vscode-azext-utils";
+import { ContainerAppItem } from "../../tree/ContainerAppItem";
+import { RevisionItem } from "../../tree/RevisionItem";
+import { executeRevisionOperation } from "./changeRevisionActiveState";
+
+export function deactivateRevision(context: IActionContext, node?: ContainerAppItem | RevisionItem): Promise {
+ return executeRevisionOperation(context, node, 'deactivateRevision');
+}
diff --git a/src/commands/revisionCommands/restartRevision.ts b/src/commands/revisionCommands/restartRevision.ts
new file mode 100644
index 000000000..85905e779
--- /dev/null
+++ b/src/commands/revisionCommands/restartRevision.ts
@@ -0,0 +1,13 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IActionContext } from "@microsoft/vscode-azext-utils";
+import { ContainerAppItem } from "../../tree/ContainerAppItem";
+import { RevisionItem } from "../../tree/RevisionItem";
+import { executeRevisionOperation } from "./changeRevisionActiveState";
+
+export function restartRevision(context: IActionContext, node?: ContainerAppItem | RevisionItem): Promise {
+ return executeRevisionOperation(context, node, 'restartRevision');
+}
diff --git a/src/commands/scaling/addScaleRule/AddScaleRuleStep.ts b/src/commands/scaling/addScaleRule/AddScaleRuleStep.ts
index eb60ef0d1..afdb91487 100644
--- a/src/commands/scaling/addScaleRule/AddScaleRuleStep.ts
+++ b/src/commands/scaling/addScaleRule/AddScaleRuleStep.ts
@@ -19,15 +19,15 @@ export class AddScaleRuleStep extends AzureWizardExecuteStep {
public hideStepCount: boolean = true;
- containerApp: ContainerApp | undefined;
- scaleRules: ScaleRule[] | undefined;
public async prompt(context: IAddScaleRuleWizardContext): Promise {
- this.containerApp = context.containerApp.data;
- this.scaleRules = context.scaleRuleGroup.data;
context.ruleName = (await context.ui.showInputBox({
prompt: localize('scaleRuleNamePrompt', 'Enter a name for the new scale rule.'),
- validateInput: (value: string | undefined): string | undefined => this.validateInput(value)
+ validateInput: (name: string | undefined): string | undefined => {
+ return validateScaleRuleInput(name, context.containerApp?.name, context.scaleRules);
+ }
})).trim();
}
public shouldPrompt(context: IAddScaleRuleWizardContext): boolean {
return context.ruleName === undefined;
}
+}
- private validateInput(name: string | undefined): string | undefined {
- name = name ? name.trim() : '';
+function validateScaleRuleInput(name: string | undefined, containerAppName: string, scaleRules: ScaleRule[]): string | undefined {
+ name = name ? name.trim() : '';
- if (!/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(name)) {
- return localize('invalidChar', `A name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character.`);
- }
+ if (!/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/.test(name)) {
+ return localize('invalidChar', `A name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character.`);
+ }
- const scaleRuleExists: boolean = !!this.scaleRules?.some((rule) => {
- return rule?.name?.length && rule?.name === name;
- });
- if (scaleRuleExists) {
- return localize('scaleRuleExists', 'The scale rule "{0}" already exists in container app "{1}". Please enter a unique name.', name, this.containerApp?.name);
- }
- return undefined;
+ const scaleRuleExists: boolean = !!scaleRules?.some((rule) => {
+ return rule?.name?.length && rule?.name === name;
+ });
+ if (scaleRuleExists) {
+ return localize('scaleRuleExists', 'The scale rule "{0}" already exists in container app "{1}". Please enter a unique name.', name, containerAppName);
}
+ return undefined;
}
diff --git a/src/commands/scaling/addScaleRule/addScaleRule.ts b/src/commands/scaling/addScaleRule/addScaleRule.ts
index 2517c7f57..d8b5bf46e 100644
--- a/src/commands/scaling/addScaleRule/addScaleRule.ts
+++ b/src/commands/scaling/addScaleRule/addScaleRule.ts
@@ -3,21 +3,69 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IActionContext } from "@microsoft/vscode-azext-utils";
-import { rootFilter } from "../../../constants";
+import { ContainerAppsAPIClient, KnownActiveRevisionsMode, Revision } from "@azure/arm-appcontainers";
+import { uiUtils } from "@microsoft/vscode-azext-azureutils";
+import { AzureWizard, IActionContext, IAzureQuickPickItem, nonNullProp, nonNullValue } from "@microsoft/vscode-azext-utils";
+import { AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
import { ext } from "../../../extensionVariables";
-import { ContainerAppTreeItem } from "../../../tree/ContainerAppTreeItem";
-import { ScaleRuleGroupTreeItem } from "../../../tree/ScaleRuleGroupTreeItem";
-import { treeUtils } from "../../../utils/treeUtils";
-
-export async function addScaleRule(context: IActionContext, node?: ScaleRuleGroupTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: new RegExp(ScaleRuleGroupTreeItem.contextValue)
- });
+import { ContainerAppItem, ContainerAppModel } from "../../../tree/ContainerAppItem";
+import { ScaleRuleGroupItem } from "../../../tree/scaling/ScaleRuleGroupItem";
+import { createContainerAppsClient } from "../../../utils/azureClients";
+import { localize } from "../../../utils/localize";
+import { pickContainerApp } from "../../../utils/pickContainerApp";
+import { AddScaleRuleStep } from "./AddScaleRuleStep";
+import { IAddScaleRuleWizardContext } from "./IAddScaleRuleWizardContext";
+import { ScaleRuleNameStep } from "./ScaleRuleNameStep";
+import { ScaleRuleTypeStep } from "./ScaleRuleTypeStep";
+
+export async function addScaleRule(context: IActionContext, node?: ScaleRuleGroupItem): Promise {
+ const { subscription, containerApp, revision } = node ?? await getContainerAppAndRevision(context);
+
+ const scale = nonNullValue(revision?.template?.scale);
+ const wizardContext: IAddScaleRuleWizardContext = {
+ ...context,
+ scale,
+ subscription,
+ containerApp,
+ scaleRules: scale.rules ?? [],
+ };
+
+ const wizard: AzureWizard = new AzureWizard(wizardContext, {
+ title: localize('addScaleRuleTitle', 'Add Scale Rule'),
+ promptSteps: [new ScaleRuleNameStep(), new ScaleRuleTypeStep()],
+ executeSteps: [new AddScaleRuleStep()],
+ showLoadingPrompt: true
+ });
+
+ await wizard.prompt();
+ await wizard.execute();
+ ext.state.notifyChildrenChanged(containerApp.managedEnvironmentId);
+}
+
+export async function getContainerAppAndRevision(context: IActionContext): Promise<{ containerApp: ContainerAppModel, revision: Revision, subscription: AzureSubscription }> {
+ const containerAppItem = await pickContainerApp(context);
+ const revision = await pickRevision(context, containerAppItem);
+ return { containerApp: containerAppItem.containerApp, revision, subscription: containerAppItem.subscription };
+}
+
+/**
+ * If the container app is in single revision mode, returns the latest revision. Otherwise, prompts the user to pick a revision.
+ */
+async function pickRevision(context: IActionContext, node: ContainerAppItem): Promise {
+ const client: ContainerAppsAPIClient = await createContainerAppsClient(context, node.subscription);
+
+ if (node.containerApp.revisionsMode === KnownActiveRevisionsMode.Single) {
+ return await client.containerAppsRevisions.getRevision(node.containerApp.resourceGroup, node.containerApp.name, nonNullProp(node.containerApp, 'latestRevisionName'));
+ } else {
+ async function getPicks(): Promise[]> {
+ const revisions = await uiUtils.listAllIterator(client.containerAppsRevisions.listRevisions(node.containerApp.resourceGroup, node.containerApp.name));
+ return revisions.map(r => ({
+ label: nonNullProp(r, 'name'),
+ data: r,
+ }));
+ }
+ return (await context.ui.showQuickPick>(getPicks(), {
+ canPickMany: false,
+ })).data;
}
- const containerApp: ContainerAppTreeItem = treeUtils.findNearestParent(node, ContainerAppTreeItem.contextValue);
- await node.createChild(context);
- await containerApp.refresh(context);
}
diff --git a/src/commands/scaling/addScaleRule/queue/QueueAuthSecretStep.ts b/src/commands/scaling/addScaleRule/queue/QueueAuthSecretStep.ts
index 2c64a8e80..242ca4a88 100644
--- a/src/commands/scaling/addScaleRule/queue/QueueAuthSecretStep.ts
+++ b/src/commands/scaling/addScaleRule/queue/QueueAuthSecretStep.ts
@@ -6,13 +6,14 @@
import { Secret } from '@azure/arm-appcontainers';
import { AzureWizardPromptStep, nonNullProp } from '@microsoft/vscode-azext-utils';
import { QuickPickItem } from 'vscode';
+import { getContainerEnvelopeWithSecrets } from '../../../../tree/ContainerAppItem';
import { localize } from '../../../../utils/localize';
import { IAddScaleRuleWizardContext } from '../IAddScaleRuleWizardContext';
export class QueueAuthSecretStep extends AzureWizardPromptStep {
public async prompt(context: IAddScaleRuleWizardContext): Promise {
const placeHolder: string = localize('chooseSecretRef', 'Choose a secret reference');
- const containerAppWithSecrets = await context.containerApp.getContainerEnvelopeWithSecrets(context);
+ const containerAppWithSecrets = await getContainerEnvelopeWithSecrets(context, context.subscription, context.containerApp);
const secrets: Secret[] | undefined = containerAppWithSecrets.configuration.secrets;
if (!secrets?.length) {
const noSecrets: string = localize('noSecretsFound', 'No secrets were found. Create a secret to proceed.');
diff --git a/src/commands/scaling/editScalingRange.ts b/src/commands/scaling/editScalingRange.ts
index 539790c2d..60c8bee9a 100644
--- a/src/commands/scaling/editScalingRange.ts
+++ b/src/commands/scaling/editScalingRange.ts
@@ -3,26 +3,21 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IActionContext } from "@microsoft/vscode-azext-utils";
+import { IActionContext, nonNullValue } from "@microsoft/vscode-azext-utils";
import { ProgressLocation, window } from "vscode";
-import { rootFilter } from "../../constants";
import { ext } from "../../extensionVariables";
-import { ContainerAppTreeItem } from "../../tree/ContainerAppTreeItem";
-import { RevisionTreeItem } from "../../tree/RevisionTreeItem";
-import { ScaleTreeItem } from "../../tree/ScaleTreeItem";
+import { refreshContainerApp } from "../../tree/ContainerAppItem";
+import { ScaleItem } from "../../tree/scaling/ScaleItem";
import { localize } from "../../utils/localize";
import { updateContainerApp } from "../updateContainerApp";
+import { getContainerAppAndRevision } from "./addScaleRule/addScaleRule";
-export async function editScalingRange(context: IActionContext, node?: ScaleTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: new RegExp(ScaleTreeItem.contextValue)
- });
- }
+export async function editScalingRange(context: IActionContext, node?: ScaleItem): Promise {
+ const { containerApp, revision, subscription } = node ?? await getContainerAppAndRevision(context);
+ const scale = nonNullValue(revision?.template?.scale);
const prompt: string = localize('editScalingRange', 'Set the range of application replicas that get created in response to a scale rule. Set any range within the minimum of 0 and the maximum of 10 replicas');
- const value: string = `${node.minReplicas}-${node.maxReplicas}`;
+ const value: string = `${scale.minReplicas ?? 0}-${scale.maxReplicas ?? 0}`;
const range = await context.ui.showInputBox({
prompt,
value,
@@ -30,12 +25,11 @@ export async function editScalingRange(context: IActionContext, node?: ScaleTree
});
const [min, max] = range.split('-').map(range => Number(range));
- const containerApp = node.parent instanceof RevisionTreeItem ? node.parent.parent.parent as ContainerAppTreeItem : node.parent as ContainerAppTreeItem;
const updating = localize('updatingRevision', 'Updating scale rule setting of "{0}" to min/max replicas of {1}-{2}...', containerApp.name, min, max);
const updated = localize('updatedRevision', 'Updated scale rule setting of "{0}" to min/max replicas of {1}-{2}.', containerApp.name, min, max);
- const template = containerApp?.data?.template || {};
+ const template = containerApp?.template || {};
template.scale ||= {};
template.scale.minReplicas = min;
@@ -43,9 +37,8 @@ export async function editScalingRange(context: IActionContext, node?: ScaleTree
await window.withProgress({ location: ProgressLocation.Notification, title: updating }, async (): Promise => {
ext.outputChannel.appendLog(updating);
- await updateContainerApp(context, containerApp, { template })
-
- await containerApp?.refresh(context);
+ await updateContainerApp(context, subscription, containerApp, { template })
+ refreshContainerApp(containerApp.id);
void window.showInformationMessage(updated);
ext.outputChannel.appendLog(updated);
});
@@ -68,4 +61,3 @@ async function validateInput(range: string | undefined): Promise): Promise {
- const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, node]);
- const resourceGroupName = node.resourceGroupName;
- const name = node.name;
- const updatedApp: ContainerApp = { ...updatedSetting, location: node.data.location };
+export async function updateContainerApp(context: IActionContext, subscription: AzureSubscription, containerApp: ContainerApp, updatedSetting: Omit): Promise {
+ const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, createSubscriptionContext(subscription)]);
+ const resourceGroupName = getResourceGroupFromId(nonNullProp(containerApp, 'id'));
+ const name = nonNullProp(containerApp, 'name');
+ const updatedApp: ContainerApp = { ...updatedSetting, location: containerApp.location };
await client.containerApps.beginUpdateAndWait(resourceGroupName, name, updatedApp);
}
diff --git a/src/commands/viewProperties.ts b/src/commands/viewProperties.ts
deleted file mode 100644
index f858a1178..000000000
--- a/src/commands/viewProperties.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { IActionContext, openReadOnlyJson } from '@microsoft/vscode-azext-utils';
-import { azResourceRegExp, rootFilter } from '../constants';
-import { ext } from '../extensionVariables';
-import { IAzureResourceTreeItem } from '../tree/IAzureResourceTreeItem';
-import { localize } from '../utils/localize';
-import { nonNullProp } from '../utils/nonNull';
-
-export async function viewProperties(context: IActionContext, node?: IAzureResourceTreeItem): Promise {
- if (!node) {
- node = await ext.rgApi.pickAppResource(context, {
- filter: rootFilter,
- expectedChildContextValue: azResourceRegExp
- });
- }
-
- if (!node.data) {
- if (node.getDataImpl) {
- await node.getDataImpl();
- } else {
- throw new Error(localize('viewProperties.noData', 'No data exists on resource "{0}"', node.label));
- }
- }
-
- await openReadOnlyJson(node, nonNullProp(node, 'data'));
-}
diff --git a/src/extension.ts b/src/extension.ts
index 2d624211e..c0d3ec5cf 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -6,17 +6,15 @@
'use strict';
import { registerAzureUtilsExtensionVariables } from '@microsoft/vscode-azext-azureutils';
-import { callWithTelemetryAndErrorHandling, createApiProvider, createAzExtOutputChannel, createExperimentationService, IActionContext, registerUIExtensionVariables } from '@microsoft/vscode-azext-utils';
-import { AzureExtensionApi, AzureExtensionApiProvider } from '@microsoft/vscode-azext-utils/api';
+import { AzExtResourceType, callWithTelemetryAndErrorHandling, createAzExtOutputChannel, createExperimentationService, IActionContext, registerUIExtensionVariables } from '@microsoft/vscode-azext-utils';
import * as vscode from 'vscode';
-import { revealTreeItem } from './commands/api/revealTreeItem';
import { registerCommands } from './commands/registerCommands';
-import { managedEnvironmentsAppProvider } from './constants';
-import { ContainerAppsResolver } from './ContainerAppsResolver';
import { ext } from './extensionVariables';
import { getResourceGroupsApi } from './getExtensionApi';
+import { ContainerAppsBranchDataProvider } from './tree/ContainerAppsBranchDataProvider';
+import { TreeItemStateStore } from './tree/TreeItemState';
-export async function activateInternal(context: vscode.ExtensionContext, perfStats: { loadStartTime: number; loadEndTime: number }, ignoreBundle?: boolean): Promise {
+export async function activateInternal(context: vscode.ExtensionContext, perfStats: { loadStartTime: number; loadEndTime: number }, ignoreBundle?: boolean): Promise {
ext.context = context;
ext.ignoreBundle = ignoreBundle;
ext.outputChannel = createAzExtOutputChannel('Azure Container Apps', ext.prefix);
@@ -32,14 +30,11 @@ export async function activateInternal(context: vscode.ExtensionContext, perfSta
registerCommands();
ext.experimentationService = await createExperimentationService(context);
- ext.rgApi = await getResourceGroupsApi();
- ext.rgApi.registerApplicationResourceResolver(managedEnvironmentsAppProvider, new ContainerAppsResolver());
+ ext.state = new TreeItemStateStore();
+ ext.rgApiV2 = await getResourceGroupsApi();
+ ext.branchDataProvider = new ContainerAppsBranchDataProvider();
+ ext.rgApiV2.resources.registerAzureResourceBranchDataProvider(AzExtResourceType.ContainerAppsEnvironment, ext.branchDataProvider);
});
-
- return createApiProvider([{
- revealTreeItem,
- apiVersion: '1.0.0'
- }]);
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
diff --git a/src/extensionVariables.ts b/src/extensionVariables.ts
index bffdf1729..216483b27 100644
--- a/src/extensionVariables.ts
+++ b/src/extensionVariables.ts
@@ -5,7 +5,10 @@
import { IAzExtOutputChannel, IExperimentationServiceAdapter } from "@microsoft/vscode-azext-utils";
import { AzureHostExtensionApi } from "@microsoft/vscode-azext-utils/hostapi";
+import { AzureResourcesApi } from "@microsoft/vscode-azext-utils/hostapi.v2";
import { ExtensionContext } from "vscode";
+import { ContainerAppsBranchDataProvider } from "./tree/ContainerAppsBranchDataProvider";
+import { TreeItemStateStore } from "./tree/TreeItemState";
/**
* Namespace for common variables used throughout the extension. They must be initialized in the activate() method of extension.ts
@@ -17,4 +20,8 @@ export namespace ext {
export const prefix: string = 'containerApps';
export let experimentationService: IExperimentationServiceAdapter;
export let rgApi: AzureHostExtensionApi;
+ export let rgApiV2: AzureResourcesApi;
+
+ export let state: TreeItemStateStore;
+ export let branchDataProvider: ContainerAppsBranchDataProvider;
}
diff --git a/src/getExtensionApi.ts b/src/getExtensionApi.ts
index 231689ddf..bb81fad20 100644
--- a/src/getExtensionApi.ts
+++ b/src/getExtensionApi.ts
@@ -3,28 +3,17 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
+import { getExtensionExports } from "@microsoft/vscode-azext-utils";
import { AzureExtensionApiProvider } from "@microsoft/vscode-azext-utils/api";
-import { AzureHostExtensionApi } from "@microsoft/vscode-azext-utils/hostapi";
-import { Extension, extensions } from "vscode";
+import { AzureResourcesApi } from "@microsoft/vscode-azext-utils/hostapi.v2";
import { localize } from "./utils/localize";
-export async function getApiExport(extensionId: string): Promise {
- const extension: Extension | undefined = extensions.getExtension(extensionId);
- if (extension) {
- if (!extension.isActive) {
- await extension.activate();
- }
-
- return extension.exports;
- }
-
- return undefined;
-}
-
-export async function getResourceGroupsApi(): Promise {
- const rgApiProvider = await getApiExport('ms-azuretools.vscode-azureresourcegroups');
+export async function getResourceGroupsApi(): Promise {
+ const rgApiProvider = await getExtensionExports('ms-azuretools.vscode-azureresourcegroups');
if (rgApiProvider) {
- return rgApiProvider.getApi('0.0.1');
+ return rgApiProvider.getApi('2.0.0', {
+ extensionId: 'ms-azuretools.vscode-azurecontainerapps',
+ });
} else {
throw new Error(localize('noResourceGroupExt', 'Could not find the Azure Resource Groups extension'));
}
diff --git a/src/tree/AzureAccountTreeItem.ts b/src/tree/AzureAccountTreeItem.ts
deleted file mode 100644
index 861096423..000000000
--- a/src/tree/AzureAccountTreeItem.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { AzureAccountTreeItemBase } from '@microsoft/vscode-azext-azureutils';
-import { ISubscriptionContext } from '@microsoft/vscode-azext-utils';
-import { SubscriptionTreeItem } from './SubscriptionTreeItem';
-
-export class AzureAccountTreeItem extends AzureAccountTreeItemBase {
- public constructor(testAccount?: {}) {
- super(undefined, testAccount);
- }
-
- public createSubscriptionTreeItem(root: ISubscriptionContext): SubscriptionTreeItem {
- return new SubscriptionTreeItem(this, root);
- }
-}
diff --git a/src/tree/ContainerAppItem.ts b/src/tree/ContainerAppItem.ts
new file mode 100644
index 000000000..4b2b0a7f1
--- /dev/null
+++ b/src/tree/ContainerAppItem.ts
@@ -0,0 +1,194 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { ContainerApp, ContainerAppsAPIClient, KnownActiveRevisionsMode } from "@azure/arm-appcontainers";
+import { getResourceGroupFromId, uiUtils } from "@microsoft/vscode-azext-azureutils";
+import { AzureWizard, callWithTelemetryAndErrorHandling, DeleteConfirmationStep, IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils";
+import { AzureSubscription, ViewPropertiesModel } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { EventEmitter, TreeItem, TreeItemCollapsibleState, Uri } from "vscode";
+import { DeleteAllContainerAppsStep } from "../commands/deleteContainerApp/DeleteAllContainerAppsStep";
+import { IDeleteContainerAppWizardContext } from "../commands/deleteContainerApp/IDeleteContainerAppWizardContext";
+import { IDeletable } from "../commands/deleteNode";
+import { ext } from "../extensionVariables";
+import { createActivityContext } from "../utils/activityUtils";
+import { createContainerAppsAPIClient, createContainerAppsClient } from "../utils/azureClients";
+import { createPortalUrl } from "../utils/createPortalUrl";
+import { localize } from "../utils/localize";
+import { treeUtils } from "../utils/treeUtils";
+import { ContainerAppsItem, createSubscriptionContext, TreeElementBase } from "./ContainerAppsBranchDataProvider";
+import { createDaprDisabledItem, DaprEnabledItem } from "./DaprItem";
+import { IngressDisabledItem, IngressItem } from "./IngressItem";
+import { LogsItem } from "./LogsItem";
+import { RevisionsItem } from "./RevisionsItem";
+import { ScaleItem } from "./scaling/ScaleItem";
+
+const refreshContainerAppEmitter = new EventEmitter();
+const refreshContainerAppEvent = refreshContainerAppEmitter.event;
+
+export function refreshContainerApp(id: string): void {
+ refreshContainerAppEmitter.fire(id);
+}
+
+export interface ContainerAppModel extends ContainerApp {
+ id: string;
+ name: string;
+ resourceGroup: string;
+ managedEnvironmentId: string;
+ revisionsMode: KnownActiveRevisionsMode;
+}
+
+export class ContainerAppItem implements ContainerAppsItem, IDeletable {
+ public static contextValue: string = 'containerApp';
+ public static contextValueRegExp: RegExp = new RegExp(ContainerAppItem.contextValue);
+
+ id: string;
+
+ private resourceGroup: string;
+ private name: string;
+
+
+ public get containerApp(): ContainerAppModel {
+ return this._containerApp;
+ }
+
+ constructor(public readonly subscription: AzureSubscription, private _containerApp: ContainerAppModel) {
+ this.id = this.containerApp.id;
+ this.resourceGroup = this.containerApp.resourceGroup;
+ this.name = this.containerApp.name;
+ refreshContainerAppEvent((id) => {
+ if (id === this.id) {
+ void this.refresh();
+ }
+ })
+ }
+
+ private async refresh(): Promise {
+ await callWithTelemetryAndErrorHandling('containerAppItem.refresh', async (context) => {
+ const client: ContainerAppsAPIClient = await createContainerAppsClient(context, this.subscription);
+ this._containerApp = ContainerAppItem.CreateContainerAppModel(await client.containerApps.get(this.resourceGroup, this.name));
+ ext.branchDataProvider.refresh(this);
+ });
+ }
+
+ viewProperties: ViewPropertiesModel = {
+ data: this.containerApp,
+ label: this.containerApp.name,
+ }
+
+ portalUrl: Uri = createPortalUrl(this.subscription, this.containerApp.id);
+
+ async getChildren(): Promise {
+
+ const result = await callWithTelemetryAndErrorHandling('getChildren', async (context) => {
+ const children: TreeElementBase[] = [];
+ children.push(this.containerApp.configuration?.dapr?.enabled ? new DaprEnabledItem(this.containerApp, this.containerApp.configuration.dapr) : createDaprDisabledItem(this.containerApp));
+
+ const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, createSubscriptionContext(this.subscription)]);
+ const revisionData = await client.containerAppsRevisions.getRevision(this.resourceGroup, this.name, nonNullProp(this.containerApp, 'latestRevisionName'));
+
+ if (this.containerApp.revisionsMode === KnownActiveRevisionsMode.Multiple) {
+ children.push(new RevisionsItem(this.subscription, this.containerApp));
+ } else {
+ children.push(new ScaleItem(this.subscription, this.containerApp, revisionData));
+ }
+
+ children.push(this.containerApp.configuration?.ingress ? new IngressItem(this.subscription, this.containerApp) : new IngressDisabledItem(this.subscription, this.containerApp));
+ children.push(new LogsItem(this.subscription, this.containerApp));
+
+ return children;
+ });
+
+ return result ?? [];
+ }
+
+ getTreeItem(): TreeItem {
+ return {
+ id: this.id,
+ label: nonNullProp(this.containerApp, 'name'),
+ iconPath: treeUtils.getIconPath('azure-containerapps'),
+ contextValue: `containerApp|revisionmode:${this.containerApp.revisionsMode}`,
+ description: this.containerApp.provisioningState === 'Succeeded' ? undefined : this.containerApp.provisioningState,
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ }
+ }
+
+ static async List(context: IActionContext, subscription: AzureSubscription, managedEnvironmentId: string): Promise {
+ const subContext = createSubscriptionContext(subscription);
+ const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, subContext]);
+ return (await uiUtils.listAllIterator(client.containerApps.listBySubscription()))
+ .filter(ca => ca.managedEnvironmentId && ca.managedEnvironmentId === managedEnvironmentId)
+ .map(ContainerAppItem.CreateContainerAppModel);
+ }
+
+ static async Get(context: IActionContext, subscription: AzureSubscription, resourceGroupName: string, containerAppName: string): Promise {
+ const client: ContainerAppsAPIClient = await createContainerAppsClient(context, subscription);
+ return ContainerAppItem.CreateContainerAppModel(await client.containerApps.get(resourceGroupName, containerAppName));
+ }
+
+ static CreateContainerAppModel(containerApp: ContainerApp): ContainerAppModel {
+ const revisionsMode = containerApp.configuration?.activeRevisionsMode as KnownActiveRevisionsMode ?? KnownActiveRevisionsMode.Single;
+ return {
+ id: nonNullProp(containerApp, 'id'),
+ name: nonNullProp(containerApp, 'name'),
+ managedEnvironmentId: nonNullProp(containerApp, 'managedEnvironmentId'),
+ resourceGroup: getResourceGroupFromId(nonNullProp(containerApp, 'id')),
+ revisionsMode,
+ ...containerApp,
+ }
+ }
+
+ async delete(context: IActionContext & { suppressPrompt?: boolean }): Promise {
+ const confirmMessage: string = localize('confirmDeleteContainerApp', 'Are you sure you want to delete container app "{0}"?', this.name);
+ const deleteContainerApp: string = localize('deleteContainerApp', 'Delete Container App "{0}"', this.name);
+
+ const wizardContext: IDeleteContainerAppWizardContext = {
+ activityTitle: deleteContainerApp,
+ containerAppNames: this.name,
+ subscription: createSubscriptionContext(this.subscription),
+ resourceGroupName: this.resourceGroup,
+ ...context,
+ ...(await createActivityContext())
+ };
+
+ const wizard: AzureWizard = new AzureWizard(wizardContext, {
+ promptSteps: [new DeleteConfirmationStep(confirmMessage)],
+ executeSteps: [new DeleteAllContainerAppsStep()]
+ });
+
+ if (!context.suppressPrompt) {
+ await wizard.prompt();
+ }
+
+ await ext.state.runWithTemporaryDescription(this.containerApp.id, localize('deleting', 'Deleting...'), async () => {
+ await wizard.execute();
+ });
+ ext.state.notifyChildrenChanged(this.containerApp.managedEnvironmentId);
+ }
+}
+
+export async function getContainerEnvelopeWithSecrets(context: IActionContext, subscription: AzureSubscription, containerApp: ContainerAppModel): Promise> {
+ // anytime you want to update the container app, you need to include the secrets but that is not retrieved by default
+ // make a deep copy, we don't want to modify the one that is cached
+ const containerAppEnvelope = JSON.parse(JSON.stringify(containerApp));
+
+ // verify all top-level properties
+ for (const key of Object.keys(containerAppEnvelope)) {
+ containerAppEnvelope[key] = nonNullProp(containerAppEnvelope, key);
+ }
+
+ const concreteContainerAppEnvelope = >containerAppEnvelope;
+ const webClient: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, createSubscriptionContext(subscription)]);
+
+ concreteContainerAppEnvelope.configuration.secrets = ((await webClient.containerApps.listSecrets(containerApp.resourceGroup, containerApp.name)).value);
+ concreteContainerAppEnvelope.configuration.registries ||= [];
+
+ return concreteContainerAppEnvelope;
+}
+
+export function isIngressEnabled(containerApp: ContainerApp): containerApp is IngressEnabledContainerApp {
+ return !!containerApp.configuration?.ingress?.fqdn;
+}
+
+type IngressEnabledContainerApp = ContainerApp & { configuration: { ingress: { fqdn: string } } };
diff --git a/src/tree/ContainerAppTreeItem.ts b/src/tree/ContainerAppTreeItem.ts
deleted file mode 100644
index 7880f89e0..000000000
--- a/src/tree/ContainerAppTreeItem.ts
+++ /dev/null
@@ -1,202 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { ContainerApp, ContainerAppsAPIClient } from "@azure/arm-appcontainers";
-import { getResourceGroupFromId } from "@microsoft/vscode-azext-azureutils";
-import { AzExtParentTreeItem, AzExtTreeItem, AzureWizard, DeleteConfirmationStep, IActionContext, TreeItemIconPath } from "@microsoft/vscode-azext-utils";
-import { DeleteAllContainerAppsStep } from "../commands/deleteContainerApp/DeleteAllContainerAppsStep";
-import { IDeleteContainerAppWizardContext } from "../commands/deleteContainerApp/IDeleteContainerAppWizardContext";
-import { azResourceContextValue, RevisionConstants } from "../constants";
-import { createActivityContext } from "../utils/activityUtils";
-import { createContainerAppsAPIClient } from "../utils/azureClients";
-import { localize } from "../utils/localize";
-import { nonNullProp } from "../utils/nonNull";
-import { openUrl } from "../utils/openUrl";
-import { treeUtils } from "../utils/treeUtils";
-import { DaprDisabledTreeItem, DaprEnabledTreeItem } from "./DaprTreeItem";
-import { IAzureResourceTreeItem } from './IAzureResourceTreeItem';
-import { IngressDisabledTreeItem, IngressTreeItem } from "./IngressTreeItem";
-import { LogsTreeItem } from "./LogsTreeItem";
-import { RevisionsTreeItem } from "./RevisionsTreeItem";
-import { RevisionTreeItem } from "./RevisionTreeItem";
-import { ScaleTreeItem } from "./ScaleTreeItem";
-
-export class ContainerAppTreeItem extends AzExtParentTreeItem implements IAzureResourceTreeItem {
- public static contextValue: string = 'containerApp';
- public static contextValueRegExp: RegExp = new RegExp(ContainerAppTreeItem.contextValue);
- public contextValue: string;
- public data: ContainerApp;
- public resourceGroupName: string;
-
- public name: string;
- public label: string;
- public childTypeLabel: string = localize('containerAppSetting', 'Container App setting');
- public managedEnvironmentId: string;
-
- public revisionsTreeItem: RevisionsTreeItem;
- public ingressTreeItem: IngressTreeItem | IngressDisabledTreeItem;
- public logTreeItem: LogsTreeItem;
- public scaleTreeItem: ScaleTreeItem;
-
- constructor(parent: AzExtParentTreeItem, ca: ContainerApp) {
- super(parent);
- this.data = ca;
-
- this.id = nonNullProp(this.data, 'id');
- this.resourceGroupName = getResourceGroupFromId(this.id);
-
- this.name = nonNullProp(this.data, 'name');
- this.label = this.name;
- this.managedEnvironmentId = nonNullProp(this.data, 'managedEnvironmentId');
-
- this.contextValue = `${ContainerAppTreeItem.contextValue}|${azResourceContextValue}|revisionmode:${this.getRevisionMode()}`;
- }
-
- public get iconPath(): TreeItemIconPath {
- return treeUtils.getIconPath('azure-containerapps');
- }
-
- public get description(): string | undefined {
- return this.data.provisioningState === 'Succeeded' ? undefined : this.data.provisioningState;
- }
-
- public async loadMoreChildrenImpl(_clearCache: boolean, context: IActionContext): Promise {
- const children: AzExtTreeItem[] = [];
- await this.updateChildren(context);
-
- if (this.data.configuration?.dapr?.enabled) {
- children.push(new DaprEnabledTreeItem(this, this.data.configuration?.dapr));
- } else {
- children.push(new DaprDisabledTreeItem(this));
- }
-
- if (this.getRevisionMode() === RevisionConstants.multiple.data) {
- children.push(this.revisionsTreeItem);
- } else {
- children.push(this.scaleTreeItem);
- }
-
- children.push(this.ingressTreeItem, this.logTreeItem)
- return children;
- }
-
- public hasMoreChildrenImpl(): boolean {
- return false;
- }
-
- public compareChildrenImpl(): number {
- return 0;
- }
-
- public async browse(): Promise {
- // make sure that ingress is enabled
- if (!this.ingressEnabled() || !this.data.configuration?.ingress?.fqdn) {
- throw new Error(localize('enableIngress', 'Enable ingress to perform this action.'));
- }
-
- await openUrl(`https://${this.data.configuration?.ingress?.fqdn}`);
- }
-
- public async deleteTreeItemImpl(context: IActionContext & { suppressPrompt?: boolean }): Promise {
- const confirmMessage: string = localize('confirmDeleteContainerApp', 'Are you sure you want to delete container app "{0}"?', this.name);
- const deleteContainerApp: string = localize('deleteContainerApp', 'Delete Container App "{0}"', this.name);
-
- const wizardContext: IDeleteContainerAppWizardContext = {
- activityTitle: deleteContainerApp,
- containerAppNames: this.name,
- subscription: this.subscription,
- resourceGroupName: this.resourceGroupName,
- ...context,
- ...(await createActivityContext())
- };
- const wizard: AzureWizard = new AzureWizard(wizardContext, {
- promptSteps: [new DeleteConfirmationStep(confirmMessage)],
- executeSteps: [new DeleteAllContainerAppsStep()]
- });
-
- if (!context.suppressPrompt) {
- await wizard.prompt();
- }
- await wizard.execute();
- }
-
- public async refreshImpl(context: IActionContext): Promise {
- const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, this]);
- const data = await client.containerApps.get(this.resourceGroupName, this.name);
-
- this.contextValue = `${ContainerAppTreeItem.contextValue}|${azResourceContextValue}|revisionmode:${this.getRevisionMode()}`;
- this.data = data;
-
- await this.updateChildren(context);
- }
-
- public async updateChildren(context: IActionContext): Promise {
- if (this.getRevisionMode() === RevisionConstants.multiple.data) {
- this.revisionsTreeItem = new RevisionsTreeItem(this);
- }
-
- const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, this]);
- const revisionData = await client.containerAppsRevisions.getRevision(this.resourceGroupName, this.name, nonNullProp(this.data, 'latestRevisionName'));
- this.scaleTreeItem = new ScaleTreeItem(this, revisionData.template?.scale);
-
- this.ingressTreeItem = this.data.configuration?.ingress ? new IngressTreeItem(this, this.data.configuration?.ingress) : new IngressDisabledTreeItem(this);
- this.logTreeItem = new LogsTreeItem(this);
- }
-
- public pickTreeItemImpl(expectedContextValues: (string | RegExp)[]): AzExtTreeItem | undefined {
- for (const expectedContextValue of expectedContextValues) {
- if (expectedContextValue instanceof RegExp) {
- if (expectedContextValue.test(ScaleTreeItem.contextValue)) {
- return this.getRevisionMode() === RevisionConstants.single.data ? this.scaleTreeItem : this.revisionsTreeItem;
- }
- } else {
- switch (expectedContextValue) {
- case RevisionTreeItem.contextValue:
- case RevisionsTreeItem.contextValue:
- return this.revisionsTreeItem;
- case IngressTreeItem.contextValue:
- case IngressDisabledTreeItem.contextValue:
- return this.ingressTreeItem;
- case LogsTreeItem.contextValue:
- return this.logTreeItem;
- case ScaleTreeItem.contextValue:
- return this.scaleTreeItem;
- default:
- }
- }
- }
-
- return undefined;
- }
-
- public async getContainerEnvelopeWithSecrets(context: IActionContext): Promise> {
- // anytime you want to update the container app, you need to include the secrets but that is not retrieved by default
- // make a deep copy, we don't want to modify the one that is cached
- const containerAppEnvelope = JSON.parse(JSON.stringify(this.data));
-
- // verify all top-level properties
- for (const key of Object.keys(containerAppEnvelope)) {
- containerAppEnvelope[key] = nonNullProp(containerAppEnvelope, key);
- }
-
- const concreteContainerAppEnvelope = >containerAppEnvelope;
- const webClient: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, this]);
-
- concreteContainerAppEnvelope.configuration.secrets = ((await webClient.containerApps.listSecrets(this.resourceGroupName, this.name)).value);
- concreteContainerAppEnvelope.configuration.registries ||= [];
-
- return concreteContainerAppEnvelope;
- }
-
- public getRevisionMode(): string {
- return this.data.configuration?.activeRevisionsMode?.toLowerCase() === 'single' ?
- RevisionConstants.single.data : RevisionConstants.multiple.data;
- }
-
- public ingressEnabled(): boolean {
- return !!this.data.configuration?.ingress;
- }
-}
-
diff --git a/src/tree/ContainerAppsBranchDataProvider.ts b/src/tree/ContainerAppsBranchDataProvider.ts
new file mode 100644
index 000000000..e07a5e21b
--- /dev/null
+++ b/src/tree/ContainerAppsBranchDataProvider.ts
@@ -0,0 +1,111 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { AzExtServiceClientCredentials, callWithTelemetryAndErrorHandling, IActionContext, ISubscriptionContext, nonNullProp } from '@microsoft/vscode-azext-utils';
+import { AzureResource, AzureResourceBranchDataProvider, AzureSubscription, ResourceModelBase, ViewPropertiesModel } from '@microsoft/vscode-azext-utils/hostapi.v2';
+import * as vscode from 'vscode';
+import { ext } from '../extensionVariables';
+import { localize } from '../utils/localize';
+import { ContainerAppModel } from './ContainerAppItem';
+import { ManagedEnvironmentItem } from './ManagedEnvironmentItem';
+
+export interface TreeElementBase extends ResourceModelBase {
+ getChildren?(): vscode.ProviderResult;
+ getTreeItem(): vscode.TreeItem | Thenable;
+
+ viewProperties?: ViewPropertiesModel;
+}
+
+export interface ContainerAppsItem extends TreeElementBase {
+ subscription: AzureSubscription;
+ containerApp: ContainerAppModel;
+}
+
+export class ContainerAppsBranchDataProvider extends vscode.Disposable implements AzureResourceBranchDataProvider {
+ private readonly onDidChangeTreeDataEmitter = new vscode.EventEmitter();
+
+ constructor() {
+ super(
+ () => {
+ this.onDidChangeTreeDataEmitter.dispose();
+ });
+ }
+
+ get onDidChangeTreeData(): vscode.Event {
+ return this.onDidChangeTreeDataEmitter.event;
+ }
+
+ async getChildren(element: TreeElementBase): Promise {
+ return (await element.getChildren?.())?.map((child) => {
+ if (child.id) {
+ return ext.state.wrapItemInStateHandling(child as TreeElementBase & { id: string }, () => this.refresh(child))
+ }
+ return child;
+ });
+ }
+
+ async getResourceItem(element: AzureResource): Promise {
+ const resourceItem = await callWithTelemetryAndErrorHandling(
+ 'getResourceItem',
+ async (context: IActionContext) => {
+ const managedEnvironment = await ManagedEnvironmentItem.Get(context, element.subscription, nonNullProp(element, 'resourceGroup'), element.name);
+ return new ManagedEnvironmentItem(element.subscription, element, managedEnvironment);
+ });
+
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ return ext.state.wrapItemInStateHandling(resourceItem!, () => this.refresh(resourceItem));
+ }
+
+ async getTreeItem(element: TreeElementBase): Promise {
+ const ti = await element.getTreeItem();
+ return ti;
+ }
+
+ refresh(element?: TreeElementBase): void {
+ this.onDidChangeTreeDataEmitter.fire(element);
+ }
+}
+
+export const branchDataProvider = new ContainerAppsBranchDataProvider();
+
+
+/**
+ * Converts a VS Code authentication session to an Azure Track 1 & 2 compatible compatible credential.
+ */
+export function createCredential(getSession: (scopes?: string[]) => vscode.ProviderResult): AzExtServiceClientCredentials {
+ return {
+ getToken: async (scopes?: string | string[]) => {
+ if (typeof scopes === 'string') {
+ scopes = [scopes];
+ }
+
+ const session = await getSession(scopes);
+
+ if (session) {
+ return {
+ token: session.accessToken
+ };
+ } else {
+ return null;
+ }
+ },
+ signRequest: async () => {
+ throw new Error((localize('signRequestError', 'Track 1 credentials are not (currently) supported.')));
+ }
+ };
+}
+
+/**
+ * Creates a subscription context from an application subscription.
+ */
+export function createSubscriptionContext(subscription: AzureSubscription): ISubscriptionContext {
+ return {
+ subscriptionDisplayName: '',
+ subscriptionPath: '',
+ userId: '',
+ ...subscription,
+ credentials: createCredential(subscription.authentication.getSession)
+ };
+}
diff --git a/src/tree/DaprItem.ts b/src/tree/DaprItem.ts
new file mode 100644
index 000000000..aeb5c2e70
--- /dev/null
+++ b/src/tree/DaprItem.ts
@@ -0,0 +1,77 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { ContainerApp, Dapr } from "@azure/arm-appcontainers";
+import { ViewPropertiesModel } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
+import { createGenericItem } from "../utils/GenericItem";
+import { localize } from "../utils/localize";
+import { treeUtils } from "../utils/treeUtils";
+import { ContainerAppModel } from "./ContainerAppItem";
+import { TreeElementBase } from "./ContainerAppsBranchDataProvider";
+
+export class DaprEnabledItem implements TreeElementBase {
+
+ id: string = `${this.containerApp.id}/DaprEnabled`;
+ constructor(private readonly containerApp: ContainerAppModel, private readonly dapr: Dapr) { }
+
+ viewProperties: ViewPropertiesModel = {
+ data: this.dapr,
+ label: localize('daprProperties', '{0} Dapr', this.containerApp.name),
+ }
+
+ getTreeItem(): TreeItem {
+ return {
+ id: this.id,
+ label: localize('dapr', 'Dapr'),
+ description: localize('enabled', 'Enabled'),
+ iconPath: treeUtils.getIconPath('dapr_logo'),
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ }
+ }
+
+ async getChildren(): Promise {
+ const children: TreeElementBase[] = [];
+
+ if (this.dapr.appId) {
+ children.push(createGenericItem({
+ contextValue: 'daprAppId',
+ description: 'app id',
+ iconPath: new ThemeIcon('dash'),
+ label: this.dapr.appId,
+ }));
+ }
+
+ if (this.dapr.appPort) {
+ children.push(createGenericItem({
+ contextValue: 'daprAppPort',
+ description: 'app port',
+ iconPath: new ThemeIcon('dash'),
+ label: String(this.dapr.appPort),
+ }))
+ }
+
+ if (this.dapr.appProtocol) {
+ children.push(createGenericItem({
+ description: 'app protocol',
+ label: String(this.dapr.appProtocol),
+ contextValue: 'daprAppProtocol',
+ iconPath: new ThemeIcon('dash'),
+ }));
+ }
+
+ return children;
+ }
+}
+
+export function createDaprDisabledItem(containerApp: ContainerApp): TreeElementBase {
+ return createGenericItem({
+ id: `${containerApp.id}/DaprDisabled`,
+ label: localize('dapr', 'Dapr'),
+ description: localize('disabled', 'Disabled'),
+ contextValue: 'dapr|disabled',
+ iconPath: new ThemeIcon('debug-disconnect'),
+ });
+}
diff --git a/src/tree/DaprTreeItem.ts b/src/tree/DaprTreeItem.ts
deleted file mode 100644
index b0dc939f7..000000000
--- a/src/tree/DaprTreeItem.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { Dapr } from "@azure/arm-appcontainers";
-import { AzExtParentTreeItem, AzExtTreeItem, GenericTreeItem, IActionContext, TreeItemIconPath } from "@microsoft/vscode-azext-utils";
-import { ThemeIcon } from "vscode";
-import { azResourceContextValue } from "../constants";
-import { localize } from "../utils/localize";
-import { treeUtils } from "../utils/treeUtils";
-import { ContainerAppTreeItem } from "./ContainerAppTreeItem";
-import { IAzureResourceTreeItem } from "./IAzureResourceTreeItem";
-
-export class DaprEnabledTreeItem extends AzExtParentTreeItem implements IAzureResourceTreeItem {
- public static contextValue: string = 'dapr|enabled';
- public readonly contextValue: string = `${DaprEnabledTreeItem.contextValue}|${azResourceContextValue}`;
- public readonly parent: ContainerAppTreeItem;
- public data: Dapr;
-
- public label: string;
-
- constructor(parent: ContainerAppTreeItem, data: Dapr | undefined) {
- super(parent);
- this.label = localize('dapr', 'Dapr');
- this.data = data || {};
- this.description = localize('enabled', 'Enabled');
- }
-
- public get iconPath(): TreeItemIconPath {
- return treeUtils.getIconPath('dapr_logo');
- }
-
- public async loadMoreChildrenImpl(_clearCache: boolean, _context: IActionContext): Promise {
- const children: AzExtTreeItem[] = [];
- this.data.appId ? children.push(new GenericTreeItem(this, { description: 'app id', label: this.data.appId, contextValue: 'daprAppId', iconPath: new ThemeIcon('dash') })) : undefined;
- this.data.appPort ? children.push(new GenericTreeItem(this, { description: 'app port', label: String(this.data.appPort), contextValue: 'daprAppPort', iconPath: new ThemeIcon('dash') })) : undefined;
- this.data.appProtocol ? children.push(new GenericTreeItem(this, { description: 'app protocol', label: String(this.data.appProtocol), contextValue: 'daprAppProtocol', iconPath: new ThemeIcon('dash') })) : undefined;
-
- return children;
- }
-
- public hasMoreChildrenImpl(): boolean {
- return false;
- }
-}
-
-export class DaprDisabledTreeItem extends AzExtTreeItem {
- public static contextValue: string = 'dapr|disabled';
- public readonly contextValue: string = DaprDisabledTreeItem.contextValue;
- public readonly parent: ContainerAppTreeItem;
-
- public label: string;
-
- constructor(parent: ContainerAppTreeItem) {
- super(parent);
- this.label = localize('dapr', 'Dapr');
- this.description = localize('disabled', 'Disabled');
- }
-
- public get iconPath(): TreeItemIconPath {
- return new ThemeIcon('debug-disconnect');
- }
-}
diff --git a/src/tree/IAzureResourceTreeItem.ts b/src/tree/IAzureResourceTreeItem.ts
deleted file mode 100644
index cb082c3ba..000000000
--- a/src/tree/IAzureResourceTreeItem.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { AzExtTreeItem } from "@microsoft/vscode-azext-utils";
-
-export interface IAzureResource {
- data: {} | undefined;
-
- /**
- * Implement this to execute any async code when data is undefined
- */
- getDataImpl?(): Promise;
-}
-
-export interface IAzureResourceTreeItem extends IAzureResource, AzExtTreeItem { }
diff --git a/src/tree/IngressItem.ts b/src/tree/IngressItem.ts
new file mode 100644
index 000000000..cf893b6be
--- /dev/null
+++ b/src/tree/IngressItem.ts
@@ -0,0 +1,90 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ContainerApp, Ingress } from "@azure/arm-appcontainers";
+import { AzureSubscription, ViewPropertiesModel } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
+import { azResourceContextValue, IngressConstants } from "../constants";
+import { createGenericItem } from "../utils/GenericItem";
+import { localize } from "../utils/localize";
+import { treeUtils } from "../utils/treeUtils";
+import { ContainerAppModel } from "./ContainerAppItem";
+import { ContainerAppsItem, TreeElementBase } from "./ContainerAppsBranchDataProvider";
+
+const label: string = localize('ingress', 'Ingress');
+
+export class IngressItem implements ContainerAppsItem {
+ static contextValue: string = 'ingress|enabled';
+ readonly contextValue: string = `${IngressItem.contextValue}|${azResourceContextValue}`;
+
+ constructor(readonly subscription: AzureSubscription, readonly containerApp: ContainerAppModel) { }
+
+ id: string = `${this.containerApp.id}/ingress`;
+
+ ingress: Ingress = this.containerApp.configuration?.ingress ?? {};
+
+ viewProperties: ViewPropertiesModel = {
+ data: this.ingress,
+ label: `${this.containerApp.name} ${label}`,
+ }
+
+ getTreeItem(): TreeItem {
+ return {
+ label,
+ contextValue: IngressItem.contextValue,
+ iconPath: treeUtils.getIconPath('10061-icon-Virtual Networks-Networking'),
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ };
+ }
+
+ async getChildren(): Promise {
+ const label: string = this.ingress.external ? IngressConstants.external : IngressConstants.internal;
+ const description: string = this.ingress.external ? IngressConstants.externalDesc : IngressConstants.internalDesc;
+
+ return [
+ createGenericItem({
+ contextValue: 'visibility',
+ description,
+ iconPath: new ThemeIcon('dash'),
+ label,
+ }),
+ createGenericItem({
+ contextValue: 'targetPort',
+ description: String(this.ingress.targetPort),
+ iconPath: new ThemeIcon('dash'),
+ label: localize('targetPort', 'Target Port'),
+ }),
+ ];
+ }
+}
+
+export class IngressDisabledItem implements TreeElementBase {
+ public static contextValue: string = 'ingress|disabled';
+ public readonly contextValue: string = IngressDisabledItem.contextValue;
+
+ constructor(public readonly subscription: AzureSubscription, public readonly containerApp: ContainerApp) { }
+
+ getTreeItem(): TreeItem {
+ return {
+ label,
+ description: localize('disabled', 'Disabled'),
+ contextValue: IngressDisabledItem.contextValue,
+ iconPath: new ThemeIcon('debug-disconnect'),
+ }
+ }
+}
+
+export function createTargetPortItem(subscription: AzureSubscription, containerApp: ContainerAppModel): ContainerAppsItem {
+ return {
+ subscription,
+ containerApp,
+ ...createGenericItem({
+ label: localize('targetPort', 'Target Port'),
+ contextValue: 'targetPort',
+ description: String(containerApp.configuration?.ingress?.targetPort),
+ iconPath: new ThemeIcon('dash'),
+ }),
+ };
+}
diff --git a/src/tree/IngressTreeItem.ts b/src/tree/IngressTreeItem.ts
deleted file mode 100644
index cba176ea1..000000000
--- a/src/tree/IngressTreeItem.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { Ingress } from "@azure/arm-appcontainers";
-import { AzExtParentTreeItem, AzExtTreeItem, GenericTreeItem, IActionContext, TreeItemIconPath } from "@microsoft/vscode-azext-utils";
-import { ThemeIcon } from "vscode";
-import { azResourceContextValue, IngressConstants } from "../constants";
-import { localize } from "../utils/localize";
-import { treeUtils } from "../utils/treeUtils";
-import { ContainerAppTreeItem } from "./ContainerAppTreeItem";
-import { IAzureResourceTreeItem } from "./IAzureResourceTreeItem";
-
-const label: string = localize('ingress', 'Ingress');
-
-export class IngressTreeItem extends AzExtParentTreeItem implements IAzureResourceTreeItem {
- public static contextValue: string = 'ingress|enabled';
- public readonly contextValue: string = `${IngressTreeItem.contextValue}|${azResourceContextValue}`;
- public readonly parent: ContainerAppTreeItem;
- public data: Ingress
-
- public label: string;
-
- constructor(parent: ContainerAppTreeItem, data?: Ingress) {
- super(parent);
- this.data = data || {};
- this.label = label;
- }
-
- public async loadMoreChildrenImpl(_clearCache: boolean, _context: IActionContext): Promise {
- const label: string = this.data.external ? IngressConstants.external : IngressConstants.internal;
- const description: string = this.data.external ? IngressConstants.externalDesc : IngressConstants.internalDesc;
-
- return [
- new GenericTreeItem(this, { label: localize('targetPort', 'Target Port'), contextValue: 'targetPort', description: String(this.data.targetPort), iconPath: new ThemeIcon('dash') }),
- new GenericTreeItem(this, { label, contextValue: 'visibility', description, iconPath: new ThemeIcon('dash') })
- ];
- }
-
- public hasMoreChildrenImpl(): boolean {
- return false;
- }
-
- public get iconPath(): TreeItemIconPath {
- return treeUtils.getIconPath('10061-icon-Virtual Networks-Networking');
- }
-}
-
-export class IngressDisabledTreeItem extends AzExtTreeItem {
- public static contextValue: string = 'ingress|disabled';
- public readonly contextValue: string = IngressDisabledTreeItem.contextValue;
- public readonly parent: ContainerAppTreeItem;
-
- public label: string;
-
- constructor(parent: ContainerAppTreeItem) {
- super(parent);
- this.label = label;
- this.description = localize('disabled', 'Disabled');
- }
-
- public get iconPath(): TreeItemIconPath {
- return new ThemeIcon('debug-disconnect');
- }
-}
diff --git a/src/tree/LogsItem.ts b/src/tree/LogsItem.ts
new file mode 100644
index 000000000..47cef21e1
--- /dev/null
+++ b/src/tree/LogsItem.ts
@@ -0,0 +1,54 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { ContainerApp } from "@azure/arm-appcontainers";
+import { AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
+import { createPortalUrl } from "../utils/createPortalUrl";
+import { createGenericItem } from "../utils/GenericItem";
+import { localize } from "../utils/localize";
+import { TreeElementBase } from "./ContainerAppsBranchDataProvider";
+
+export class LogsItem implements TreeElementBase {
+ constructor(private readonly subscription: AzureSubscription, private readonly containerApp: ContainerApp) { }
+ id: string = `${this.containerApp.id}/Logs`;
+
+ getTreeItem(): TreeItem {
+ return {
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ contextValue: 'logs',
+ iconPath: new ThemeIcon('book'),
+ id: this.id,
+ label: localize('logs', 'Logs'),
+ };
+ }
+
+ async getChildren(): Promise {
+ const iconPath = new ThemeIcon('link-external');
+ const openInPortal = 'azureResourceGroups.openInPortal';
+ return [
+ createGenericItem({
+ contextValue: 'openLogs',
+ commandId: openInPortal,
+ iconPath,
+ id: `${this.containerApp.id}/logs`,
+ label: localize('openLogs', 'Open Logs'),
+ commandArgs: [{
+ portalUrl: createPortalUrl(this.subscription, `${this.containerApp.id}/logs`),
+ }]
+ }),
+ createGenericItem({
+ contextValue: 'openLogStream',
+ commandId: openInPortal,
+ iconPath,
+ id: `${this.id}/logstream`,
+ label: localize('openLogStream', 'Open Log Stream'),
+ commandArgs: [{
+ portalUrl: createPortalUrl(this.subscription, `${this.containerApp.id}/logstream`),
+ }]
+ }),
+ ];
+ }
+}
diff --git a/src/tree/LogsTreeItem.ts b/src/tree/LogsTreeItem.ts
deleted file mode 100644
index d145e5d06..000000000
--- a/src/tree/LogsTreeItem.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { AzExtParentTreeItem, AzExtTreeItem, GenericTreeItem, TreeItemIconPath } from "@microsoft/vscode-azext-utils";
-import { ThemeIcon } from "vscode";
-import { localize } from "../utils/localize";
-import { ContainerAppTreeItem } from "./ContainerAppTreeItem";
-
-export class LogsTreeItem extends AzExtParentTreeItem {
- public static contextValue: string = 'log';
- public static openLogsContext: string = 'openLog';
- public readonly contextValue: string = LogsTreeItem.contextValue;
- public readonly parent: ContainerAppTreeItem;
-
- public label: string;
- public childTypeLabel: string = localize('logView', 'log view');
-
- constructor(parent: ContainerAppTreeItem) {
- super(parent);
- this.id = `${this.parent.id}/logParent`;
- this.label = localize('logs', 'Logs');
- }
-
- public get iconPath(): TreeItemIconPath {
- return new ThemeIcon('book');
- }
-
- public async loadMoreChildrenImpl(): Promise {
- const iconPath = new ThemeIcon('link-external');
- return [
- new GenericTreeItem(this, { label: 'Open Logs', contextValue: 'openLogs', commandId: 'containerApps.openInPortal', iconPath, id: `${this.parent.id}/logs` }),
- new GenericTreeItem(this, { label: 'Open Log Stream', contextValue: 'openLogStream', commandId: 'containerApps.openInPortal', iconPath, id: `${this.parent.id}/logstream` })
- ]
- }
-
- public hasMoreChildrenImpl(): boolean {
- return false;
- }
-}
diff --git a/src/tree/ManagedEnvironmentItem.ts b/src/tree/ManagedEnvironmentItem.ts
new file mode 100644
index 000000000..9990627c4
--- /dev/null
+++ b/src/tree/ManagedEnvironmentItem.ts
@@ -0,0 +1,78 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { ContainerAppsAPIClient, ManagedEnvironment, Resource } from "@azure/arm-appcontainers";
+import { getResourceGroupFromId, uiUtils } from "@microsoft/vscode-azext-azureutils";
+import { callWithTelemetryAndErrorHandling, IActionContext, nonNullProp } from "@microsoft/vscode-azext-utils";
+import { AzureResource, AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { TreeItem, TreeItemCollapsibleState } from "vscode";
+import { createContainerAppsAPIClient } from "../utils/azureClients";
+import { treeUtils } from "../utils/treeUtils";
+import { ContainerAppItem } from "./ContainerAppItem";
+import { ContainerAppsItem, createSubscriptionContext, TreeElementBase } from "./ContainerAppsBranchDataProvider";
+
+type ManagedEnvironmentModel = ManagedEnvironment & ResourceModel;
+
+export class ManagedEnvironmentItem implements TreeElementBase {
+
+ public static contextValue: string = 'containerEnvironment';
+ id: string;
+
+ constructor(public readonly subscription: AzureSubscription, public readonly resource: AzureResource, public readonly managedEnvironment: ManagedEnvironmentModel) {
+ this.id = managedEnvironment.id;
+ }
+
+ async getChildren(): Promise {
+
+ const result = await callWithTelemetryAndErrorHandling('getChildren', async (context) => {
+ const containerApps = await ContainerAppItem.List(context, this.subscription, this.id);
+ return containerApps.map(ca => new ContainerAppItem(this.subscription, ca));
+ });
+
+ return result ?? [];
+ }
+
+ getTreeItem(): TreeItem {
+ return {
+ label: this.managedEnvironment.name,
+ id: this.id,
+ iconPath: treeUtils.getIconPath('managedEnvironment'),
+ contextValue: ManagedEnvironmentItem.contextValue,
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ }
+ }
+
+ static async List(context: IActionContext, subscription: AzureSubscription): Promise {
+ const subContext = createSubscriptionContext(subscription);
+ const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, subContext]);
+ return await uiUtils.listAllIterator(client.managedEnvironments.listBySubscription());
+ }
+
+ static async Get(context: IActionContext, subscription: AzureSubscription, resourceGroup: string, name: string): Promise {
+ const subContext = createSubscriptionContext(subscription);
+ const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, subContext]);
+ return ManagedEnvironmentItem.CreateManagedEnvironmentModel(await client.managedEnvironments.get(resourceGroup, name));
+ }
+
+ private static CreateManagedEnvironmentModel(managedEnvironment: ManagedEnvironment): ManagedEnvironmentModel {
+ return createAzureResourceModel(managedEnvironment);
+ }
+}
+
+interface ResourceModel extends Resource {
+ id: string;
+ name: string;
+ resourceGroup: string;
+}
+
+function createAzureResourceModel(resource: T): T & ResourceModel {
+ const id = nonNullProp(resource, 'id');
+ return {
+ id,
+ name: nonNullProp(resource, 'name'),
+ resourceGroup: getResourceGroupFromId(id),
+ ...resource,
+ }
+}
diff --git a/src/tree/ManagedEnvironmentTreeItem.ts b/src/tree/ManagedEnvironmentTreeItem.ts
deleted file mode 100644
index db1110f78..000000000
--- a/src/tree/ManagedEnvironmentTreeItem.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { ManagedEnvironment } from "@azure/arm-appcontainers";
-import { AzExtParentTreeItem, AzExtTreeItem, IActionContext, ICreateChildImplContext } from "@microsoft/vscode-azext-utils";
-import { IAzureResourceTreeItem } from './IAzureResourceTreeItem';
-import { ResolvedContainerEnvironmentResource } from "./ResolvedContainerAppsResource";
-
-export class ManagedEnvironmentTreeItem extends AzExtParentTreeItem implements IAzureResourceTreeItem {
- public static contextValue: string = ResolvedContainerEnvironmentResource.contextValue;
- public static contextValueRegExp: RegExp = ResolvedContainerEnvironmentResource.contextValueRegExp;
- public readonly contextValue: string;
- public readonly data: ManagedEnvironment;
- public readonly childTypeLabel: string;
- public resourceGroupName: string;
-
- public resolved: ResolvedContainerEnvironmentResource;
-
- public name: string;
- public label: string;
-
- constructor(parent: AzExtParentTreeItem, resolvedContainerAppsResource: ResolvedContainerEnvironmentResource) {
- super(parent);
- this.resolved = resolvedContainerAppsResource;
- this.data = this.resolved.data;
-
- this.resourceGroupName = this.resolved.resourceGroupName;
- this.name = this.resolved.name;
- this.label = this.resolved.label;
- this.contextValue = this.resolved.resolvedContextValue;
- this.childTypeLabel = this.resolved.childTypeLabel;
- }
-
- public get id(): string {
- return this.resolved.id;
- }
-
- public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise {
- return await this.resolved.loadMoreChildrenImpl(clearCache, context);
- }
-
- public hasMoreChildrenImpl(): boolean {
- return this.resolved.hasMoreChildrenImpl();
- }
-
- public async createChildImpl(context: ICreateChildImplContext): Promise {
- return this.resolved.createChildImpl(context);
- }
-
- public async deleteTreeItemImpl(context: IActionContext & { suppressPrompt?: boolean }): Promise {
- return await this.resolved.deleteTreeItemImpl(context);
- }
-}
diff --git a/src/tree/ResolvedContainerAppsResource.ts b/src/tree/ResolvedContainerAppsResource.ts
deleted file mode 100644
index f8c42f6d7..000000000
--- a/src/tree/ResolvedContainerAppsResource.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { ContainerApp, ContainerAppsAPIClient, ManagedEnvironment } from "@azure/arm-appcontainers";
-import { getResourceGroupFromId, LocationListStep, uiUtils, VerifyProvidersStep } from "@microsoft/vscode-azext-azureutils";
-import { AzExtTreeItem, AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, IActionContext, ICreateChildImplContext, ISubscriptionContext } from "@microsoft/vscode-azext-utils";
-import { ResolvedAppResourceBase } from "@microsoft/vscode-azext-utils/hostapi";
-import { ContainerAppCreateStep } from "../commands/createContainerApp/ContainerAppCreateStep";
-import { ContainerAppNameStep } from "../commands/createContainerApp/ContainerAppNameStep";
-import { EnableIngressStep } from "../commands/createContainerApp/EnableIngressStep";
-import { EnvironmentVariablesListStep } from "../commands/createContainerApp/EnvironmentVariablesListStep";
-import { IContainerAppWithActivityContext } from "../commands/createContainerApp/IContainerAppContext";
-import { DeleteAllContainerAppsStep } from "../commands/deleteContainerApp/DeleteAllContainerAppsStep";
-import { DeleteEnvironmentConfirmationStep } from "../commands/deleteManagedEnvironment/DeleteEnvironmentConfirmationStep";
-import { DeleteManagedEnvironmentStep } from "../commands/deleteManagedEnvironment/DeleteManagedEnvironmentStep";
-import { IDeleteManagedEnvironmentWizardContext } from "../commands/deleteManagedEnvironment/IDeleteManagedEnvironmentWizardContext";
-import { ContainerRegistryListStep } from "../commands/deployImage/ContainerRegistryListStep";
-import { azResourceContextValue, webProvider } from "../constants";
-import { createActivityContext } from "../utils/activityUtils";
-import { createContainerAppsAPIClient } from "../utils/azureClients";
-import { localize } from "../utils/localize";
-import { nonNullProp } from "../utils/nonNull";
-import { ContainerAppTreeItem } from "./ContainerAppTreeItem";
-import { IAzureResource } from "./IAzureResourceTreeItem";
-import { ManagedEnvironmentTreeItem } from "./ManagedEnvironmentTreeItem";
-
-export class ResolvedContainerEnvironmentResource implements ResolvedAppResourceBase, IAzureResource {
- public static contextValue: string = 'containerEnvironment';
- public static contextValueRegExp: RegExp = new RegExp(ResolvedContainerEnvironmentResource.contextValue);
- public resolvedContextValue: string = `${ResolvedContainerEnvironmentResource.contextValue}|${azResourceContextValue}`;
- public contextValuesToAdd: string[] = [];
-
- public readonly data: ManagedEnvironment;
- public resourceGroupName: string;
- public readonly childTypeLabel: string = localize('containerApp', 'Container App');
-
- public name: string;
- public label: string;
-
- public constructor(private readonly _subscription: ISubscriptionContext, ke: ManagedEnvironment) {
- this.data = ke;
-
- this.resourceGroupName = getResourceGroupFromId(this.id);
- this.name = nonNullProp(this.data, 'name');
- this.label = this.name;
-
- this.contextValuesToAdd.push(this.resolvedContextValue);
- }
-
- public get id(): string {
- return nonNullProp(this.data, 'id');
- }
-
- public async loadMoreChildrenImpl(_clearCache: boolean, context: IActionContext): Promise {
- const proxyTree: ManagedEnvironmentTreeItem = this as unknown as ManagedEnvironmentTreeItem;
- const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, this._subscription]);
- // could be more efficient to call this once at Subscription level, and filter based off that but then risk stale data
- const containerApps: ContainerApp[] = (await uiUtils.listAllIterator(client.containerApps.listBySubscription()))
- .filter(ca => ca.managedEnvironmentId && ca.managedEnvironmentId === this.id)
-
- return await proxyTree.createTreeItemsWithErrorHandling(
- containerApps,
- 'invalidContainerApp',
- ca => new ContainerAppTreeItem(proxyTree, ca),
- ca => ca.name
- );
- }
-
- public hasMoreChildrenImpl(): boolean {
- return false;
- }
-
- public async createChildImpl(context: ICreateChildImplContext): Promise {
- const proxyTree: ManagedEnvironmentTreeItem = this as unknown as ManagedEnvironmentTreeItem;
- const wizardContext: IContainerAppWithActivityContext = {
- ...context, ...this._subscription, managedEnvironmentId: this.id, ...(await createActivityContext())
- };
-
- const title: string = localize('createContainerApp', 'Create Container App');
- const promptSteps: AzureWizardPromptStep[] =
- [new ContainerAppNameStep(), new ContainerRegistryListStep(), new EnvironmentVariablesListStep(), new EnableIngressStep()];
- const executeSteps: AzureWizardExecuteStep[] = [new VerifyProvidersStep([webProvider]), new ContainerAppCreateStep()];
-
- wizardContext.newResourceGroupName = this.resourceGroupName;
- await LocationListStep.setLocation(wizardContext, this.data.location);
-
- const wizard: AzureWizard = new AzureWizard(wizardContext, {
- title,
- promptSteps,
- executeSteps,
- showLoadingPrompt: true
- });
-
- await wizard.prompt();
- const newContainerAppName = nonNullProp(wizardContext, 'newContainerAppName');
- context.showCreatingTreeItem(newContainerAppName);
- wizardContext.activityTitle = localize('createNamedContainerApp', 'Create Container App "{0}"', newContainerAppName);
- try {
- await wizard.execute();
- } finally {
- // refresh this node even if create fails because container app provision failure throws an error, but still creates a container app
- await proxyTree.refresh(context);
- }
-
- return new ContainerAppTreeItem(proxyTree, nonNullProp(wizardContext, 'containerApp'));
- }
-
- public async deleteTreeItemImpl(context: IActionContext & { suppressPrompt?: boolean }): Promise {
- const proxyTree: ManagedEnvironmentTreeItem = this as unknown as ManagedEnvironmentTreeItem;
- const containerApps = (await proxyTree.loadAllChildren(context));
-
- const deleteManagedEnvironment: string = localize('deleteManagedEnvironment', 'Delete Container Apps environment "{0}"', proxyTree.name);
-
- const wizardContext: IDeleteManagedEnvironmentWizardContext = {
- activityTitle: deleteManagedEnvironment,
- containerAppNames: containerApps.map(ca => ca.name),
- managedEnvironmentName: proxyTree.name,
- resourceGroupName: proxyTree.resourceGroupName,
- subscription: proxyTree.subscription,
- ...context,
- ...(await createActivityContext())
- };
- const wizard: AzureWizard = new AzureWizard(wizardContext, {
- promptSteps: [new DeleteEnvironmentConfirmationStep()],
- executeSteps: [new DeleteAllContainerAppsStep(), new DeleteManagedEnvironmentStep()]
- });
-
- if (!context.suppressPrompt) {
- await wizard.prompt();
- }
- await wizard.execute();
- }
-}
diff --git a/src/tree/RevisionItem.ts b/src/tree/RevisionItem.ts
new file mode 100644
index 000000000..02cea131a
--- /dev/null
+++ b/src/tree/RevisionItem.ts
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { KnownRevisionProvisioningState, Revision } from "@azure/arm-appcontainers";
+import { nonNullProp, TreeItemIconPath } from "@microsoft/vscode-azext-utils";
+import { AzureSubscription, ViewPropertiesModel } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { ThemeColor, ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
+import { localize } from "../utils/localize";
+import { ContainerAppModel } from "./ContainerAppItem";
+import { ContainerAppsItem, TreeElementBase } from "./ContainerAppsBranchDataProvider";
+import { ScaleItem } from "./scaling/ScaleItem";
+
+export interface RevisionsItemModel extends ContainerAppsItem {
+ revision: Revision;
+}
+
+export class RevisionItem implements RevisionsItemModel {
+ id: string;
+
+ constructor(public readonly subscription: AzureSubscription, public readonly containerApp: ContainerAppModel, public readonly revision: Revision) {
+ this.id = nonNullProp(this.revision, 'id');
+ }
+
+ viewProperties: ViewPropertiesModel = {
+ data: this.revision,
+ label: nonNullProp(this.revision, 'name'),
+ }
+
+ async getChildren(): Promise {
+ return [new ScaleItem(this.subscription, this.containerApp, this.revision)];
+ }
+
+ getTreeItem(): TreeItem {
+ const description = !this.revision.active ?
+ localize('inactive', 'Inactive') :
+ this.revision.name === this.containerApp.latestRevisionName ?
+ localize('latest', 'Latest') :
+ undefined;
+
+ return {
+ id: this.id,
+ label: this.revision.name,
+ iconPath: this.iconPath,
+ description,
+ contextValue: 'revision',
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ }
+ }
+
+ private get iconPath(): TreeItemIconPath {
+ let id: string;
+ let colorId: string;
+
+ if (!this.revision.active) {
+ id = 'circle-slash';
+ colorId = 'testing.iconUnset';
+ } else {
+ switch (this.revision.provisioningState) {
+ case KnownRevisionProvisioningState.Deprovisioning:
+ case KnownRevisionProvisioningState.Provisioning:
+ id = 'play-circle';
+ colorId = 'testing.iconUnset';
+ break;
+ case KnownRevisionProvisioningState.Failed:
+ id = 'error';
+ colorId = 'testing.iconFailed';
+ break;
+ case KnownRevisionProvisioningState.Provisioned:
+ id = 'pass'
+ colorId = 'testing.iconPassed';
+ break;
+ case KnownRevisionProvisioningState.Deprovisioned:
+ default:
+ id = 'circle-slash';
+ colorId = 'testing.iconUnset';
+ }
+ }
+
+ return new ThemeIcon(id, colorId ? new ThemeColor(colorId) : undefined);
+ }
+}
diff --git a/src/tree/RevisionTreeItem.ts b/src/tree/RevisionTreeItem.ts
deleted file mode 100644
index 67485b1b8..000000000
--- a/src/tree/RevisionTreeItem.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { KnownRevisionProvisioningState, Revision } from "@azure/arm-appcontainers";
-import { AzExtParentTreeItem, AzExtTreeItem, IActionContext, TreeItemIconPath } from "@microsoft/vscode-azext-utils";
-import { ThemeColor, ThemeIcon } from "vscode";
-import { azResourceContextValue } from "../constants";
-import { localize } from "../utils/localize";
-import { nonNullProp } from "../utils/nonNull";
-import { IAzureResourceTreeItem } from './IAzureResourceTreeItem';
-import { RevisionsTreeItem } from "./RevisionsTreeItem";
-import { ScaleTreeItem } from "./ScaleTreeItem";
-
-export class RevisionTreeItem extends AzExtParentTreeItem implements IAzureResourceTreeItem {
- public static contextValue: string = 'revision';
- public readonly contextValue: string = `${RevisionTreeItem.contextValue}|${azResourceContextValue}`;
- public data: Revision;
- public readonly parent: RevisionsTreeItem;
-
- public scaleTreeItem: ScaleTreeItem;
-
- public name: string;
- public label: string;
-
- constructor(parent: RevisionsTreeItem, re: Revision) {
- super(parent);
- this.data = re;
-
- this.id = nonNullProp(this.data, 'id');
- this.name = nonNullProp(this.data, 'name');
- this.label = this.name;
- }
-
- public get description(): string | undefined {
- return !this.data.active ?
- localize('inactive', 'Inactive') :
- this.name === this.parent.parent.data.latestRevisionName ?
- localize('latest', 'Latest') :
- undefined;
- }
-
- public get iconPath(): TreeItemIconPath {
- let id: string;
- let colorId: string;
-
- if (!this.data.active) {
- id = 'circle-slash';
- colorId = 'testing.iconUnset';
- } else {
- switch (this.data.provisioningState) {
- case KnownRevisionProvisioningState.Deprovisioning:
- case KnownRevisionProvisioningState.Provisioning:
- id = 'play-circle';
- colorId = 'testing.iconUnset';
- break;
- case KnownRevisionProvisioningState.Failed:
- id = 'error';
- colorId = 'testing.iconFailed';
- break;
- case KnownRevisionProvisioningState.Provisioned:
- id = 'pass'
- colorId = 'testing.iconPassed';
- break;
- case KnownRevisionProvisioningState.Deprovisioned:
- default:
- id = 'circle-slash';
- colorId = 'testing.iconUnset';
- }
- }
-
- return new ThemeIcon(id, colorId ? new ThemeColor(colorId) : undefined);
- }
-
- public async refreshImpl(context: IActionContext): Promise {
- this.data = await this.parent.getRevision(context, this.name);
- }
-
- public async loadMoreChildrenImpl(_clearCache: boolean, _context: IActionContext): Promise {
- this.scaleTreeItem = new ScaleTreeItem(this, this.data.template?.scale);
- return [this.scaleTreeItem];
- }
-
- public pickTreeItemImpl(expectedContextValues: (string | RegExp)[]): AzExtTreeItem | undefined {
- for (const expectedContextValue of expectedContextValues) {
- if (expectedContextValue instanceof RegExp) {
- if (expectedContextValue.test(ScaleTreeItem.contextValue)) return this.scaleTreeItem;
- } else {
- switch (expectedContextValue) {
- case ScaleTreeItem.contextValue:
- return this.scaleTreeItem;
- default:
- }
- }
- }
-
- return undefined;
- }
-
- public hasMoreChildrenImpl(): boolean {
- return false;
- }
-}
diff --git a/src/tree/RevisionsItem.ts b/src/tree/RevisionsItem.ts
new file mode 100644
index 000000000..376fddef7
--- /dev/null
+++ b/src/tree/RevisionsItem.ts
@@ -0,0 +1,42 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { uiUtils } from "@microsoft/vscode-azext-azureutils";
+import { callWithTelemetryAndErrorHandling } from "@microsoft/vscode-azext-utils";
+import { AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { TreeItem, TreeItemCollapsibleState } from "vscode";
+import { createContainerAppsAPIClient } from "../utils/azureClients";
+import { localize } from "../utils/localize";
+import { treeUtils } from "../utils/treeUtils";
+import { ContainerAppModel } from "./ContainerAppItem";
+import { ContainerAppsItem, createSubscriptionContext } from "./ContainerAppsBranchDataProvider";
+import { RevisionItem } from "./RevisionItem";
+
+export class RevisionsItem implements ContainerAppsItem {
+ id: string;
+
+ constructor(public readonly subscription: AzureSubscription, public readonly containerApp: ContainerAppModel) {
+ this.id = `${containerApp.id}/Revisions`;
+ }
+
+ async getChildren(): Promise {
+ const result = await callWithTelemetryAndErrorHandling('getChildren', async (context) => {
+ const client = await createContainerAppsAPIClient([context, createSubscriptionContext(this.subscription)]);
+ const revisions = await uiUtils.listAllIterator(client.containerAppsRevisions.listRevisions(this.containerApp.resourceGroup, this.containerApp.name));
+ return revisions.map(revision => new RevisionItem(this.subscription, this.containerApp, revision));
+ });
+
+ return result?.reverse() ?? [];
+ }
+
+ getTreeItem(): TreeItem {
+ return {
+ label: localize('revisions', 'Revisions'),
+ iconPath: treeUtils.getIconPath('02885-icon-menu-Container-Revision-Active'),
+ contextValue: 'revisions',
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ }
+ }
+}
diff --git a/src/tree/RevisionsTreeItem.ts b/src/tree/RevisionsTreeItem.ts
deleted file mode 100644
index 0e099ba85..000000000
--- a/src/tree/RevisionsTreeItem.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { ContainerAppsAPIClient, Revision } from "@azure/arm-appcontainers";
-import { uiUtils } from "@microsoft/vscode-azext-azureutils";
-import { AzExtParentTreeItem, AzExtTreeItem, IActionContext, TreeItemIconPath } from "@microsoft/vscode-azext-utils";
-import { RevisionConstants } from "../constants";
-import { createContainerAppsAPIClient } from "../utils/azureClients";
-import { localize } from "../utils/localize";
-import { treeUtils } from "../utils/treeUtils";
-import { ContainerAppTreeItem } from "./ContainerAppTreeItem";
-import { RevisionTreeItem } from "./RevisionTreeItem";
-
-export class RevisionsTreeItem extends AzExtParentTreeItem {
- public static contextValue: string = 'revisions';
- public readonly contextValue: string = RevisionsTreeItem.contextValue;
- public readonly childTypeLabel: string = localize('revision', 'Revision');
- public readonly parent: ContainerAppTreeItem;
-
- public name: string;
- public label: string;
-
- constructor(parent: ContainerAppTreeItem) {
- super(parent);
- this.label = localize('revisons', 'Revisions');
- }
-
- public get iconPath(): TreeItemIconPath {
- return treeUtils.getIconPath('02885-icon-menu-Container-Revision-Active');
- }
-
- public get description(): string {
- return this.parent.data.configuration?.activeRevisionsMode?.toLowerCase() === 'single' ? RevisionConstants.single.label : RevisionConstants.multiple.label;
- }
-
- public async loadMoreChildrenImpl(_clearCache: boolean, context: IActionContext): Promise {
- const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, this]);
- const revisions: Revision[] = await uiUtils.listAllIterator(client.containerAppsRevisions.listRevisions(this.parent.resourceGroupName, this.parent.name));
-
- return await this.createTreeItemsWithErrorHandling(
- revisions,
- 'invalidRevision',
- re => new RevisionTreeItem(this, re),
- re => re.name
- );
- }
-
- public hasMoreChildrenImpl(): boolean {
- return false;
- }
-
- public async getRevision(context: IActionContext, name: string): Promise {
- const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, this]);
- return await client.containerAppsRevisions.getRevision(this.parent.resourceGroupName, this.parent.name, name);
- }
-}
diff --git a/src/tree/ScaleRuleGroupTreeItem.ts b/src/tree/ScaleRuleGroupTreeItem.ts
deleted file mode 100644
index 9de06959e..000000000
--- a/src/tree/ScaleRuleGroupTreeItem.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { ScaleRule } from "@azure/arm-appcontainers";
-import { AzExtParentTreeItem, AzExtTreeItem, AzureWizard, ICreateChildImplContext, IWizardOptions, nonNullProp, TreeItemIconPath } from "@microsoft/vscode-azext-utils";
-import { ThemeIcon } from "vscode";
-import { AddScaleRuleStep } from "../commands/scaling/addScaleRule/AddScaleRuleStep";
-import { IAddScaleRuleWizardContext } from "../commands/scaling/addScaleRule/IAddScaleRuleWizardContext";
-import { ScaleRuleNameStep } from "../commands/scaling/addScaleRule/ScaleRuleNameStep";
-import { ScaleRuleTypeStep } from "../commands/scaling/addScaleRule/ScaleRuleTypeStep";
-import { azResourceContextValue } from "../constants";
-import { localize } from "../utils/localize";
-import { treeUtils } from "../utils/treeUtils";
-import { ContainerAppTreeItem } from "./ContainerAppTreeItem";
-import { IAzureResourceTreeItem } from "./IAzureResourceTreeItem";
-import { ScaleRuleTreeItem } from "./ScaleRuleTreeItem";
-import { ScaleTreeItem } from "./ScaleTreeItem";
-
-export class ScaleRuleGroupTreeItem extends AzExtParentTreeItem implements IAzureResourceTreeItem {
- public static contextValue: string = 'scaleRules';
- public readonly contextValue: string = `${ScaleRuleGroupTreeItem.contextValue}|${azResourceContextValue}`;
- public readonly parent: ScaleTreeItem;
-
- public label: string;
- public data: ScaleRule[];
-
- constructor(parent: ScaleTreeItem, data: ScaleRule[]) {
- super(parent);
- this.label = localize('scaleRules', 'Scale Rules');
- this.data = data;
- }
-
- public get iconPath(): TreeItemIconPath {
- return new ThemeIcon('symbol-constant');
- }
-
- public async createChildImpl(context: ICreateChildImplContext): Promise {
- const scale: ScaleTreeItem = treeUtils.findNearestParent(this, ScaleTreeItem.contextValue);
- const containerApp: ContainerAppTreeItem = treeUtils.findNearestParent(this, ContainerAppTreeItem.contextValue);
-
- const title: string = localize('addScaleRuleTitle', 'Add Scale Rule');
- const wizardContext: IAddScaleRuleWizardContext = {
- ...context, containerApp, scale, scaleRuleGroup: this,
- };
- const wizardOptions: IWizardOptions = {
- title,
- promptSteps: [new ScaleRuleNameStep(), new ScaleRuleTypeStep()],
- executeSteps: [new AddScaleRuleStep()],
- showLoadingPrompt: true
- };
- const wizard: AzureWizard = new AzureWizard(wizardContext, wizardOptions);
- await wizard.prompt();
- context.showCreatingTreeItem(nonNullProp(wizardContext, 'ruleName'));
- await wizard.execute();
- return new ScaleRuleTreeItem(this, nonNullProp(wizardContext, "scaleRule"));
- }
-
- public async loadMoreChildrenImpl(): Promise {
- return this.createTreeItemsWithErrorHandling(
- this.data,
- 'invalidRule',
- rule => new ScaleRuleTreeItem(this, rule),
- _rule => localize('invalidScalingRule', 'Invalid Scaling Rule')
- );
- }
-
- public hasMoreChildrenImpl(): boolean {
- return false;
- }
-}
diff --git a/src/tree/ScaleRuleTreeItem.ts b/src/tree/ScaleRuleTreeItem.ts
deleted file mode 100644
index 6a4f1fe98..000000000
--- a/src/tree/ScaleRuleTreeItem.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { ScaleRule } from "@azure/arm-appcontainers";
-import { AzExtTreeItem, TreeItemIconPath } from "@microsoft/vscode-azext-utils";
-import { ThemeIcon } from "vscode";
-import { azResourceContextValue } from "../constants";
-import { localize } from "../utils/localize";
-import { nonNullProp } from "../utils/nonNull";
-import { IAzureResourceTreeItem } from "./IAzureResourceTreeItem";
-import { ScaleRuleGroupTreeItem } from "./ScaleRuleGroupTreeItem";
-
-export class ScaleRuleTreeItem extends AzExtTreeItem implements IAzureResourceTreeItem {
- public static contextValue: string = 'scaleRule';
- public readonly contextValue: string = `${ScaleRuleTreeItem.contextValue}|${azResourceContextValue}`;
- public readonly parent: ScaleRuleGroupTreeItem;
- public data: ScaleRule;
-
- public label: string;
-
- constructor(parent: ScaleRuleGroupTreeItem, data: ScaleRule) {
- super(parent);
- this.data = data;
- this.label = nonNullProp(data, 'name');
- }
-
- public get description(): string {
- if (this.data.http) return localize('http', "HTTP");
- else if (this.data.azureQueue) return localize('azureQueue', 'Azure Queue');
- else if (this.data.custom) return localize('custom', 'Custom');
- else return localize('unknown', 'Unknown');
- }
-
- public get iconPath(): TreeItemIconPath {
- return new ThemeIcon('dash');
- }
-}
diff --git a/src/tree/ScaleTreeItem.ts b/src/tree/ScaleTreeItem.ts
deleted file mode 100644
index f3a477201..000000000
--- a/src/tree/ScaleTreeItem.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { Scale } from "@azure/arm-appcontainers";
-import { AzExtParentTreeItem, AzExtTreeItem, GenericTreeItem, TreeItemIconPath } from "@microsoft/vscode-azext-utils";
-import { ThemeIcon } from "vscode";
-import { azResourceContextValue } from "../constants";
-import { localize } from "../utils/localize";
-import { treeUtils } from "../utils/treeUtils";
-import { ContainerAppTreeItem } from "./ContainerAppTreeItem";
-import { IAzureResourceTreeItem } from "./IAzureResourceTreeItem";
-import { RevisionTreeItem } from "./RevisionTreeItem";
-import { ScaleRuleGroupTreeItem } from "./ScaleRuleGroupTreeItem";
-
-export class ScaleTreeItem extends AzExtParentTreeItem implements IAzureResourceTreeItem {
- public static contextValue: string = 'scale';
- public readonly contextValue: string = `${ScaleTreeItem.contextValue}|${azResourceContextValue}`;
- public readonly parent: ContainerAppTreeItem | RevisionTreeItem;
- public data: Scale;
-
- public label: string;
- public minReplicas: string;
- public maxReplicas: string;
-
- constructor(parent: ContainerAppTreeItem | RevisionTreeItem, data: Scale | undefined) {
- super(parent);
- this.label = localize('scale', 'Scaling');
-
- this.data = data || {};
- this.minReplicas = String(this.data.minReplicas ?? 0);
- this.maxReplicas = String(this.data.maxReplicas ?? this.data.minReplicas ?? 0);
- }
-
- public get iconPath(): TreeItemIconPath {
- return treeUtils.getIconPath('02887-icon-menu-Container-Scale');
- }
-
- public async loadMoreChildrenImpl(): Promise {
- return [
- new GenericTreeItem(this, { label: localize('minMax', 'Min / max replicas'), description: `${this.minReplicas} / ${this.maxReplicas}`, contextValue: 'minMaxReplica', iconPath: new ThemeIcon('dash') }),
- new ScaleRuleGroupTreeItem(this, this.data.rules ?? [])]
- }
-
- public hasMoreChildrenImpl(): boolean {
- return false;
- }
-}
diff --git a/src/tree/SubscriptionTreeItem.ts b/src/tree/SubscriptionTreeItem.ts
deleted file mode 100644
index 4755bebbb..000000000
--- a/src/tree/SubscriptionTreeItem.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { ContainerAppsAPIClient, ManagedEnvironment } from "@azure/arm-appcontainers";
-import { LocationListStep, ResourceGroupCreateStep, SubscriptionTreeItemBase, uiUtils, VerifyProvidersStep } from "@microsoft/vscode-azext-azureutils";
-import { AzExtTreeItem, AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, IActionContext, ICreateChildImplContext } from '@microsoft/vscode-azext-utils';
-import { IManagedEnvironmentContext } from '../commands/createManagedEnvironment/IManagedEnvironmentContext';
-import { LogAnalyticsCreateStep } from '../commands/createManagedEnvironment/LogAnalyticsCreateStep';
-import { ManagedEnvironmentCreateStep } from '../commands/createManagedEnvironment/ManagedEnvironmentCreateStep';
-import { ManagedEnvironmentNameStep } from '../commands/createManagedEnvironment/ManagedEnvironmentNameStep';
-import { createActivityContext } from "../utils/activityUtils";
-import { createContainerAppsAPIClient } from '../utils/azureClients';
-import { localize } from '../utils/localize';
-import { nonNullProp } from '../utils/nonNull';
-import { ManagedEnvironmentTreeItem } from './ManagedEnvironmentTreeItem';
-import { ResolvedContainerEnvironmentResource } from "./ResolvedContainerAppsResource";
-
-export class SubscriptionTreeItem extends SubscriptionTreeItemBase {
- public readonly childTypeLabel: string = localize('ManagedEnvironment', 'Container Apps environment');
- private readonly _nextLink: string | undefined;
-
- public hasMoreChildrenImpl(): boolean {
- return !!this._nextLink;
- }
-
- public async loadMoreChildrenImpl(_clearCache: boolean, context: IActionContext): Promise {
- const client: ContainerAppsAPIClient = await createContainerAppsAPIClient([context, this]);
- const environments: ManagedEnvironment[] = await uiUtils.listAllIterator(client.managedEnvironments.listBySubscription());
-
- return await this.createTreeItemsWithErrorHandling(
- environments,
- 'invalidManagedEnvironment',
- ke => new ManagedEnvironmentTreeItem(this, new ResolvedContainerEnvironmentResource(this.subscription, ke)),
- ke => ke.name
- );
- }
-
- public static async createChild(context: ICreateChildImplContext, node: SubscriptionTreeItemBase): Promise {
- const wizardContext: IManagedEnvironmentContext = {
- ...context,
- ...node.subscription,
- ...(await createActivityContext())
- };
-
- const title: string = localize('createManagedEnv', 'Create Container Apps environment');
- const promptSteps: AzureWizardPromptStep[] = [];
- const executeSteps: AzureWizardExecuteStep[] = [];
-
- promptSteps.push(new ManagedEnvironmentNameStep());
- executeSteps.push(new VerifyProvidersStep(['Microsoft.App', 'Microsoft.OperationalInsights']), new ResourceGroupCreateStep(), new LogAnalyticsCreateStep(), new ManagedEnvironmentCreateStep());
- LocationListStep.addProviderForFiltering(wizardContext, 'Microsoft.App', 'managedEnvironments');
- LocationListStep.addStep(wizardContext, promptSteps);
-
- const wizard: AzureWizard = new AzureWizard(wizardContext, {
- title,
- promptSteps,
- executeSteps,
- showLoadingPrompt: true
- });
-
- await wizard.prompt();
- const newManagedEnvName = nonNullProp(wizardContext, 'newManagedEnvironmentName');
- wizardContext.newResourceGroupName = newManagedEnvName;
- wizardContext.activityTitle = localize('createNamedManagedEnv', 'Create Container Apps environment "{0}"', newManagedEnvName);
- await wizard.execute();
-
- const resolvedEnvironment = new ResolvedContainerEnvironmentResource(node.subscription, nonNullProp(wizardContext, 'managedEnvironment'));
- return new ManagedEnvironmentTreeItem(node, resolvedEnvironment);
- }
-}
diff --git a/src/tree/TreeItemState.ts b/src/tree/TreeItemState.ts
new file mode 100644
index 000000000..d8eb46f81
--- /dev/null
+++ b/src/tree/TreeItemState.ts
@@ -0,0 +1,141 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import * as vscode from 'vscode';
+import { createGenericItem } from '../utils/GenericItem';
+import { TreeElementBase } from './ContainerAppsBranchDataProvider';
+
+interface TreeItemState {
+ /**
+ * Apply a temporary description to the tree item
+ */
+ temporaryDescription?: string;
+ /**
+ * Set the tree item icon to a spinner
+ */
+ spinner?: boolean;
+ /**
+ * Temporary children to be displayed
+ */
+ temporaryChildren?: TreeElementBase[];
+}
+
+type ResourceGroupsItem = TreeElementBase & { id: string };
+
+export class TreeItemStateStore implements vscode.Disposable {
+ private readonly store: Record | undefined> = {};
+ private readonly disposables: vscode.Disposable[] = [];
+ private readonly onDidUpdateStateEmitter = new vscode.EventEmitter();
+ private readonly onDidUpdateStateEvent: vscode.Event = this.onDidUpdateStateEmitter.event;
+
+ /**
+ * Notify a resource that its children have changed.
+ */
+ notifyChildrenChanged(id: string): void {
+ this.onDidUpdateStateEmitter.fire(id);
+ }
+
+ wrapItemInStateHandling(item: ResourceGroupsItem, refresh: (item: ResourceGroupsItem) => void): ResourceGroupsItem {
+ const getTreeItem = item.getTreeItem.bind(item) as typeof item.getTreeItem;
+ item.getTreeItem = async () => {
+ const treeItem = await getTreeItem();
+ if (item.id) {
+ return this.applyToTreeItem({ ...treeItem, id: item.id });
+ }
+ return treeItem;
+ }
+
+ if (item.getChildren) {
+ const getChildren = item.getChildren.bind(item) as typeof item.getChildren;
+ item.getChildren = async () => {
+ const children = await getChildren() ?? [];
+
+ const state = this.getState(item.id);
+ if (state.temporaryChildren) {
+ children.unshift(...state.temporaryChildren);
+ }
+
+ return children;
+ }
+ }
+
+ this.onDidRequestRefresh(item.id, () => refresh(item));
+
+ return item;
+ }
+
+ dispose(): void {
+ this.disposables.forEach((disposable) => {
+ disposable.dispose();
+ });
+ }
+
+ async runWithTemporaryDescription(id: string, description: string, callback: () => Promise): Promise {
+ let result: T;
+ this.update(id, { ...this.getState(id), temporaryDescription: description, spinner: true });
+ try {
+ result = await callback();
+ } finally {
+ this.update(id, { ...this.getState(id), temporaryDescription: undefined, spinner: false });
+ }
+ return result;
+ }
+
+ private async runWithTemporaryChildren(id: string, child: TreeElementBase, callback: () => Promise): Promise {
+ let result: T;
+ this.update(id, { ...this.getState(id), temporaryChildren: [child] });
+ try {
+ result = await callback();
+ } finally {
+ this.update(id, { ...this.getState(id), temporaryChildren: undefined });
+ }
+ return result;
+ }
+
+ async showCreatingChild(id: string, label: string, callback: () => Promise): Promise {
+ return await this.runWithTemporaryChildren(id, createGenericItem({
+ iconPath: new vscode.ThemeIcon('loading~spin'),
+ label,
+ contextValue: 'creatingChild',
+ }), async () => {
+ return await callback();
+ });
+ }
+
+ private applyStateToTreeItem(state: Partial, treeItem: vscode.TreeItem): vscode.TreeItem {
+
+ if (state.temporaryDescription) {
+ treeItem.description = state.temporaryDescription;
+ }
+
+ if (state.spinner) {
+ treeItem.iconPath = new vscode.ThemeIcon('loading~spin');
+ }
+
+ return treeItem;
+ }
+
+ private onDidRequestRefresh(id: string, callback: () => void): void {
+ this.disposables.push(this.onDidUpdateStateEvent((eventId: string) => {
+ if (eventId === id) {
+ callback();
+ }
+ }));
+ }
+
+ private applyToTreeItem(treeItem: vscode.TreeItem & { id: string }): vscode.TreeItem {
+ const state = this.getState(treeItem.id);
+ return this.applyStateToTreeItem(state, { ...treeItem });
+ }
+
+ private getState(id: string): Partial {
+ return this.store[id] ?? {};
+ }
+
+ private update(id: string, state: Partial): void {
+ this.store[id] = { ...this.getState(id), ...state };
+ this.onDidUpdateStateEmitter.fire(id);
+ }
+}
diff --git a/src/tree/scaling/ScaleItem.ts b/src/tree/scaling/ScaleItem.ts
new file mode 100644
index 000000000..07268b6a5
--- /dev/null
+++ b/src/tree/scaling/ScaleItem.ts
@@ -0,0 +1,60 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { Revision, Scale } from "@azure/arm-appcontainers";
+import { nonNullValue } from "@microsoft/vscode-azext-utils";
+import { AzureSubscription, ViewPropertiesModel } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
+import { createGenericItem } from "../../utils/GenericItem";
+import { localize } from "../../utils/localize";
+import { treeUtils } from "../../utils/treeUtils";
+import { ContainerAppModel } from "../ContainerAppItem";
+import { ContainerAppsItem, TreeElementBase } from "../ContainerAppsBranchDataProvider";
+import { createScaleRuleGroupItem } from "./ScaleRuleGroupItem";
+
+export class ScaleItem implements ContainerAppsItem {
+ constructor(
+ public readonly subscription: AzureSubscription,
+ public readonly containerApp: ContainerAppModel,
+ public readonly revision: Revision,
+ ) { }
+
+ id: string = `${this.parentResource.id}/scale`;
+
+ viewProperties: ViewPropertiesModel = {
+ data: this.scale,
+ label: `${this.parentResource.name} Scaling`,
+ };
+
+ get scale(): Scale {
+ return nonNullValue(this.revision?.template?.scale);
+ }
+
+ get parentResource(): ContainerAppModel | Revision {
+ return this.revision?.name === this.containerApp.latestRevisionName ? this.containerApp : this.revision;
+ }
+
+ getTreeItem(): TreeItem {
+ return {
+ id: this.id,
+ label: localize('scaling', 'Scaling'),
+ contextValue: 'scale',
+ iconPath: treeUtils.getIconPath('02887-icon-menu-Container-Scale'),
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ }
+ }
+
+ async getChildren?(): Promise {
+ return [
+ createGenericItem({
+ label: localize('minMax', 'Min / max replicas'),
+ description: `${this.scale?.minReplicas ?? 0} / ${this.scale?.maxReplicas ?? 0}`,
+ contextValue: 'minMaxReplica',
+ iconPath: new ThemeIcon('dash'),
+ }),
+ createScaleRuleGroupItem(this.subscription, this.containerApp, this.revision),
+ ]
+ }
+}
diff --git a/src/tree/scaling/ScaleRuleGroupItem.ts b/src/tree/scaling/ScaleRuleGroupItem.ts
new file mode 100644
index 000000000..a5914d994
--- /dev/null
+++ b/src/tree/scaling/ScaleRuleGroupItem.ts
@@ -0,0 +1,45 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { Revision, ScaleRule } from "@azure/arm-appcontainers";
+import { nonNullValueAndProp } from "@microsoft/vscode-azext-utils";
+import { AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { ThemeIcon, TreeItemCollapsibleState } from "vscode";
+import { localize } from "../../utils/localize";
+import { ContainerAppModel } from "../ContainerAppItem";
+import { RevisionsItemModel } from "../RevisionItem";
+import { createScaleRuleItem } from "./ScaleRuleItem";
+
+export interface ScaleRuleGroupItem extends RevisionsItemModel {
+ scaleRules: ScaleRule[];
+}
+
+export function createScaleRuleGroupItem(subscription: AzureSubscription, containerApp: ContainerAppModel, revision: Revision): ScaleRuleGroupItem {
+ const scaleRules = nonNullValueAndProp(revision.template, 'scale').rules ?? [];
+ const parentResource = revision.name === containerApp.latestRevisionName ? containerApp : revision;
+ const id = `${parentResource.id}/scalerules`;
+
+ return {
+ id,
+ subscription,
+ containerApp,
+ scaleRules,
+ viewProperties: {
+ data: scaleRules,
+ label: `${parentResource.name} Scale Rules`,
+ },
+ revision,
+ getTreeItem: () => ({
+ id,
+ label: localize('scaleRules', 'Scale Rules'),
+ iconPath: new ThemeIcon('symbol-constant'),
+ contextValue: 'scaleRules',
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ }),
+ getChildren: async () => {
+ return scaleRules.map(scaleRule => createScaleRuleItem(subscription, containerApp, revision, scaleRule));
+ },
+ };
+}
diff --git a/src/tree/scaling/ScaleRuleItem.ts b/src/tree/scaling/ScaleRuleItem.ts
new file mode 100644
index 000000000..7854030d2
--- /dev/null
+++ b/src/tree/scaling/ScaleRuleItem.ts
@@ -0,0 +1,52 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { Revision, ScaleRule } from "@azure/arm-appcontainers";
+import { nonNullProp } from "@microsoft/vscode-azext-utils";
+import { AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { ThemeIcon, TreeItem } from "vscode";
+import { localize } from "../../utils/localize";
+import { ContainerAppModel } from "../ContainerAppItem";
+import { ContainerAppsItem } from "../ContainerAppsBranchDataProvider";
+
+export interface ScaleRuleItem extends ContainerAppsItem {
+ scaleRule: ScaleRule;
+}
+
+export function createScaleRuleItem(subscription: AzureSubscription, containerApp: ContainerAppModel, revision: Revision, scaleRule: ScaleRule): ScaleRuleItem {
+ const parentResource = revision.name === containerApp.latestRevisionName ? containerApp : revision;
+
+ const id = `${parentResource.id}/${scaleRule.name}`;
+
+ return {
+ id,
+ subscription,
+ containerApp,
+ scaleRule,
+ viewProperties: {
+ data: scaleRule,
+ label: `${parentResource.name} ${localize('scaleRule', 'Scale Rule')} ${scaleRule.name}`,
+ },
+ getTreeItem: (): TreeItem => ({
+ id,
+ label: nonNullProp(scaleRule, 'name'),
+ iconPath: new ThemeIcon('dash'),
+ contextValue: 'scaleRule',
+ description: getDescription(scaleRule),
+ }),
+ };
+}
+
+function getDescription(scaleRule: ScaleRule): string {
+ if (scaleRule.http) {
+ return localize('http', "HTTP");
+ } else if (scaleRule.azureQueue) {
+ return localize('azureQueue', 'Azure Queue');
+ } else if (scaleRule.custom) {
+ return localize('custom', 'Custom');
+ } else {
+ return localize('unknown', 'Unknown');
+ }
+}
diff --git a/src/utils/GenericItem.ts b/src/utils/GenericItem.ts
new file mode 100644
index 000000000..8dd6cb7b3
--- /dev/null
+++ b/src/utils/GenericItem.ts
@@ -0,0 +1,29 @@
+import { IGenericTreeItemOptions } from '@microsoft/vscode-azext-utils';
+import * as vscode from 'vscode';
+import { TreeElementBase } from '../tree/ContainerAppsBranchDataProvider';
+
+export interface GenericItemOptions extends IGenericTreeItemOptions {
+ commandArgs?: unknown[];
+}
+
+export function createGenericItem(options: GenericItemOptions): TreeElementBase {
+
+ let commandArgs = options.commandArgs;
+ const item = {
+ id: options.id,
+ getTreeItem(): vscode.TreeItem {
+ return {
+ ...options,
+ command: options.commandId ? {
+ title: '',
+ command: options.commandId,
+ arguments: commandArgs,
+ } : undefined,
+ }
+ }
+ };
+
+ commandArgs ??= [item];
+
+ return item;
+}
diff --git a/src/utils/activityUtils.ts b/src/utils/activityUtils.ts
index 5b59a3e93..95eff9fe7 100644
--- a/src/utils/activityUtils.ts
+++ b/src/utils/activityUtils.ts
@@ -9,7 +9,7 @@ import { settingUtils } from "../utils/settingUtils";
export async function createActivityContext(): Promise {
return {
- registerActivity: async (activity) => ext.rgApi.registerActivity(activity),
+ registerActivity: async (activity) => ext.rgApiV2.activity.registerActivity(activity),
suppressNotification: await settingUtils.getWorkspaceSetting('suppressActivityNotifications', undefined, 'azureResourceGroups'),
};
}
diff --git a/src/utils/azureClients.ts b/src/utils/azureClients.ts
index 43f90f2f3..92b979c83 100644
--- a/src/utils/azureClients.ts
+++ b/src/utils/azureClients.ts
@@ -8,10 +8,17 @@ import { ContainerRegistryManagementClient, ContainerRegistryManagementModels }
import { OperationalInsightsManagementClient } from '@azure/arm-operationalinsights';
import { ContainerRegistryClient, KnownContainerRegistryAudience } from '@azure/container-registry';
import { AzExtClientContext, createAzureClient, parseClientContext } from '@microsoft/vscode-azext-azureutils';
+import { IActionContext } from "@microsoft/vscode-azext-utils";
+import { AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import { createSubscriptionContext } from "../tree/ContainerAppsBranchDataProvider";
// Lazy-load @azure packages to improve startup performance.
// NOTE: The client is the only import that matters, the rest of the types disappear when compiled to JavaScript
+export async function createContainerAppsClient(context: IActionContext, subscription: AzureSubscription): Promise {
+ return createContainerAppsAPIClient([context, createSubscriptionContext(subscription)]);
+}
+
export async function createContainerAppsAPIClient(context: AzExtClientContext): Promise {
return createAzureClient(context, (await import('@azure/arm-appcontainers')).ContainerAppsAPIClient)
}
diff --git a/src/utils/createPortalUrl.ts b/src/utils/createPortalUrl.ts
new file mode 100644
index 000000000..93457bae5
--- /dev/null
+++ b/src/utils/createPortalUrl.ts
@@ -0,0 +1,16 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { OpenInPortalOptions } from "@microsoft/vscode-azext-azureutils";
+import { AzureSubscription } from "@microsoft/vscode-azext-utils/hostapi.v2";
+import * as vscode from 'vscode';
+
+// TODO move to shared package
+export function createPortalUrl(subscription: AzureSubscription, id: string, options?: OpenInPortalOptions): vscode.Uri {
+ const queryPrefix: string = (options && options.queryPrefix) ? `?${options.queryPrefix}` : '';
+ const url: string = `${subscription.environment.portalUrl}/${queryPrefix}#@${subscription.tenantId}/resource${id}`;
+
+ return vscode.Uri.parse(url);
+}
diff --git a/src/utils/pickContainerApp.ts b/src/utils/pickContainerApp.ts
new file mode 100644
index 000000000..ab0bc8447
--- /dev/null
+++ b/src/utils/pickContainerApp.ts
@@ -0,0 +1,15 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { AzExtResourceType, azureResourceExperience, IActionContext } from "@microsoft/vscode-azext-utils";
+import { ext } from "../extensionVariables";
+import { ContainerAppItem } from "../tree/ContainerAppItem";
+
+// TODO: support creating a new container app from picker
+export async function pickContainerApp(context: IActionContext): Promise {
+ return await azureResourceExperience(context, ext.rgApiV2.resources.azureResourceTreeDataProvider, AzExtResourceType.ContainerAppsEnvironment, {
+ include: ContainerAppItem.contextValueRegExp,
+ });
+}
diff --git a/src/utils/pickEnvironment.ts b/src/utils/pickEnvironment.ts
new file mode 100644
index 000000000..ccb7a4369
--- /dev/null
+++ b/src/utils/pickEnvironment.ts
@@ -0,0 +1,12 @@
+/*---------------------------------------------------------------------------------------------
+* Copyright (c) Microsoft Corporation. All rights reserved.
+* Licensed under the MIT License. See License.txt in the project root for license information.
+*--------------------------------------------------------------------------------------------*/
+
+import { AzExtResourceType, azureResourceExperience, IActionContext } from "@microsoft/vscode-azext-utils";
+import { ext } from "../extensionVariables";
+import { ManagedEnvironmentItem } from "../tree/ManagedEnvironmentItem";
+
+export async function pickEnvironment(context: IActionContext): Promise {
+ return await azureResourceExperience(context, ext.rgApiV2.resources.azureResourceTreeDataProvider, AzExtResourceType.ContainerAppsEnvironment);
+}