mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 11:01:37 -05:00
Compare commits
267 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22f384e569 | ||
|
|
d9ba75a1a5 | ||
|
|
d162d0a00f | ||
|
|
6579de5803 | ||
|
|
dc8e4414ff | ||
|
|
4d51ccb41e | ||
|
|
d05095ca82 | ||
|
|
ae31ba8b09 | ||
|
|
4cc1ddfc28 | ||
|
|
df557a8dbe | ||
|
|
7a9287be0b | ||
|
|
6ee5eb2104 | ||
|
|
6b6a8c422d | ||
|
|
d013b295b7 | ||
|
|
fb50a7dbb7 | ||
|
|
f70897cd4d | ||
|
|
c637b81d4d | ||
|
|
7d712e71eb | ||
|
|
f21709c09f | ||
|
|
2a4cb4ce08 | ||
|
|
c882aeddb0 | ||
|
|
fae37856fb | ||
|
|
666034b85c | ||
|
|
720f91b0ea | ||
|
|
644f5a16ef | ||
|
|
b5a9a3647b | ||
|
|
0f813a9900 | ||
|
|
5fef212ea3 | ||
|
|
d2d24e3827 | ||
|
|
07eb964c32 | ||
|
|
076ab355a0 | ||
|
|
aa350f7e49 | ||
|
|
0ac6f40559 | ||
|
|
19d77f02dd | ||
|
|
9ea3889170 | ||
|
|
1a5ae9cf32 | ||
|
|
1cc338c79b | ||
|
|
bb40a71166 | ||
|
|
1882c02ad6 | ||
|
|
18758b6748 | ||
|
|
b310e3eed4 | ||
|
|
375ce82b7d | ||
|
|
bd797ea6e1 | ||
|
|
eb3c5f1f47 | ||
|
|
20c290aa91 | ||
|
|
6172b4677f | ||
|
|
69d3552749 | ||
|
|
9b6018a21e | ||
|
|
da92875fea | ||
|
|
df9b0f7771 | ||
|
|
075b3cb35e | ||
|
|
8600cc3cd1 | ||
|
|
91ee26a888 | ||
|
|
20969bf244 | ||
|
|
9033ed5583 | ||
|
|
345c6b16e3 | ||
|
|
711923cd46 | ||
|
|
dacbc24143 | ||
|
|
e06e88cc0f | ||
|
|
d0ca3031e0 | ||
|
|
e4663b9778 | ||
|
|
35f7736b96 | ||
|
|
bf05ea69ef | ||
|
|
d6358b3e29 | ||
|
|
e69346e7fa | ||
|
|
eecffbf82d | ||
|
|
6684e227d6 | ||
|
|
4a5ac92e46 | ||
|
|
78b285e43a | ||
|
|
bc85f52dd4 | ||
|
|
2ca093f15f | ||
|
|
0413b95343 | ||
|
|
8550faaa73 | ||
|
|
555f8fbb1e | ||
|
|
45d41347ba | ||
|
|
7d4ee2d0b8 | ||
|
|
e675fc14f0 | ||
|
|
febfe3718f | ||
|
|
d48984fe13 | ||
|
|
73f00b63ce | ||
|
|
dbd1c1b5b3 | ||
|
|
4fd6a57afc | ||
|
|
23560a8744 | ||
|
|
1e8519d54a | ||
|
|
62b90c41ff | ||
|
|
d39e503788 | ||
|
|
8752ba434d | ||
|
|
7a8888f073 | ||
|
|
d5c5b3451a | ||
|
|
703f97a7bf | ||
|
|
a9166194cb | ||
|
|
8715f2bdb4 | ||
|
|
41e2767880 | ||
|
|
91cdd610fd | ||
|
|
d75cf2b657 | ||
|
|
f53119c2a6 | ||
|
|
290687a207 | ||
|
|
4ed1ef34b6 | ||
|
|
b7e0bbb64a | ||
|
|
85056fb1b7 | ||
|
|
fd282cd20b | ||
|
|
0354775107 | ||
|
|
872095ed84 | ||
|
|
169138e24b | ||
|
|
10c3199a23 | ||
|
|
c3e0478447 | ||
|
|
656a86a103 | ||
|
|
ee60319f70 | ||
|
|
a7f68ebd33 | ||
|
|
effdf4f538 | ||
|
|
004d55d98d | ||
|
|
00493d6555 | ||
|
|
29049bb15e | ||
|
|
7761c3b171 | ||
|
|
66edf059be | ||
|
|
22c3829225 | ||
|
|
b5ce7af090 | ||
|
|
6231df85e0 | ||
|
|
7c47aea06e | ||
|
|
e40c8dda25 | ||
|
|
9b841f4a49 | ||
|
|
4852de7b8e | ||
|
|
66053f5152 | ||
|
|
c5fc37c373 | ||
|
|
199f280586 | ||
|
|
7c6ae87fc8 | ||
|
|
f25c250547 | ||
|
|
32c7a4aeec | ||
|
|
8690c350d2 | ||
|
|
48d0aa72cc | ||
|
|
69616cd221 | ||
|
|
d6c35836cc | ||
|
|
f7fa9bece3 | ||
|
|
de5e81a157 | ||
|
|
498f278887 | ||
|
|
d9b24522e5 | ||
|
|
a683b1b777 | ||
|
|
447a9d0f34 | ||
|
|
4f6fe5955d | ||
|
|
fe25674401 | ||
|
|
71c12883fe | ||
|
|
d5384cad0e | ||
|
|
5e7446af6c | ||
|
|
385e4a2245 | ||
|
|
a754d235c0 | ||
|
|
f678e27515 | ||
|
|
cb58286247 | ||
|
|
93f5581bc7 | ||
|
|
97ff0694b2 | ||
|
|
882a7342e0 | ||
|
|
837236be0f | ||
|
|
12a3bf6b3b | ||
|
|
1d96476a81 | ||
|
|
2b4c608b93 | ||
|
|
3fb8d57d25 | ||
|
|
c75628639c | ||
|
|
514d599e62 | ||
|
|
319e0811f1 | ||
|
|
0e269e0438 | ||
|
|
37ad1b0fdb | ||
|
|
5b5705c03d | ||
|
|
f8d9106eef | ||
|
|
711bffb1f5 | ||
|
|
480d8e2cd0 | ||
|
|
99a924dbcd | ||
|
|
dfe84da196 | ||
|
|
af8316291e | ||
|
|
7150257218 | ||
|
|
61ddf297f9 | ||
|
|
66410edf02 | ||
|
|
e1b35d266a | ||
|
|
3036b53647 | ||
|
|
7efe4f9ce9 | ||
|
|
8de5efdf86 | ||
|
|
b3048213e8 | ||
|
|
2767ff819d | ||
|
|
6de89d9978 | ||
|
|
7de6b9a296 | ||
|
|
46fbb1c07f | ||
|
|
ac0dc4ee76 | ||
|
|
c13bd5c03f | ||
|
|
5d496ec83b | ||
|
|
a0b02fe16c | ||
|
|
45a346cfc1 | ||
|
|
9f5f49e956 | ||
|
|
8f638be687 | ||
|
|
95d4f130c6 | ||
|
|
f082ac2176 | ||
|
|
972312b3f5 | ||
|
|
1071c6dfff | ||
|
|
cce794dd14 | ||
|
|
a911044745 | ||
|
|
51da512edb | ||
|
|
95ce199d56 | ||
|
|
749f13d9bf | ||
|
|
51751557b6 | ||
|
|
7a0790f365 | ||
|
|
298402647d | ||
|
|
e1bbcb2ff1 | ||
|
|
fcece32cdd | ||
|
|
66bdc54c89 | ||
|
|
a38e8a74c3 | ||
|
|
ad69164f09 | ||
|
|
c33d2cc40a | ||
|
|
0cba2f980d | ||
|
|
72651819d6 | ||
|
|
f43afff8d1 | ||
|
|
e1e2227f2f | ||
|
|
d8c54ccd56 | ||
|
|
1e0e792cef | ||
|
|
e7f9763237 | ||
|
|
08fb04f966 | ||
|
|
895b43f36a | ||
|
|
b273cf5a65 | ||
|
|
75d455185a | ||
|
|
655588891c | ||
|
|
f2f3d1c4ef | ||
|
|
cf5572a7f1 | ||
|
|
c11b50ff59 | ||
|
|
f22840c942 | ||
|
|
889ed3584e | ||
|
|
81b7cd2612 | ||
|
|
065dfef1e4 | ||
|
|
5b3279065b | ||
|
|
4a061da9da | ||
|
|
17949b9d03 | ||
|
|
a63f43088e | ||
|
|
5157508dd5 | ||
|
|
0e0fade5d8 | ||
|
|
84fd46c901 | ||
|
|
ef240a9a63 | ||
|
|
427a859c63 | ||
|
|
6631f8e2d9 | ||
|
|
c970887d5e | ||
|
|
ccc8df31c7 | ||
|
|
ec4bc7f7db | ||
|
|
58136f7c35 | ||
|
|
03f6a9b188 | ||
|
|
9d4d5374d7 | ||
|
|
ac4aa8db9a | ||
|
|
205e683a4d | ||
|
|
ac4afbec6c | ||
|
|
cabe158f07 | ||
|
|
ce0710a4b4 | ||
|
|
9b25406dfb | ||
|
|
3e19c5769c | ||
|
|
9213325dc7 | ||
|
|
e2a7767b53 | ||
|
|
005b1ff35b | ||
|
|
c36ae27e9b | ||
|
|
b3acef3124 | ||
|
|
4971a94244 | ||
|
|
ee1ec8e759 | ||
|
|
aeb833578d | ||
|
|
99f342e98c | ||
|
|
1fc12876cd | ||
|
|
6d2a5cbd60 | ||
|
|
f74d6f6d9b | ||
|
|
395daddbcd | ||
|
|
097b92b94b | ||
|
|
3b31d1018f | ||
|
|
85eb7b8824 | ||
|
|
a03c7b7a61 | ||
|
|
9f0a88587b | ||
|
|
036163ac79 | ||
|
|
c5ea8aefa3 | ||
|
|
60147c1a86 |
@@ -770,7 +770,6 @@
|
||||
"chart.js",
|
||||
"plotly.js",
|
||||
"angular2-grid",
|
||||
"kburtram-query-plan",
|
||||
"html-to-image",
|
||||
"turndown",
|
||||
"gridstack",
|
||||
|
||||
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -7,12 +7,16 @@
|
||||
/extensions/azcli/ @Charles-Gagnon @swells @candiceye
|
||||
/extensions/azurecore/ @cssuh @cheenamalhotra
|
||||
/extensions/dacpac/ @kisantia
|
||||
/extensions/datavirtualization @Charles-Gagnon
|
||||
/extensions/import @aasimkhan30
|
||||
/extensions/machine-learning @llali
|
||||
/extensions/notebook @azure-data-studio-notebook-devs
|
||||
/extensions/query-history/ @Charles-Gagnon
|
||||
/extensions/resource-deployment/ @Charles-Gagnon
|
||||
/extensions/schema-compare/ @kisantia
|
||||
/extensions/sql-bindings/ @vasubhog @Charles-Gagnon @lucyzhang929 @chlafreniere @MaddyDev
|
||||
/extensions/sql-database-projects/ @Benjin @kisantia
|
||||
/extensions/sql-migration @AkshayMata @raymondtruong @brian-harris @junierch @siyangMicrosoft
|
||||
/extensions/mssql/config.json @Charles-Gagnon @alanrenmsft @kburtram
|
||||
|
||||
/src/sql/*.d.ts @alanrenmsft @Charles-Gagnon
|
||||
|
||||
82
CHANGELOG.md
82
CHANGELOG.md
@@ -1,5 +1,87 @@
|
||||
# Change Log
|
||||
|
||||
## Version 1.41.2
|
||||
* Release date: February 10, 2023
|
||||
* Release status: General Availability
|
||||
|
||||
### Bug fixes in 1.41.2
|
||||
|
||||
| New Item | Details |
|
||||
| --- | --- |
|
||||
| Connection | Fixed a regression blocking connections to sovereign Azure clouds |
|
||||
| Query Editor | Fixed a regression causing the Output window to display on each query execution |
|
||||
|
||||
## Version 1.41.1
|
||||
* Release date: January 30, 2023
|
||||
* Release status: General Availability
|
||||
|
||||
### Bug fixes in 1.41.1
|
||||
|
||||
| New Item | Details |
|
||||
| --- | --- |
|
||||
| Connection | Fixed a bug causing incorrect Azure account tenant selection when connecting to server through Azure view |
|
||||
| Object Explorer | Fixed a regression causing Object Explorer to not show database objects for Azure SQL DB Basic SLO |
|
||||
|
||||
## Version 1.41.0
|
||||
* Release date: January 25, 2023
|
||||
* Release status: General Availability
|
||||
|
||||
### What's new in 1.41.0
|
||||
|
||||
| New Item | Details |
|
||||
| --- | --- |
|
||||
| Azure Subscriptions | Introduced Azure Synapse Analytics and Dedicated SQL Pools nodes. |
|
||||
| Azure SQL Migration Extension | Premium series memory optimized SQL MI SKUs included in recommendations. |
|
||||
| Connection | Migrated Azure authentication library from ADAL to MSAL. MSAL is the library used by default starting with release 1.41. However, if you encounter issues, you can change back to ADAL within **Settings > Azure: Authentication Library**. |
|
||||
| Connection | Added ability to provide a description when creating a firewall rule from Azure Data Studio. |
|
||||
| Connection | Include ability to change password for new or expired login. |
|
||||
| Connection | Add support for SQL Server Alias use when connecting to a server. |
|
||||
| MongoDB Atlas Extension | Provides the ability to connect to and query data on MongoDB Atlas (Preview). |
|
||||
| Notebooks | Provide option for users to convert markdown to a table or not when HMTL table tag is present. |
|
||||
| Object Explorer | Databases are no longer brought online in serverless Azure SQL when Databases node is expanded. |
|
||||
| Object Explorer | Added support for Ledger views. |
|
||||
| Object Explorer | Fixed issue with key binding for objectExplorer.manage not working. |
|
||||
| Query Editor | Fixes and updates to SQL grammar (colorization and auto-complete). |
|
||||
| Query Plan Viewer | Changed default folder to be user’s home directory when saving a query plan. |
|
||||
| Query Results | Added ability to only copy Column Headers, and only for cells that are highlighted. |
|
||||
| Query Results | Added option to show or hide the action bar in the results window. |
|
||||
| Query Results | Increased height of horizontal scrollbar in results window. |
|
||||
| Query Results | Added new aggregate details in the results toolbar when selecting multiple cells. |
|
||||
| SQL Projects Extension | Provide the ability select an existing project via a new dropdown. |
|
||||
|
||||
### Bug fixes in 1.41.0
|
||||
|
||||
| New Item | Details |
|
||||
| --- | --- |
|
||||
| Accessibility | Accessibility improvements were made in the Query Plan Viewer, Query History Extension and Migration Extension. |
|
||||
| Big Data Cluster | Fix missing connect icon in BDC view header bar. |
|
||||
| Big Data Cluster | Fixed issue preventing HDFS nodes for BDC servers in Object Explorer from expanding. |
|
||||
| Connection | Added ability to delete a connection that has expired AAD credentials. |
|
||||
| Connection | Improved experience when Azure Active Directory token expiration occurs. |
|
||||
| Connection | Improved connection experience when using multiple Azure tenants. |
|
||||
| Connection | Addressed problem with adding a firewall exception for a non-default Azure subscription. |
|
||||
| Migration Extension | Added support for non-public clouds for migration scenarios. |
|
||||
| MySQL Extension | Updated resource endpoints to support AAD logins in the MySQL extension. |
|
||||
| Notebooks | Improve Intellisense refresh in Notebook cells. |
|
||||
| Notebooks | Address issue with "New Notebook Job" resulting in an empty form. |
|
||||
| Object Explorer | Fixed issue with database list not loading. |
|
||||
| Query Execution | Fixed error generated when executing a query with LEFT JOIN and NULL values. |
|
||||
| Query Plan Viewer | When saving query plans (.sqlplan file), the filename will numerically increment to prevent duplicate filenames. |
|
||||
| Query Results | Fixed issue where users were unable to open JSON data as a new file. |
|
||||
| Query Results | Provide proper cell selection and navigation in the query results grid. |
|
||||
| Query Results | Improved the handling of line breaks when copying cell contents. |
|
||||
| Query Results | Addressed issue where a column would re-size incorrectly when auto-sizing in the results output. |
|
||||
| Query Results | Improved JSON cell handling from query results. |
|
||||
| Query Results | Fixed behavior where focus was incorrectly set on a cell using keyboard navigation. |
|
||||
| Resource Deployment | Remove 'Preview' flag for SQL Server 2022 deployment types. |
|
||||
| Schema Compare Extension | Fixed problem where differences in schema compare were not being highlighted. |
|
||||
| Schema Compare Extension | Permissions are now included in schema compare when the "Include Permissions" option is selected. |
|
||||
| SQL Projects Extension | Changes to db_datawriter or db_datareader roles are now supported. |
|
||||
| SQL Projects Extension | Updated Database Projects Net Core SDK Location dialog to be more descriptive. |
|
||||
| Table Designer | Updated Table Designer to disable transaction support for Azure Synapse databases. |
|
||||
| Table Designer | Addressed problem of the table name not refreshing after being updated prior to publishing. |
|
||||
| Table Designer | Fixed issue where table designer could not be opened for existing Ledger tables. |
|
||||
|
||||
## Version 1.40.2
|
||||
* Release date: December 27, 2022
|
||||
* Release status: General Availability
|
||||
|
||||
31
README.md
31
README.md
@@ -9,24 +9,27 @@ Azure Data Studio is a data management tool that enables you to work with SQL Se
|
||||
## **Download the latest Azure Data Studio release**
|
||||
|
||||
|Platform |Type |Download |
|
||||
| --------|-----------------|-------- |
|
||||
|Windows |User Installer |[64 bit][win-user] |
|
||||
| |System Installer |[64 bit][win-system] |
|
||||
| |.zip |[64 bit][win-zip] |
|
||||
| --------|-----------------|----------------------- |
|
||||
|Windows |User Installer |[64 bit][win-user] [ARM][win-user-arm64] |
|
||||
| |System Installer |[64 bit][win-system] [ARM][win-system-arm64] |
|
||||
| |.zip |[64 bit][win-zip] [ARM][win-zip-arm64] |
|
||||
|Linux |.tar.gz |[64 bit][linux-zip] |
|
||||
| |.deb |[64 bit][linux-deb] |
|
||||
| |.rpm |[64 bit][linux-rpm] |
|
||||
|Mac |.zip |[Universal][osx-universal] [Intel Chip][osx-zip] [Apple Silicon][osx-arm64]|
|
||||
|Mac |.zip |[Universal][osx-universal] [Intel Chip][osx-zip] [Apple Silicon][osx-arm64] |
|
||||
|
||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2215273
|
||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2215525
|
||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2215526
|
||||
[osx-universal]: https://go.microsoft.com/fwlink/?linkid=2215527
|
||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2215420
|
||||
[osx-arm64]: https://go.microsoft.com/fwlink/?linkid=2215346
|
||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2215421
|
||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2215347
|
||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2215528
|
||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2222768
|
||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2222769
|
||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2223104
|
||||
[win-user-arm64]: https://go.microsoft.com/fwlink/?linkid=2222660
|
||||
[win-system-arm64]: https://go.microsoft.com/fwlink/?linkid=2222849
|
||||
[win-zip-arm64]: https://go.microsoft.com/fwlink/?linkid=2222850
|
||||
[osx-universal]: https://go.microsoft.com/fwlink/?linkid=2222873
|
||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2222874
|
||||
[osx-arm64]: https://go.microsoft.com/fwlink/?linkid=2222680
|
||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2222918
|
||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2223105
|
||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2222875
|
||||
|
||||
Go to our [download page](https://aka.ms/getazuredatastudio) for more specific instructions.
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ stages:
|
||||
variables:
|
||||
NPM_CONFIG_ARCH: arm64
|
||||
VSCODE_ARCH: arm64
|
||||
AZDATA_RUNTIME: OSX_ARM64
|
||||
# Do not run tests for arm64 build
|
||||
RUN_TESTS: false
|
||||
RUN_SMOKE_TESTS: false
|
||||
@@ -123,6 +124,7 @@ stages:
|
||||
- job: Windows_ARM64
|
||||
variables:
|
||||
VSCODE_ARCH: arm64
|
||||
AZDATA_RUNTIME: Windows_ARM64
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true'))
|
||||
steps:
|
||||
- template: win32/sql-product-build-win32.yml
|
||||
|
||||
@@ -28,17 +28,16 @@ async function main() {
|
||||
const outAppPath = path.join(buildDir, `azuredatastudio-darwin-${arch}`, appName); // {{SQL CARBON EDIT}} - CHANGE VSCode to azuredatastudio
|
||||
const productJsonPath = path.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json');
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Current STS arm64 builds doesn't work on osx-arm64, we need to use the x64 version of STS on osx-arm64 until the issue is fixed.
|
||||
// Tracked by: https://github.com/microsoft/azuredatastudio/issues/20775
|
||||
// makeUniversalApp function will complain if the x64 ADS and arm64 ADS have the same STS binaries, to workaround the issue, we need
|
||||
// to delete STS from both of them and then copy it to the universal app.
|
||||
// STS binaries for x64 and arm64 have different file count and cannot be combined
|
||||
// Remove them from the package before the makeUniversalApp step and copy them to the universal package after it.
|
||||
const stsPath = '/Contents/Resources/app/extensions/mssql/sqltoolsservice';
|
||||
const tempSTSDir = path.join(buildDir, 'sqltoolsservice');
|
||||
const x64STSDir = path.join(x64AppPath, stsPath);
|
||||
const arm64STSDir = path.join(arm64AppPath, stsPath);
|
||||
const targetSTSDirs = [x64STSDir, arm64STSDir];
|
||||
// backup the x64 STS to a temporary directory, later it will be copied to the universal app directory.
|
||||
// backup the STS folders to a temporary directory, later they will be copied to the universal app directory.
|
||||
await fs.copy(x64STSDir, tempSTSDir);
|
||||
await fs.copy(arm64STSDir, tempSTSDir);
|
||||
// delete STS directories from both x64 ADS and arm64 ADS.
|
||||
console.debug(`Removing SqlToolsService folders.`);
|
||||
targetSTSDirs.forEach(async (dir) => {
|
||||
@@ -89,7 +88,8 @@ async function main() {
|
||||
}
|
||||
// {{SQL CARBON EDIT}}
|
||||
console.debug(`Copying SqlToolsService to the universal app folder.`);
|
||||
await fs.copy(tempSTSDir, path.join(outAppPath, stsPath), { overwrite: true });
|
||||
await fs.copy(path.join(tempSTSDir, 'OSX'), path.join(outAppPath, stsPath, 'OSX'), { overwrite: true });
|
||||
await fs.copy(path.join(tempSTSDir, 'OSX_ARM64'), path.join(outAppPath, stsPath, 'OSX_ARM64'), { overwrite: true });
|
||||
}
|
||||
if (require.main === module) {
|
||||
main().catch(err => {
|
||||
|
||||
@@ -34,17 +34,16 @@ async function main() {
|
||||
const productJsonPath = path.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json');
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Current STS arm64 builds doesn't work on osx-arm64, we need to use the x64 version of STS on osx-arm64 until the issue is fixed.
|
||||
// Tracked by: https://github.com/microsoft/azuredatastudio/issues/20775
|
||||
// makeUniversalApp function will complain if the x64 ADS and arm64 ADS have the same STS binaries, to workaround the issue, we need
|
||||
// to delete STS from both of them and then copy it to the universal app.
|
||||
// STS binaries for x64 and arm64 have different file count and cannot be combined
|
||||
// Remove them from the package before the makeUniversalApp step and copy them to the universal package after it.
|
||||
const stsPath = '/Contents/Resources/app/extensions/mssql/sqltoolsservice';
|
||||
const tempSTSDir = path.join(buildDir, 'sqltoolsservice');
|
||||
const x64STSDir = path.join(x64AppPath, stsPath);
|
||||
const arm64STSDir = path.join(arm64AppPath, stsPath);
|
||||
const targetSTSDirs = [x64STSDir, arm64STSDir];
|
||||
// backup the x64 STS to a temporary directory, later it will be copied to the universal app directory.
|
||||
// backup the STS folders to a temporary directory, later they will be copied to the universal app directory.
|
||||
await fs.copy(x64STSDir, tempSTSDir);
|
||||
await fs.copy(arm64STSDir, tempSTSDir);
|
||||
// delete STS directories from both x64 ADS and arm64 ADS.
|
||||
console.debug(`Removing SqlToolsService folders.`);
|
||||
targetSTSDirs.forEach(async dir => {
|
||||
@@ -100,7 +99,8 @@ async function main() {
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
console.debug(`Copying SqlToolsService to the universal app folder.`);
|
||||
await fs.copy(tempSTSDir, path.join(outAppPath, stsPath), { overwrite: true });
|
||||
await fs.copy(path.join(tempSTSDir, 'OSX'), path.join(outAppPath, stsPath, 'OSX'), { overwrite: true });
|
||||
await fs.copy(path.join(tempSTSDir, 'OSX_ARM64'), path.join(outAppPath, stsPath, 'OSX_ARM64'), { overwrite: true });
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
@@ -47,6 +47,7 @@ module.exports.unicodeFilter = [
|
||||
'!build/win32/**',
|
||||
'!extensions/markdown-language-features/notebook-out/*.js',
|
||||
'!extensions/markdown-math/notebook-out/**',
|
||||
'!extensions/notebook-renderers/renderer-out/**',
|
||||
'!extensions/php-language-features/src/features/phpGlobalFunctions.ts',
|
||||
'!extensions/typescript-language-features/test-workspace/**',
|
||||
'!extensions/vscode-api-tests/testWorkspace/**',
|
||||
@@ -130,6 +131,7 @@ module.exports.indentationFilter = [
|
||||
'!extensions/markdown-language-features/media/*.js',
|
||||
'!extensions/markdown-language-features/notebook-out/*.js',
|
||||
'!extensions/markdown-math/notebook-out/*.js',
|
||||
'!extensions/notebook-renderers/renderer-out/*.js',
|
||||
'!extensions/simple-browser/media/*.js',
|
||||
|
||||
// {{SQL CARBON EDIT}} Except for our stuff
|
||||
|
||||
@@ -69,7 +69,6 @@ const compilations = [
|
||||
'vscode-api-tests/tsconfig.json',
|
||||
'vscode-colorize-tests/tsconfig.json',
|
||||
'vscode-custom-editor-tests/tsconfig.json',
|
||||
'vscode-notebook-tests/tsconfig.json',
|
||||
'vscode-test-resolver/tsconfig.json'
|
||||
];
|
||||
*/
|
||||
|
||||
@@ -125,8 +125,10 @@ const extensionsFilter = filter([
|
||||
'**/azcli.xlf',
|
||||
'**/azurecore.xlf',
|
||||
'**/azurehybridtoolkit.xlf',
|
||||
'**/azuremonitor.xlf',
|
||||
'**/cms.xlf',
|
||||
'**/dacpac.xlf',
|
||||
'**/datavirtualization.xlf',
|
||||
'**/git.xlf',
|
||||
'**/data-workspace.xlf',
|
||||
'**/import.xlf',
|
||||
|
||||
@@ -105,7 +105,7 @@ function buildWin32Setup(arch, target) {
|
||||
IncompatibleArchAppId: { 'ia32': x64AppId, 'x64': ia32AppId, 'arm64': ia32AppId }[arch],
|
||||
AppUserId: product.win32AppUserModelId,
|
||||
ArchitecturesAllowed: { 'ia32': '', 'x64': 'x64', 'arm64': 'arm64 x64' }[arch], //{{SQL CARBON EDIT}} - we still have x64 binaries in SqlToolsService, need to allow x64 binaries for arm64 arch.
|
||||
ArchitecturesInstallIn64BitMode: { 'ia32': '', 'x64': 'x64', 'arm64': 'arm64' }[arch],
|
||||
ArchitecturesInstallIn64BitMode: { 'ia32': '', 'x64': 'x64', 'arm64': 'arm64 x64' }[arch], //{{SQL CARBON EDIT}} - same as line above.
|
||||
SourceDir: sourcePath,
|
||||
RepoDir: repoPath,
|
||||
OutputDir: outputPath,
|
||||
|
||||
@@ -241,7 +241,6 @@ const excludedExtensions = [
|
||||
'vscode-test-resolver',
|
||||
'ms-vscode.node-debug',
|
||||
'ms-vscode.node-debug2',
|
||||
'vscode-notebook-tests',
|
||||
'vscode-custom-editor-tests',
|
||||
'integration-tests', // {{SQL CARBON EDIT}}
|
||||
];
|
||||
@@ -474,7 +473,7 @@ const esbuildMediaScripts = [
|
||||
'markdown-language-features/esbuild-notebook.js',
|
||||
'markdown-language-features/esbuild-preview.js',
|
||||
'markdown-math/esbuild.js',
|
||||
// 'notebook-renderers/esbuild.js', {{SQL CARBON EDIT}} We don't have this extension
|
||||
'notebook-renderers/esbuild.js',
|
||||
'simple-browser/esbuild-preview.js',
|
||||
];
|
||||
async function webpackExtensions(taskName, isWatch, webpackConfigLocations) {
|
||||
|
||||
@@ -285,7 +285,6 @@ const excludedExtensions = [
|
||||
'vscode-test-resolver',
|
||||
'ms-vscode.node-debug',
|
||||
'ms-vscode.node-debug2',
|
||||
'vscode-notebook-tests',
|
||||
'vscode-custom-editor-tests',
|
||||
'integration-tests', // {{SQL CARBON EDIT}}
|
||||
];
|
||||
@@ -572,7 +571,7 @@ const esbuildMediaScripts = [
|
||||
'markdown-language-features/esbuild-notebook.js',
|
||||
'markdown-language-features/esbuild-preview.js',
|
||||
'markdown-math/esbuild.js',
|
||||
// 'notebook-renderers/esbuild.js', {{SQL CARBON EDIT}} We don't have this extension
|
||||
'notebook-renderers/esbuild.js',
|
||||
'simple-browser/esbuild-preview.js',
|
||||
];
|
||||
|
||||
|
||||
@@ -169,35 +169,43 @@ const textFields = {
|
||||
const VSCODEExtensions = [
|
||||
"bat",
|
||||
"configuration-editing",
|
||||
"csharp",
|
||||
"dart",
|
||||
"docker",
|
||||
"git-ui",
|
||||
"fsharp",
|
||||
"git",
|
||||
"github-authentication",
|
||||
"git-base",
|
||||
"github",
|
||||
"github-authentication",
|
||||
"html",
|
||||
"image-preview",
|
||||
"json-language-features",
|
||||
"javascript",
|
||||
"json",
|
||||
"json-language-features",
|
||||
"julia",
|
||||
"markdown-basics",
|
||||
"markdown-language-features",
|
||||
"markdown-math",
|
||||
"merge-conflict",
|
||||
"microsoft-authentication",
|
||||
"notebook-renderers",
|
||||
"powershell",
|
||||
"python",
|
||||
"r",
|
||||
"search-result",
|
||||
"simple-browser",
|
||||
"sql",
|
||||
"theme-abyss",
|
||||
"theme-defaults",
|
||||
"theme-kimbie-dark",
|
||||
"theme-monokai-dimmed",
|
||||
"theme-monokai",
|
||||
"theme-monokai-dimmed",
|
||||
"theme-quietlight",
|
||||
"theme-red",
|
||||
"theme-seti",
|
||||
"theme-solarized-dark",
|
||||
"theme-solarized-light",
|
||||
"theme-tomorrow-night-blue",
|
||||
"typescript-basics",
|
||||
"xml",
|
||||
"yaml"
|
||||
];
|
||||
|
||||
@@ -187,35 +187,43 @@ const textFields = {
|
||||
const VSCODEExtensions = [
|
||||
"bat",
|
||||
"configuration-editing",
|
||||
"csharp",
|
||||
"dart",
|
||||
"docker",
|
||||
"git-ui",
|
||||
"fsharp",
|
||||
"git",
|
||||
"github-authentication",
|
||||
"git-base",
|
||||
"github",
|
||||
"github-authentication",
|
||||
"html",
|
||||
"image-preview",
|
||||
"json-language-features",
|
||||
"javascript",
|
||||
"json",
|
||||
"json-language-features",
|
||||
"julia",
|
||||
"markdown-basics",
|
||||
"markdown-language-features",
|
||||
"markdown-math",
|
||||
"merge-conflict",
|
||||
"microsoft-authentication",
|
||||
"notebook-renderers",
|
||||
"powershell",
|
||||
"python",
|
||||
"r",
|
||||
"search-result",
|
||||
"simple-browser",
|
||||
"sql",
|
||||
"theme-abyss",
|
||||
"theme-defaults",
|
||||
"theme-kimbie-dark",
|
||||
"theme-monokai-dimmed",
|
||||
"theme-monokai",
|
||||
"theme-monokai-dimmed",
|
||||
"theme-quietlight",
|
||||
"theme-red",
|
||||
"theme-seti",
|
||||
"theme-solarized-dark",
|
||||
"theme-solarized-light",
|
||||
"theme-tomorrow-night-blue",
|
||||
"typescript-basics",
|
||||
"xml",
|
||||
"yaml"
|
||||
];
|
||||
|
||||
@@ -29,6 +29,7 @@ exports.dirs = [
|
||||
'extensions/image-preview',
|
||||
'extensions/import',
|
||||
'extensions/integration-tests',
|
||||
'extensions/ipynb',
|
||||
'extensions/json-language-features',
|
||||
'extensions/json-language-features/server',
|
||||
'extensions/kusto',
|
||||
@@ -39,6 +40,7 @@ exports.dirs = [
|
||||
'extensions/microsoft-authentication',
|
||||
'extensions/mssql',
|
||||
'extensions/notebook',
|
||||
'extensions/notebook-renderers',
|
||||
'extensions/profiler',
|
||||
'extensions/python',
|
||||
'extensions/query-history',
|
||||
|
||||
@@ -226,9 +226,9 @@ has-unicode@^2.0.1:
|
||||
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
|
||||
|
||||
http-cache-semantics@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
|
||||
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
|
||||
|
||||
http-proxy-agent@^4.0.1:
|
||||
version "4.0.1"
|
||||
|
||||
@@ -1972,9 +1972,9 @@ htmlparser2@^8.0.1:
|
||||
entities "^4.3.0"
|
||||
|
||||
http-cache-semantics@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
|
||||
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
|
||||
|
||||
http-proxy-agent@^5.0.0:
|
||||
version "5.0.0"
|
||||
|
||||
@@ -23,7 +23,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -23,9 +23,13 @@ Both of these are available as menu items on the context menu for nodes in the O
|
||||
## Code of Conduct
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Telemetry
|
||||
|
||||
This extension collects telemetry data, which is used to help understand how to improve the product. For example, this usage data helps to debug issues, such as slow start-up times, and to prioritize new features. While we appreciate the insights this data provides, we also know that not everyone wants to send usage data and you can disable telemetry as described in the Azure Data Studio [disable telemetry reporting](https://github.com/Microsoft/azuredatastudio/wiki/How-to-Disable-Telemetry-Reporting#how-to-disable-telemetry-reporting) documentation.
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"downloadUrl": "https://sqlopsextensions.blob.core.windows.net/tools/ssmsmin/{#version#}/{#fileName#}",
|
||||
"version": "16.0.19061.0",
|
||||
"downloadFileNames": {
|
||||
"Windows_64": "SsmsMin-16.0.19061.0-win-x64.zip",
|
||||
"Windows_86": "SsmsMin-16.0.19061.0-win-x86.zip"
|
||||
"Windows_86": "SsmsMin-16.0.19061.0-win-x86.zip",
|
||||
"Windows": "SsmsMin-16.0.19061.0-win-x64.zip"
|
||||
},
|
||||
"installDirectory": "ssmsmin/{#platform#}/{#version#}",
|
||||
"executableFiles": [
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "admin-tool-ext-win",
|
||||
"displayName": "%adminToolExtWin.displayName%",
|
||||
"description": "%adminToolExtWin.description%",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/extensions/admin-tool-ext-win/license/Azure%20Data%20Studio%20Extension%20-%20Standalone%20(free)%20Use%20Terms.txt",
|
||||
@@ -107,8 +107,8 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/ads-extension-telemetry": "^1.3.2",
|
||||
"@microsoft/ads-service-downloader": "1.0.4",
|
||||
"@microsoft/ads-extension-telemetry": "^2.0.0",
|
||||
"@microsoft/ads-service-downloader": "^1.2.1",
|
||||
"vscode-nls": "^4.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -24,6 +24,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
|
||||
const ssmsMinVer = JSON.parse(rawConfig.toString()).version;
|
||||
exePath = path.join(context.extensionPath, 'ssmsmin', 'Windows', ssmsMinVer, 'ssmsmin.exe');
|
||||
registerCommands(context);
|
||||
context.subscriptions.push(TelemetryReporter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +52,7 @@ function registerCommands(context: vscode.ExtensionContext): void {
|
||||
*/
|
||||
async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||
if (!connectionContext) {
|
||||
TelemetryReporter.sendErrorEvent(TelemetryViews.SsmsMinProperties, 'NoConnectionContext');
|
||||
TelemetryReporter.sendErrorEvent2(TelemetryViews.SsmsMinProperties, 'NoConnectionContext');
|
||||
void vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForProp', "No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand"));
|
||||
return;
|
||||
}
|
||||
@@ -63,7 +64,7 @@ async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: az
|
||||
else if (connectionContext.nodeInfo) {
|
||||
nodeType = connectionContext.nodeInfo.nodeType;
|
||||
} else {
|
||||
TelemetryReporter.sendErrorEvent(TelemetryViews.SsmsMinProperties, 'NoOENode');
|
||||
TelemetryReporter.sendErrorEvent2(TelemetryViews.SsmsMinProperties, 'NoOENode');
|
||||
void vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', "Could not determine Object Explorer node from connectionContext : {0}", JSON.stringify(connectionContext)));
|
||||
return;
|
||||
}
|
||||
@@ -80,7 +81,7 @@ async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: az
|
||||
async function handleLaunchSsmsMinGswDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||
const action = 'GenerateScripts';
|
||||
if (!connectionContext) {
|
||||
TelemetryReporter.sendErrorEvent(TelemetryViews.SsmsMinGsw, 'NoConnectionContext');
|
||||
TelemetryReporter.sendErrorEvent2(TelemetryViews.SsmsMinGsw, 'NoConnectionContext');
|
||||
void vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForGsw', "No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand"));
|
||||
return;
|
||||
}
|
||||
@@ -97,7 +98,7 @@ async function handleLaunchSsmsMinGswDialogCommand(connectionContext?: azdata.Ob
|
||||
*/
|
||||
async function launchSsmsDialog(action: string, connectionContext: azdata.ObjectExplorerContext): Promise<void> {
|
||||
if (!connectionContext.connectionProfile) {
|
||||
TelemetryReporter.sendErrorEvent(TelemetryViews.SsmsMinDialog, 'NoConnectionProfile');
|
||||
TelemetryReporter.sendErrorEvent2(TelemetryViews.SsmsMinDialog, 'NoConnectionProfile');
|
||||
void vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionProfile', "No connectionProfile provided from connectionContext : {0}", JSON.stringify(connectionContext)));
|
||||
return;
|
||||
}
|
||||
@@ -111,7 +112,7 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object
|
||||
oeNode = await azdata.objectexplorer.getNode(connectionContext.connectionProfile.id, connectionContext.nodeInfo.nodePath);
|
||||
}
|
||||
else {
|
||||
TelemetryReporter.sendErrorEvent(TelemetryViews.SsmsMinDialog, 'NoOENode');
|
||||
TelemetryReporter.sendErrorEvent2(TelemetryViews.SsmsMinDialog, 'NoOENode');
|
||||
void vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', "Could not determine Object Explorer node from connectionContext : {0}", JSON.stringify(connectionContext)));
|
||||
return;
|
||||
}
|
||||
@@ -155,9 +156,10 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object
|
||||
runningProcesses.delete(proc.pid);
|
||||
const err = stderr.toString();
|
||||
if ((execException?.code !== 0) || err !== '') {
|
||||
TelemetryReporter.sendErrorEvent(
|
||||
TelemetryReporter.sendErrorEvent2(
|
||||
TelemetryViews.SsmsMinDialog,
|
||||
'LaunchSsmsDialogError',
|
||||
execException,
|
||||
execException ? execException?.code?.toString() : '',
|
||||
getTelemetryErrorType(err));
|
||||
}
|
||||
|
||||
@@ -182,58 +182,27 @@
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
|
||||
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
|
||||
|
||||
"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3":
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7"
|
||||
integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g==
|
||||
"@microsoft/ads-extension-telemetry@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-2.0.0.tgz#18ce267c7ed05c3b9dd99604a743e59f684c4e7c"
|
||||
integrity sha512-hExe/akhgq15v/h19LAFqiKNV6N9VxD19lOwGxEmO55yoWUm3E2cYealxvoYCwGDmSJfCbjR9fz/KM8Yz4XWAA==
|
||||
dependencies:
|
||||
"@microsoft/applicationinsights-core-js" "2.8.4"
|
||||
"@microsoft/applicationinsights-shims" "^2.0.1"
|
||||
"@microsoft/dynamicproto-js" "^1.1.6"
|
||||
"@vscode/extension-telemetry" "0.6.1"
|
||||
|
||||
"@microsoft/1ds-post-js@^3.2.3":
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216"
|
||||
integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ==
|
||||
dependencies:
|
||||
"@microsoft/1ds-core-js" "3.2.3"
|
||||
"@microsoft/applicationinsights-shims" "^2.0.1"
|
||||
"@microsoft/dynamicproto-js" "^1.1.6"
|
||||
|
||||
"@microsoft/ads-extension-telemetry@^1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-1.3.2.tgz#d9cfb4bc7099df73e000b7bafa48bb748db924fe"
|
||||
integrity sha512-TG1TE7FPp5rBA9zYPVjralZut8Bq/b5XCgm0kmkLyoQyn3c9ntmWXFuNQPOXmgbIemg5YY1/7DHKrfNcO/igkQ==
|
||||
dependencies:
|
||||
"@vscode/extension-telemetry" "^0.6.2"
|
||||
|
||||
"@microsoft/ads-service-downloader@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-service-downloader/-/ads-service-downloader-1.0.4.tgz#94e13461d655d0864cbf93978247cbd1097e7863"
|
||||
integrity sha512-XVJ3RW4X5mzlPYeJnwTii5/6ywVib4UqCtrvxwRWSFe214Hi8jO2zNxzcpamiTCnHm2b8wZAuWGfsvIShbf/yg==
|
||||
"@microsoft/ads-service-downloader@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-service-downloader/-/ads-service-downloader-1.2.1.tgz#b0216cb0486db6697ccf9e166ec81a9764bdb3aa"
|
||||
integrity sha512-xB3VUaEYauXtm3zFko5clHnhF7l7QbX0AnnULGDrd2JANu1zThBR6toUQ9+zAMO+0KCHs71XLSuoP2A24G3WCw==
|
||||
dependencies:
|
||||
async-retry "^1.2.3"
|
||||
eventemitter2 "^5.0.1"
|
||||
http-proxy-agent "^2.1.0"
|
||||
https-proxy-agent "^2.2.3"
|
||||
http-proxy-agent "^5.0.0"
|
||||
https-proxy-agent "^5.0.1"
|
||||
mkdirp "1.0.4"
|
||||
tar "^6.1.11"
|
||||
tmp "^0.0.33"
|
||||
yauzl "^2.10.0"
|
||||
|
||||
"@microsoft/applicationinsights-core-js@2.8.4":
|
||||
version "2.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596"
|
||||
integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ==
|
||||
dependencies:
|
||||
"@microsoft/applicationinsights-shims" "2.0.1"
|
||||
"@microsoft/dynamicproto-js" "^1.1.6"
|
||||
|
||||
"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd"
|
||||
integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ==
|
||||
|
||||
"@microsoft/azdata-test@^2.0.3":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/azdata-test/-/azdata-test-2.0.3.tgz#652984efa2f5adc56cdae9029a4d5f33446b54d3"
|
||||
@@ -246,11 +215,6 @@
|
||||
rimraf "^2.6.3"
|
||||
typemoq "^2.1.0"
|
||||
|
||||
"@microsoft/dynamicproto-js@^1.1.6":
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc"
|
||||
integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg==
|
||||
|
||||
"@microsoft/vscodetestcover@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/vscodetestcover/-/vscodetestcover-1.2.1.tgz#65f25132075a465a7a99688204486ee2b65ac07b"
|
||||
@@ -266,6 +230,11 @@
|
||||
istanbul-reports "^3.0.0"
|
||||
mocha "^7.1.1"
|
||||
|
||||
"@tootallnate/once@2":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
|
||||
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
|
||||
|
||||
"@types/mocha@^7.0.2":
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce"
|
||||
@@ -276,13 +245,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.7.tgz#01e4ea724d9e3bd50d90c11fd5980ba317d8fa11"
|
||||
integrity sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w==
|
||||
|
||||
"@vscode/extension-telemetry@^0.6.2":
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e"
|
||||
integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w==
|
||||
dependencies:
|
||||
"@microsoft/1ds-core-js" "^3.2.3"
|
||||
"@microsoft/1ds-post-js" "^3.2.3"
|
||||
"@vscode/extension-telemetry@0.6.1":
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.1.tgz#f8d1f7145baf932b75077c48107edff48501fc14"
|
||||
integrity sha512-Y4Oc8yGURGVF4WhCZcu+EVy+MAIeQDLDVeDlLn59H0C1w+7xr8dL2ZtDBioy+Hog1Edrd6zOwr3Na7xe1iC/UA==
|
||||
|
||||
agent-base@4, agent-base@^4.3.0:
|
||||
version "4.3.0"
|
||||
@@ -291,6 +257,13 @@ agent-base@4, agent-base@^4.3.0:
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
agent-base@6:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||
dependencies:
|
||||
debug "4"
|
||||
|
||||
ansi-colors@3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813"
|
||||
@@ -486,6 +459,13 @@ debug@3.2.6, debug@^3.1.0:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^2.2.0:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
@@ -806,7 +786,16 @@ http-proxy-agent@^2.1.0:
|
||||
agent-base "4"
|
||||
debug "3.1.0"
|
||||
|
||||
https-proxy-agent@^2.2.3, https-proxy-agent@^2.2.4:
|
||||
http-proxy-agent@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
|
||||
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
|
||||
dependencies:
|
||||
"@tootallnate/once" "2"
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
https-proxy-agent@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
||||
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
|
||||
@@ -814,6 +803,14 @@ https-proxy-agent@^2.2.3, https-proxy-agent@^2.2.4:
|
||||
agent-base "^4.3.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
https-proxy-agent@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
||||
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
|
||||
dependencies:
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
@@ -1178,7 +1175,7 @@ ms@2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
ms@^2.1.1:
|
||||
ms@2.1.2, ms@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
@@ -17,7 +17,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "arc",
|
||||
"displayName": "%arc.displayName%",
|
||||
"description": "%arc.description%",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.0",
|
||||
"publisher": "Microsoft",
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||
"icon": "images/extension.png",
|
||||
@@ -54,7 +54,7 @@
|
||||
{
|
||||
"command": "arc.connectToController",
|
||||
"title": "%command.connectToController.title%",
|
||||
"icon": "$(disconnect)"
|
||||
"icon": "$(plug)"
|
||||
},
|
||||
{
|
||||
"command": "arc.removeController",
|
||||
|
||||
@@ -190,3 +190,7 @@ export namespace cssStyles {
|
||||
}
|
||||
|
||||
export const iconSize = '20px';
|
||||
|
||||
export const encryptOption = 'encrypt';
|
||||
export const trustServerCertificateOption = 'trustServerCertificate';
|
||||
export const encryptReadMoreLink = 'https://learn.microsoft.com/sql/database-engine/configure-windows/enable-encrypted-connections-to-the-database-engine';
|
||||
|
||||
@@ -142,12 +142,21 @@ export const controllerPassword = localize('arc.controllerPassword', "Controller
|
||||
export const username = localize('arc.username', "Username");
|
||||
export const password = localize('arc.password', "Password");
|
||||
export const rememberPassword = localize('arc.rememberPassword', "Remember Password");
|
||||
export const encrypt = localize('arc.encrypt', "Encrypt");
|
||||
export const encryptDescription = localize('arc.encryptDescription', "When true, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed.");
|
||||
export const trustServerCertificate = localize('arc.trustServerCertificate', "Trust Server Certificate");
|
||||
export const trustServerCertDescription = localize('arc.trustServerCertDescription', "When true (and encrypt=true), SQL Server uses SSL encryption for all data sent between the client and server without validating the server certificate.");
|
||||
export const enableTrustServerCert = localize('arc.enableTrustServerCert', "Enable Trust Server Certificate");
|
||||
export const msgPromptSSLCertificateValidationFailed = localize('arc.msgPromptSSLCertificateValidationFailed', 'Encryption was enabled on this connection, review your SSL and certificate configuration for the target SQL Server, or set \'Trust server certificate\' to \'true\' in the settings file. Note: A self-signed certificate offers only limited protection and is not a recommended practice for production environments. Do you want to enable \'Trust server certificate\' on this connection and retry?');
|
||||
export const connect = localize('arc.connect', "Connect");
|
||||
export const readMore = localize('arc.readMore', "Read more");
|
||||
export const cancel = localize('arc.cancel', "Cancel");
|
||||
export const apply = localize('arc.apply', "Apply");
|
||||
export const ok = localize('arc.ok', "Ok");
|
||||
export const on = localize('arc.on', "On");
|
||||
export const off = localize('arc.off', "Off");
|
||||
export const booleantrue = localize('arc.booleantrue', "True");
|
||||
export const booleanfalse = localize('arc.booleanfalse', "False");
|
||||
export const notConfigured = localize('arc.notConfigured', "Not Configured");
|
||||
|
||||
// Database States - see https://docs.microsoft.com/sql/relational-databases/databases/database-states
|
||||
|
||||
@@ -219,7 +219,10 @@ export class MiaaModel extends ResourceModel {
|
||||
saveProfile: true,
|
||||
id: '',
|
||||
groupId: undefined,
|
||||
options: {}
|
||||
options: {
|
||||
encrypt: this._miaaInfo.encrypt || true,
|
||||
trustServerCertificate: this._miaaInfo.trustServerCertificate || false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -240,6 +243,8 @@ export class MiaaModel extends ResourceModel {
|
||||
this._activeConnectionId = connectionProfile.id;
|
||||
this.info.connectionId = connectionProfile.id;
|
||||
this._miaaInfo.userName = connectionProfile.userName;
|
||||
this._miaaInfo.encrypt = connectionProfile.options.encrypt;
|
||||
this._miaaInfo.trustServerCertificate = connectionProfile.options.trustServerCertificate;
|
||||
await this._treeDataProvider.saveControllers();
|
||||
}
|
||||
|
||||
@@ -270,6 +275,5 @@ export class MiaaModel extends ResourceModel {
|
||||
this._databaseTimeWindow.set(dbName, ['', '']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
4
extensions/arc/src/typings/arc.d.ts
vendored
4
extensions/arc/src/typings/arc.d.ts
vendored
@@ -20,7 +20,9 @@ declare module 'arc' {
|
||||
}
|
||||
|
||||
export type MiaaResourceInfo = ResourceInfo & {
|
||||
userName?: string
|
||||
userName?: string,
|
||||
encrypt?: string,
|
||||
trustServerCertificate?: boolean
|
||||
};
|
||||
|
||||
export type PGResourceInfo = ResourceInfo & {
|
||||
|
||||
@@ -8,11 +8,15 @@ import * as vscode from 'vscode';
|
||||
import { Deferred } from '../../common/promise';
|
||||
import * as loc from '../../localizedConstants';
|
||||
import { createCredentialId } from '../../common/utils';
|
||||
import { credentialNamespace } from '../../constants';
|
||||
import * as constants from '../../constants';
|
||||
import { InitializingComponent } from '../components/initializingComponent';
|
||||
import { ResourceModel } from '../../models/resourceModel';
|
||||
import { ControllerModel } from '../../models/controllerModel';
|
||||
|
||||
export interface IReconnectAction {
|
||||
(profile: azdata.IConnectionProfile): Promise<boolean>;
|
||||
}
|
||||
|
||||
export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
protected modelBuilder!: azdata.ModelBuilder;
|
||||
|
||||
@@ -20,6 +24,9 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
protected usernameInputBox!: azdata.InputBoxComponent;
|
||||
protected passwordInputBox!: azdata.InputBoxComponent;
|
||||
protected rememberPwCheckBox!: azdata.CheckBoxComponent;
|
||||
protected encryptSelectBox!: azdata.DropDownComponent;
|
||||
protected trustServerCertificateSelectBox!: azdata.DropDownComponent;
|
||||
|
||||
private options: { [name: string]: any } = {};
|
||||
|
||||
protected _completionPromise = new Deferred<azdata.IConnectionProfile | undefined>();
|
||||
@@ -29,7 +36,11 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
}
|
||||
|
||||
public showDialog(dialogTitle: string, connectionProfile?: azdata.IConnectionProfile): azdata.window.Dialog {
|
||||
const dialog = azdata.window.createModelViewDialog(dialogTitle);
|
||||
const dialog = azdata.window.createModelViewDialog(dialogTitle, undefined, 'narrow');
|
||||
const trueCategory: azdata.CategoryValue = { displayName: loc.booleantrue, name: 'true' }
|
||||
const falseCategory: azdata.CategoryValue = { displayName: loc.booleanfalse, name: 'false' }
|
||||
const booleanCategoryValues: azdata.CategoryValue[] = [trueCategory, falseCategory];
|
||||
|
||||
dialog.cancelButton.onClick(() => this.handleCancel());
|
||||
dialog.registerContent(async view => {
|
||||
this.modelBuilder = view.modelBuilder;
|
||||
@@ -47,13 +58,22 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
.withProps({
|
||||
inputType: 'password',
|
||||
value: connectionProfile?.password
|
||||
})
|
||||
.component();
|
||||
}).component();
|
||||
this.rememberPwCheckBox = this.modelBuilder.checkBox()
|
||||
.withProps({
|
||||
label: loc.rememberPassword,
|
||||
checked: connectionProfile?.savePassword
|
||||
}).component();
|
||||
this.encryptSelectBox = this.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
values: booleanCategoryValues,
|
||||
value: connectionProfile?.options[constants.encryptOption] ? trueCategory : falseCategory
|
||||
}).component();
|
||||
this.trustServerCertificateSelectBox = this.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
values: booleanCategoryValues,
|
||||
value: connectionProfile?.options[constants.trustServerCertificateOption] ? trueCategory : falseCategory
|
||||
}).component();
|
||||
|
||||
let formModel = this.modelBuilder.formContainer()
|
||||
.withFormItems([{
|
||||
@@ -73,6 +93,18 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
}, {
|
||||
component: this.rememberPwCheckBox,
|
||||
title: ''
|
||||
}, {
|
||||
component: this.encryptSelectBox,
|
||||
title: loc.encrypt,
|
||||
layout: {
|
||||
info: loc.encryptDescription,
|
||||
}
|
||||
}, {
|
||||
component: this.trustServerCertificateSelectBox,
|
||||
title: loc.trustServerCertificate,
|
||||
layout: {
|
||||
info: loc.trustServerCertDescription,
|
||||
}
|
||||
}
|
||||
],
|
||||
title: ''
|
||||
@@ -94,6 +126,10 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
if (!this.serverNameInputBox.value || !this.usernameInputBox.value || !this.passwordInputBox.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.options.encrypt = this.encryptSelectBox.value;
|
||||
this.options.trustServerCertificate = this.trustServerCertificateSelectBox.value;
|
||||
|
||||
const connectionProfile: azdata.IConnectionProfile = {
|
||||
serverName: this.serverNameInputBox.value,
|
||||
databaseName: '',
|
||||
@@ -109,10 +145,15 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
groupId: undefined,
|
||||
options: this.options
|
||||
};
|
||||
|
||||
return await this.connect(connectionProfile);
|
||||
}
|
||||
|
||||
private async connect(connectionProfile: azdata.IConnectionProfile): Promise<boolean> {
|
||||
const result = await azdata.connection.connect(connectionProfile, false, false);
|
||||
if (result.connected) {
|
||||
connectionProfile.id = result.connectionId!;
|
||||
const credentialProvider = await azdata.credentials.getProvider(credentialNamespace);
|
||||
const credentialProvider = await azdata.credentials.getProvider(constants.credentialNamespace);
|
||||
if (connectionProfile.savePassword) {
|
||||
await credentialProvider.saveCredential(createCredentialId(this._controllerModel.info.id, this._model.info.resourceType, this._model.info.name), connectionProfile.password);
|
||||
} else {
|
||||
@@ -123,10 +164,40 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
}
|
||||
else {
|
||||
vscode.window.showErrorMessage(this.connectionFailedMessage(result.errorMessage));
|
||||
return false;
|
||||
// Show error with instructions for MSSQL Provider Encryption error code -2146893019 thrown by SqlClient when certificate validation fails.
|
||||
if (result.errorCode === -2146893019) {
|
||||
return this.showInstructionTextAsWarning(connectionProfile, async updatedConnection => {
|
||||
return await this.connect(updatedConnection);
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async showInstructionTextAsWarning(profile: azdata.IConnectionProfile, reconnectAction: IReconnectAction): Promise<boolean> {
|
||||
while (true) {
|
||||
const selection = await vscode.window.showWarningMessage(
|
||||
loc.msgPromptSSLCertificateValidationFailed,
|
||||
{ modal: false },
|
||||
...[
|
||||
loc.enableTrustServerCert,
|
||||
loc.readMore,
|
||||
loc.cancel
|
||||
]);
|
||||
if (selection === loc.enableTrustServerCert) {
|
||||
profile.options.encrypt = true;
|
||||
profile.options.trustServerCertificate = true;
|
||||
return await reconnectAction(profile);
|
||||
} else if (selection === loc.readMore) {
|
||||
vscode.env.openExternal(vscode.Uri.parse(constants.encryptReadMoreLink));
|
||||
// Show the dialog again so the user can still pick yes or no after they've read the docs
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected abstract get providerName(): string;
|
||||
|
||||
protected abstract connectionFailedMessage(error: any): string;
|
||||
|
||||
@@ -110,10 +110,15 @@ export class ControllerTreeNode extends TreeNode {
|
||||
node = new PostgresTreeNode(postgresModel, this.model);
|
||||
break;
|
||||
case ResourceType.sqlManagedInstances:
|
||||
// Fill in the username too if we already have it
|
||||
(resourceInfo as MiaaResourceInfo).userName = (this.model.info.resources.find(info =>
|
||||
// Fill in the username and connection properties too if we already have them
|
||||
let miaaResourceInfo = this.model.info.resources.find(info =>
|
||||
info.name === resourceInfo.name &&
|
||||
info.resourceType === resourceInfo.resourceType) as MiaaResourceInfo)?.userName;
|
||||
info.resourceType === resourceInfo.resourceType) as MiaaResourceInfo;
|
||||
if (miaaResourceInfo) {
|
||||
(resourceInfo as MiaaResourceInfo).userName = miaaResourceInfo.userName;
|
||||
(resourceInfo as MiaaResourceInfo).encrypt = miaaResourceInfo.encrypt;
|
||||
(resourceInfo as MiaaResourceInfo).trustServerCertificate = miaaResourceInfo.trustServerCertificate;
|
||||
}
|
||||
const miaaModel = new MiaaModel(this.model, resourceInfo, registration, this._treeDataProvider);
|
||||
node = new MiaaTreeNode(miaaModel, this.model);
|
||||
break;
|
||||
|
||||
@@ -8,7 +8,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "azcli",
|
||||
"displayName": "%azcli.arc.displayName%",
|
||||
"description": "%azcli.arc.description%",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.0",
|
||||
"publisher": "Microsoft",
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||
"icon": "images/extension.png",
|
||||
|
||||
@@ -12,7 +12,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -4,19 +4,9 @@
|
||||
"default": false,
|
||||
"description": "%config.enableUsGovCloudDescription%"
|
||||
},
|
||||
"accounts.azure.enableUsNatCloud": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%config.enableUsNatCloudDescription%"
|
||||
},
|
||||
"accounts.azure.enableChinaCloud": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%config.enableChinaCloudDescription%"
|
||||
},
|
||||
"accounts.azure.enableGermanyCloud": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%config.enableGermanyCloudDescription%"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,16 +63,6 @@
|
||||
"default": false,
|
||||
"description": "%config.enableUsGovCloudDescription%"
|
||||
},
|
||||
"accounts.azure.cloud.enableUsNatCloud": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%config.enableUsNatCloudDescription%"
|
||||
},
|
||||
"accounts.azure.cloud.enableGermanyCloud": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%config.enableGermanyCloudDescription%"
|
||||
},
|
||||
"accounts.azure.cloud.enableChinaCloud": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
@@ -363,7 +353,6 @@
|
||||
"@azure/msal-node": "^1.9.0",
|
||||
"@azure/storage-blob": "^12.6.0",
|
||||
"axios": "^0.27.2",
|
||||
"crypto": "^1.0.1",
|
||||
"lockfile": "1.0.4",
|
||||
"msal": "^1.4.16",
|
||||
"node-fetch": "^2.6.7",
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import { Deferred } from '../interfaces';
|
||||
import * as url from 'url';
|
||||
import * as Constants from '../../constants';
|
||||
import { SimpleTokenCache } from '../simpleTokenCache';
|
||||
import { SimpleTokenCache } from '../utils/simpleTokenCache';
|
||||
import { MemoryDatabase } from '../utils/memoryDatabase';
|
||||
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { Logger } from '../../utils/Logger';
|
||||
@@ -145,14 +145,21 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
Logger.error(ex.originalMessageAndException);
|
||||
}
|
||||
} else {
|
||||
const message = ex.errorMessage || ex.message;
|
||||
if (message) {
|
||||
loginComplete?.reject(new AzureAuthError(message, message, undefined));
|
||||
return {
|
||||
canceled: false,
|
||||
errorCode: ex.errorCode,
|
||||
errorMessage: message
|
||||
};
|
||||
}
|
||||
Logger.error(ex);
|
||||
|
||||
}
|
||||
return {
|
||||
canceled: false
|
||||
};
|
||||
} finally {
|
||||
loginComplete?.reject(new AzureAuthError(localize('azureAuth.unidentifiedError', "Unidentified error with azure authentication"), 'Unidentified error with azure auth', undefined));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,8 +212,9 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
}
|
||||
|
||||
const resource = this.resources.find(s => s.azureResourceId === azureResource);
|
||||
|
||||
if (!resource) {
|
||||
Logger.error(`Unable to find Azure resource ${azureResource} for account ${account.displayInfo.userId} and tenant ${tenantId}`);
|
||||
Logger.error(`Unable to find Azure resource ${azureResource}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -293,7 +301,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
* re-authentication process for their tenant.
|
||||
*/
|
||||
public async refreshTokenAdal(tenant: Tenant, resource: Resource, refreshToken: RefreshToken | undefined): Promise<OAuthTokenResponse | undefined> {
|
||||
Logger.pii('Refreshing token', [{ name: 'token', objOrArray: refreshToken }], []);
|
||||
Logger.piiSanitized('Refreshing token', [{ name: 'token', objOrArray: refreshToken }], []);
|
||||
if (refreshToken) {
|
||||
const postData: RefreshTokenPostData = {
|
||||
grant_type: 'refresh_token',
|
||||
@@ -315,12 +323,14 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
* @param azureResource
|
||||
* @returns The authentication result, including the access token
|
||||
*/
|
||||
public async getTokenMsal(accountId: string, azureResource: azdata.AzureResource, tenantId: string): Promise<AuthenticationResult | null> {
|
||||
public async getTokenMsal(accountId: string, azureResource: azdata.AzureResource, tenantId: string): Promise<AuthenticationResult | azdata.PromptFailedResult | null> {
|
||||
const resource = this.resources.find(s => s.azureResourceId === azureResource);
|
||||
|
||||
if (!resource) {
|
||||
Logger.error(`Error: Could not fetch the azure resource ${azureResource} `);
|
||||
Logger.error(`Unable to find Azure resource ${azureResource}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Resource endpoint must end with '/' to form a valid scope for MSAL token request.
|
||||
const endpoint = resource.endpoint.endsWith('/') ? resource.endpoint : resource.endpoint + '/';
|
||||
|
||||
@@ -341,7 +351,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
// https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/3687
|
||||
const tokenRequest = {
|
||||
account: account,
|
||||
authority: `https://login.microsoftonline.com/${tenantId}`,
|
||||
authority: `${this.loginEndpointUrl}${tenantId}`,
|
||||
scopes: newScope,
|
||||
forceRefresh: true
|
||||
};
|
||||
@@ -356,11 +366,17 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
displayName: ''
|
||||
};
|
||||
return this.handleInteractionRequiredMsal(tenant, resource);
|
||||
} else if (e.name === 'ClientAuthError') {
|
||||
Logger.error(e.message);
|
||||
} else {
|
||||
if (e.name === 'ClientAuthError') {
|
||||
Logger.verbose('[ClientAuthError] Failed to silently acquire token');
|
||||
}
|
||||
return {
|
||||
canceled: false,
|
||||
name: e.name,
|
||||
errorCode: e.errorCode,
|
||||
errorMessage: e.errorMessage || e.message
|
||||
}
|
||||
}
|
||||
Logger.error('Failed to silently acquire token, not InteractionRequiredAuthError');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +402,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
const tokenUrl = `${this.loginEndpointUrl}${tenant.id}/oauth2/token`;
|
||||
const response = await this.makePostRequest(tokenUrl, postData);
|
||||
|
||||
Logger.pii('Token: ', [{ name: 'access token', objOrArray: response.data }, { name: 'refresh token', objOrArray: response.data }], []);
|
||||
Logger.piiSanitized('Token: ', [{ name: 'access token', objOrArray: response.data }, { name: 'refresh token', objOrArray: response.data }], []);
|
||||
if (response.data.error === 'interaction_required') {
|
||||
return this.handleInteractionRequiredAdal(tenant, resource);
|
||||
}
|
||||
@@ -537,13 +553,13 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
private async saveTokenAdal(tenant: Tenant, resource: Resource, accountKey: azdata.AccountKey, { accessToken, refreshToken, expiresOn }: OAuthTokenResponse) {
|
||||
const msg = localize('azure.cacheErrorAdd', "Error when adding your account to the cache.");
|
||||
if (!tenant.id || !resource.id) {
|
||||
Logger.pii('Tenant ID or resource ID was undefined', [], [], tenant, resource);
|
||||
Logger.piiSanitized('Tenant ID or resource ID was undefined', [], [], tenant, resource);
|
||||
throw new AzureAuthError(msg, 'Adding account to cache failed', undefined);
|
||||
}
|
||||
try {
|
||||
Logger.pii(`Saving access token`, [{ name: 'access_token', objOrArray: accessToken }], []);
|
||||
Logger.piiSanitized(`Saving access token`, [{ name: 'access_token', objOrArray: accessToken }], []);
|
||||
await this.tokenCache.saveCredential(`${accountKey.accountId}_access_${resource.id}_${tenant.id}`, JSON.stringify(accessToken));
|
||||
Logger.pii(`Saving refresh token`, [{ name: 'refresh_token', objOrArray: refreshToken }], []);
|
||||
Logger.piiSanitized(`Saving refresh token`, [{ name: 'refresh_token', objOrArray: refreshToken }], []);
|
||||
await this.tokenCache.saveCredential(`${accountKey.accountId}_refresh_${resource.id}_${tenant.id}`, JSON.stringify(refreshToken));
|
||||
this.memdb.set(`${accountKey.accountId}_${tenant.id}_${resource.id}`, expiresOn);
|
||||
} catch (ex) {
|
||||
@@ -557,7 +573,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
const parseMsg = localize('azure.cacheErrorParse', "Error when parsing your account from the cache");
|
||||
|
||||
if (!tenant.id || !resource.id) {
|
||||
Logger.pii('Tenant ID or resource ID was undefined', [], [], tenant, resource);
|
||||
Logger.piiSanitized('Tenant ID or resource ID was undefined', [], [], tenant, resource);
|
||||
throw new AzureAuthError(getMsg, 'Getting account from cache failed', undefined);
|
||||
}
|
||||
|
||||
@@ -584,7 +600,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
if (refreshTokenString) {
|
||||
refreshToken = JSON.parse(refreshTokenString);
|
||||
}
|
||||
Logger.pii('GetSavedToken ', [{ name: 'access', objOrArray: accessToken }, { name: 'refresh', objOrArray: refreshToken }], [], `expiresOn=${expiresOn}`);
|
||||
Logger.piiSanitized('GetSavedToken ', [{ name: 'access', objOrArray: accessToken }, { name: 'refresh', objOrArray: refreshToken }], [], `expiresOn=${expiresOn}`);
|
||||
return {
|
||||
accessToken, refreshToken, expiresOn
|
||||
};
|
||||
@@ -670,7 +686,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
}
|
||||
|
||||
};
|
||||
const messageBody = localize('azurecore.consentDialog.body', "Your tenant '{0} ({1})' requires you to re-authenticate again to access {2} resources. Press Open to start the authentication process.", tenant.displayName, tenant.id, resource.id);
|
||||
const messageBody = localize('azurecore.consentDialog.body', "Your tenant '{0} ({1})' requires you to re-authenticate again to access {2} resources. Press Open to start the authentication process.", tenant.displayName, tenant.id, resource.endpoint);
|
||||
const result = await vscode.window.showInformationMessage(messageBody, { modal: true }, openItem, closeItem, dontAskAgainItem);
|
||||
|
||||
if (result?.action) {
|
||||
@@ -693,7 +709,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
let accountIssuer = 'unknown';
|
||||
|
||||
if (tokenClaims.iss === 'https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/' ||
|
||||
tokenClaims.iss === 'https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/v2.0') {
|
||||
tokenClaims.iss === `${this.loginEndpointUrl}72f988bf-86f1-41af-91ab-2d7cd011db47/v2.0`) {
|
||||
accountIssuer = Constants.AccountIssuer.Corp;
|
||||
}
|
||||
if (tokenClaims?.idp === 'live.com') {
|
||||
@@ -775,7 +791,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
|
||||
// Intercept response and print out the response for future debugging
|
||||
const response = await axios.post(url, qs.stringify(postData), config);
|
||||
Logger.pii('POST request ', [{ name: 'data', objOrArray: postData }, { name: 'response', objOrArray: response.data }], [], url);
|
||||
Logger.piiSanitized('POST request ', [{ name: 'data', objOrArray: postData }, { name: 'response', objOrArray: response.data }], [], url);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -789,7 +805,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
};
|
||||
|
||||
const response = await axios.get(url, config);
|
||||
Logger.pii('GET request ', [{ name: 'response', objOrArray: response.data.value ?? response.data }], [], url,);
|
||||
Logger.piiSanitized('GET request ', [{ name: 'response', objOrArray: response.data.value ?? response.data }], [], url,);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AzureAccountProviderMetadata, AzureAuthType, Resource, Tenant } from 'a
|
||||
import { Deferred } from '../interfaces';
|
||||
import * as vscode from 'vscode';
|
||||
import * as crypto from 'crypto';
|
||||
import { SimpleTokenCache } from '../simpleTokenCache';
|
||||
import { SimpleTokenCache } from '../utils/simpleTokenCache';
|
||||
import { SimpleWebServer } from '../utils/simpleWebServer';
|
||||
import { AzureAuthError } from './azureAuthError';
|
||||
import { Logger } from '../../utils/Logger';
|
||||
@@ -233,7 +233,7 @@ export class AzureAuthCodeGrant extends AzureAuth {
|
||||
codeChallenge: this.pkceCodes.codeChallenge,
|
||||
codeChallengeMethod: this.pkceCodes.challengeMethod,
|
||||
prompt: Constants.SELECT_ACCOUNT,
|
||||
authority: `https://login.microsoftonline.com/${tenant.id}`,
|
||||
authority: `${this.loginEndpointUrl}${tenant.id}`,
|
||||
state: state
|
||||
};
|
||||
let authCodeRequest: AuthorizationCodeRequest;
|
||||
@@ -241,7 +241,7 @@ export class AzureAuthCodeGrant extends AzureAuth {
|
||||
scopes: this.scopes,
|
||||
redirectUri: `${this.redirectUri}:${serverPort}/redirect`,
|
||||
codeVerifier: this.pkceCodes.codeVerifier,
|
||||
authority: `https://login.microsoftonline.com/${tenant.id}`,
|
||||
authority: `${this.loginEndpointUrl}${tenant.id}`,
|
||||
code: ''
|
||||
};
|
||||
let authCodeUrl = await this.clientApplication.getAuthCodeUrl(authUrlRequest);
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from 'azurecore';
|
||||
import { Deferred } from '../interfaces';
|
||||
import { AuthenticationResult, DeviceCodeRequest, PublicClientApplication } from '@azure/msal-node';
|
||||
import { SimpleTokenCache } from '../simpleTokenCache';
|
||||
import { SimpleTokenCache } from '../utils/simpleTokenCache';
|
||||
import { Logger } from '../../utils/Logger';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
@@ -64,7 +64,7 @@ export class AzureDeviceCode extends AzureAuth {
|
||||
|
||||
const deviceCodeRequest: DeviceCodeRequest = {
|
||||
scopes: this.scopes,
|
||||
authority: `https://login.microsoftonline.com/${tenant.id}`,
|
||||
authority: `${this.loginEndpointUrl}${tenant.id}`,
|
||||
deviceCodeCallback: async (response) => {
|
||||
await azdata.accounts.beginAutoOAuthDeviceCode(this.metadata.id, this.pageTitle, response.message, response.userCode, response.verificationUri);
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import {
|
||||
AzureAccount
|
||||
} from 'azurecore';
|
||||
import { Deferred } from './interfaces';
|
||||
import { PublicClientApplication } from '@azure/msal-node';
|
||||
import { SimpleTokenCache } from './simpleTokenCache';
|
||||
import { AuthenticationResult, PublicClientApplication } from '@azure/msal-node';
|
||||
import { SimpleTokenCache } from './utils/simpleTokenCache';
|
||||
import { Logger } from '../utils/Logger';
|
||||
import { MultiTenantTokenResponse, Token, AzureAuth } from './auths/azureAuth';
|
||||
import { AzureAuthCodeGrant } from './auths/azureAuthCodeGrant';
|
||||
@@ -105,7 +105,7 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
|
||||
|
||||
private async _initialize(storedAccounts: AzureAccount[]): Promise<AzureAccount[]> {
|
||||
const accounts: AzureAccount[] = [];
|
||||
console.log(`Initializing stored accounts ${JSON.stringify(accounts)}`);
|
||||
Logger.verbose(`Initializing stored accounts ${JSON.stringify(accounts)}`);
|
||||
const updatedAccounts = filterAccounts(storedAccounts, this.authLibrary);
|
||||
for (let account of updatedAccounts) {
|
||||
const azureAuth = this.getAuthMethod(account);
|
||||
@@ -144,14 +144,11 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
|
||||
await this.initCompletePromise;
|
||||
const azureAuth = this.getAuthMethod(account);
|
||||
if (azureAuth) {
|
||||
Logger.pii(`Getting account security token for ${JSON.stringify(account.key)} (tenant ${tenantId}). Auth Method = ${azureAuth.userFriendlyName}`, [], []);
|
||||
Logger.piiSanitized(`Getting account security token for ${JSON.stringify(account.key)} (tenant ${tenantId}). Auth Method = ${azureAuth.userFriendlyName}`, [], []);
|
||||
if (this.authLibrary === Constants.AuthLibrary.MSAL) {
|
||||
tenantId = tenantId || account.properties.owningTenant.id;
|
||||
let authResult = await azureAuth.getTokenMsal(account.key.accountId, resource, tenantId);
|
||||
if (!authResult || !authResult.account || !authResult.account.idTokenClaims) {
|
||||
Logger.error(`MSAL: getToken call failed`);
|
||||
throw Error('Failed to get token');
|
||||
} else {
|
||||
if (this.isAuthenticationResult(authResult) && authResult.account && authResult.account.idTokenClaims) {
|
||||
const token: Token = {
|
||||
key: authResult.account.homeAccountId,
|
||||
token: authResult.accessToken,
|
||||
@@ -159,6 +156,14 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
|
||||
expiresOn: authResult.account.idTokenClaims.exp
|
||||
};
|
||||
return token;
|
||||
} else {
|
||||
Logger.error(`MSAL: getToken call failed`);
|
||||
// Throw error with MSAL-specific code/message, else throw generic error message
|
||||
if (this.isProviderError(authResult)) {
|
||||
throw new Error(localize('msalTokenError', `{0} occurred when acquiring token. \n{1}`, authResult.errorCode, authResult.errorMessage));
|
||||
} else {
|
||||
throw new Error(localize('genericTokenError', 'Failed to get token'));
|
||||
}
|
||||
}
|
||||
} else { // fallback to ADAL as default
|
||||
return azureAuth.getAccountSecurityTokenAdal(account, tenantId, resource);
|
||||
@@ -171,6 +176,23 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
|
||||
}
|
||||
}
|
||||
|
||||
private isAuthenticationResult(result: AuthenticationResult | azdata.ProviderError | null): result is AuthenticationResult {
|
||||
if (result) {
|
||||
return typeof (<AuthenticationResult>result).accessToken === 'string';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private isProviderError(result: AuthenticationResult | azdata.ProviderError | null): result is azdata.ProviderError {
|
||||
if (result) {
|
||||
return typeof (<azdata.ProviderError>result).errorMessage === 'string';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async _getSecurityToken(account: AzureAccount, resource: azdata.AzureResource): Promise<MultiTenantTokenResponse | undefined> {
|
||||
void vscode.window.showInformationMessage(localize('azure.deprecatedGetSecurityToken', "A call was made to azdata.accounts.getSecurityToken, this method is deprecated and will be removed in future releases. Please use getAccountSecurityToken instead."));
|
||||
const azureAccount = account as AzureAccount;
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as azdata from 'azdata';
|
||||
import * as events from 'events';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as vscode from 'vscode';
|
||||
import { SimpleTokenCache } from './simpleTokenCache';
|
||||
import { promises as fsPromises } from 'fs';
|
||||
import { SimpleTokenCache } from './utils/simpleTokenCache';
|
||||
import providerSettings from './providerSettings';
|
||||
import { AzureAccountProvider as AzureAccountProvider } from './azureAccountProvider';
|
||||
import { AzureAccountProviderMetadata } from 'azurecore';
|
||||
@@ -144,7 +146,8 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
||||
const isSaw: boolean = vscode.env.appName.toLowerCase().indexOf(Constants.Saw) > 0;
|
||||
const noSystemKeychain = vscode.workspace.getConfiguration(Constants.AzureSection).get<boolean>(Constants.NoSystemKeyChainSection);
|
||||
const tokenCacheKey = `azureTokenCache-${provider.metadata.id}`;
|
||||
const tokenCacheKeyMsal = `azureTokenCacheMsal-${provider.metadata.id}`;
|
||||
const tokenCacheKeyMsal = Constants.MSALCacheName;
|
||||
await this.clearOldCacheIfExists();
|
||||
try {
|
||||
if (!this._credentialProvider) {
|
||||
throw new Error('Credential provider not registered');
|
||||
@@ -155,7 +158,7 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
||||
await simpleTokenCache.init();
|
||||
|
||||
// MSAL Cache Plugin
|
||||
this._cachePluginProvider = new MsalCachePluginProvider(tokenCacheKeyMsal, this._userStoragePath);
|
||||
this._cachePluginProvider = new MsalCachePluginProvider(tokenCacheKeyMsal, this._userStoragePath, this._credentialProvider);
|
||||
|
||||
const msalConfiguration: Configuration = {
|
||||
auth: {
|
||||
@@ -184,6 +187,22 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears old cache file that is no longer needed on system.
|
||||
*/
|
||||
private async clearOldCacheIfExists(): Promise<void> {
|
||||
let filePath = path.join(this._userStoragePath, Constants.oldMsalCacheFileName);
|
||||
try {
|
||||
await fsPromises.access(filePath);
|
||||
await fsPromises.unlink('file:' + filePath);
|
||||
Logger.verbose(`Old cache file removed successfully.`);
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') {
|
||||
Logger.verbose(`Error occurred while removing old cache file: ${e}`);
|
||||
} // else file doesn't exist.
|
||||
}
|
||||
}
|
||||
|
||||
private getLoggerCallback(): ILoggerCallback {
|
||||
return (level: number, message: string, containsPii: boolean) => {
|
||||
if (!containsPii) {
|
||||
@@ -200,7 +219,7 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Logger.verbose(message);
|
||||
Logger.pii(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,115 +168,6 @@ const usGovAzureSettings: ProviderSettings = {
|
||||
}
|
||||
};
|
||||
|
||||
const usNatAzureSettings: ProviderSettings = {
|
||||
configKey: 'enableUsNatCloud',
|
||||
metadata: {
|
||||
displayName: localize('usNatCloudDisplayName', "Azure (US National)"),
|
||||
id: 'azure_usNatCloud',
|
||||
settings: {
|
||||
host: 'https://login.microsoftonline.eaglex.ic.gov/',
|
||||
clientId: 'a69788c6-1d43-44ed-9ca3-b83e194da255',
|
||||
microsoftResource: {
|
||||
id: SettingIds.marm,
|
||||
endpoint: 'https://management.azure.eaglex.ic.gov/',
|
||||
azureResourceId: AzureResource.MicrosoftResourceManagement
|
||||
},
|
||||
graphResource: {
|
||||
id: SettingIds.graph,
|
||||
endpoint: 'https://graph.eaglex.ic.gov/',
|
||||
azureResourceId: AzureResource.Graph
|
||||
},
|
||||
armResource: {
|
||||
id: SettingIds.arm,
|
||||
endpoint: 'https://management.core.eaglex.ic.gov/',
|
||||
azureResourceId: AzureResource.ResourceManagement
|
||||
},
|
||||
sqlResource: {
|
||||
id: SettingIds.sql,
|
||||
endpoint: 'https://database.cloudapi.eaglex.ic.gov/',
|
||||
azureResourceId: AzureResource.Sql
|
||||
},
|
||||
ossRdbmsResource: {
|
||||
id: SettingIds.ossrdbms,
|
||||
endpoint: 'https://ossrdbms-aad.database.cloudapi.eaglex.ic.gov/',
|
||||
azureResourceId: AzureResource.OssRdbms
|
||||
},
|
||||
azureKeyVaultResource: {
|
||||
id: SettingIds.vault,
|
||||
endpoint: 'https://vault.cloudapi.eaglex.ic.gov/',
|
||||
azureResourceId: AzureResource.AzureKeyVault
|
||||
},
|
||||
azureLogAnalyticsResource: {
|
||||
id: SettingIds.ala,
|
||||
endpoint: 'https://api.loganalytics.azure.eaglex.ic.gov/',
|
||||
azureResourceId: AzureResource.AzureLogAnalytics,
|
||||
},
|
||||
azureStorageResource: {
|
||||
id: SettingIds.storage,
|
||||
endpoint: '',
|
||||
endpointSuffix: '.core.eaglex.ic.gov/',
|
||||
azureResourceId: AzureResource.AzureStorage
|
||||
},
|
||||
redirectUri: 'http://localhost',
|
||||
scopes: [
|
||||
'openid', 'email', 'profile', 'offline_access',
|
||||
'https://management.core.eaglex.ic.gov/user_impersonation'
|
||||
],
|
||||
portalEndpoint: 'https://portal.azure.eaglex.ic.gov/'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const germanyAzureSettings: ProviderSettings = {
|
||||
configKey: 'enableGermanyCloud',
|
||||
metadata: {
|
||||
displayName: localize('germanyCloud', "Azure (Germany)"),
|
||||
id: 'azure_germanyCloud',
|
||||
settings: {
|
||||
host: 'https://login.microsoftazure.de/',
|
||||
clientId: 'a69788c6-1d43-44ed-9ca3-b83e194da255',
|
||||
graphResource: {
|
||||
id: SettingIds.graph,
|
||||
endpoint: 'https://graph.cloudapi.de/',
|
||||
azureResourceId: AzureResource.Graph
|
||||
},
|
||||
msGraphResource: {
|
||||
id: SettingIds.msgraph,
|
||||
endpoint: 'https://graph.microsoft.de/',
|
||||
azureResourceId: AzureResource.MsGraph
|
||||
},
|
||||
armResource: {
|
||||
id: SettingIds.arm,
|
||||
endpoint: 'https://management.microsoftazure.de/',
|
||||
azureResourceId: AzureResource.ResourceManagement
|
||||
},
|
||||
azureKeyVaultResource: {
|
||||
id: SettingIds.vault,
|
||||
endpoint: 'https://vault.microsoftazure.de/',
|
||||
azureResourceId: AzureResource.AzureKeyVault
|
||||
},
|
||||
azureStorageResource: {
|
||||
id: SettingIds.storage,
|
||||
endpoint: '',
|
||||
endpointSuffix: '.core.cloudapi.de/',
|
||||
azureResourceId: AzureResource.AzureStorage
|
||||
},
|
||||
powerBiResource: {
|
||||
id: SettingIds.powerbi,
|
||||
endpoint: 'https://analysis.windows.net/powerbi/api/',
|
||||
azureResourceId: AzureResource.PowerBi
|
||||
},
|
||||
redirectUri: 'http://localhost',
|
||||
scopes: [
|
||||
'openid', 'email', 'profile', 'offline_access',
|
||||
'https://management.microsoftazure.de/user_impersonation'
|
||||
],
|
||||
portalEndpoint: 'https://portal.microsoftazure.de/'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const chinaAzureSettings: ProviderSettings = {
|
||||
configKey: 'enableChinaCloud',
|
||||
metadata: {
|
||||
@@ -340,5 +231,5 @@ const chinaAzureSettings: ProviderSettings = {
|
||||
}
|
||||
}
|
||||
};
|
||||
const allSettings = [publicAzureSettings, usGovAzureSettings, usNatAzureSettings, germanyAzureSettings, chinaAzureSettings];
|
||||
const allSettings = [publicAzureSettings, usGovAzureSettings, chinaAzureSettings];
|
||||
export default allSettings;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { promises as fs, constants as fsConstants } from 'fs';
|
||||
import { Logger } from '../../utils/Logger';
|
||||
|
||||
export type ReadWriteHook = (contents: string) => Promise<string>;
|
||||
const noOpHook: ReadWriteHook = async (contents): Promise<string> => {
|
||||
@@ -97,7 +98,7 @@ export class FileDatabase {
|
||||
fileContents = await fs.readFile(this.dbPath, { encoding: 'utf8' });
|
||||
fileContents = await this.readHook(fileContents);
|
||||
} catch (ex) {
|
||||
console.log(`file db does not exist ${ex}`);
|
||||
Logger.error(`Error occurred when initializing File Database from file system cache, ADAL cache will be reset: ${ex}`);
|
||||
await this.createFile();
|
||||
this.db = {};
|
||||
this.isDirty = true;
|
||||
@@ -107,7 +108,7 @@ export class FileDatabase {
|
||||
try {
|
||||
this.db = JSON.parse(fileContents);
|
||||
} catch (ex) {
|
||||
console.log(`DB was corrupted, resetting it ${ex}`);
|
||||
Logger.error(`Error occurred when reading file database contents as JSON, ADAL cache will be reset: ${ex}`);
|
||||
await this.createFile();
|
||||
this.db = {};
|
||||
}
|
||||
@@ -139,7 +140,7 @@ export class FileDatabase {
|
||||
|
||||
this.isDirty = false;
|
||||
} catch (ex) {
|
||||
console.log(`File saving is erroring! ${ex}`);
|
||||
Logger.error(`Error occurred while saving cache contents to file storage, this may cause issues with ADAL cache persistence: ${ex}`);
|
||||
} finally {
|
||||
this.isSaving = false;
|
||||
}
|
||||
|
||||
@@ -3,32 +3,50 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as azdata from 'azdata';
|
||||
import * as os from 'os';
|
||||
import * as crypto from 'crypto';
|
||||
import * as vscode from 'vscode';
|
||||
import { AuthLibrary } from '../../constants';
|
||||
import * as LocalizedConstants from '../../localizedConstants';
|
||||
import { Logger } from '../../utils/Logger';
|
||||
|
||||
export class FileEncryptionHelper {
|
||||
constructor(
|
||||
private _credentialService: azdata.CredentialProvider,
|
||||
private _fileName: string,
|
||||
) { }
|
||||
private readonly _authLibrary: AuthLibrary,
|
||||
private readonly _credentialService: azdata.CredentialProvider,
|
||||
protected readonly _fileName: string
|
||||
) {
|
||||
this._algorithm = this._authLibrary === AuthLibrary.MSAL ? 'aes-256-cbc' : 'aes-256-gcm';
|
||||
this._bufferEncoding = this._authLibrary === AuthLibrary.MSAL ? 'utf16le' : 'hex';
|
||||
this._binaryEncoding = this._authLibrary === AuthLibrary.MSAL ? 'base64' : 'hex';
|
||||
}
|
||||
|
||||
private _algorithm: string;
|
||||
private _bufferEncoding: BufferEncoding;
|
||||
private _binaryEncoding: crypto.HexBase64BinaryEncoding;
|
||||
private _ivBuffer: Buffer | undefined;
|
||||
private _keyBuffer: Buffer | undefined;
|
||||
|
||||
async init(): Promise<void> {
|
||||
const iv = await this._credentialService.readCredential(`${this._fileName}-iv`);
|
||||
const key = await this._credentialService.readCredential(`${this._fileName}-key`);
|
||||
if (!iv?.password || !key?.password) {
|
||||
public async init(): Promise<void> {
|
||||
|
||||
const ivCredId = `${this._fileName}-iv`;
|
||||
const keyCredId = `${this._fileName}-key`;
|
||||
|
||||
const iv = await this.readEncryptionKey(ivCredId);
|
||||
const key = await this.readEncryptionKey(keyCredId);
|
||||
|
||||
if (!iv || !key) {
|
||||
this._ivBuffer = crypto.randomBytes(16);
|
||||
this._keyBuffer = crypto.randomBytes(32);
|
||||
try {
|
||||
await this._credentialService.saveCredential(`${this._fileName}-iv`, this._ivBuffer.toString('hex'));
|
||||
await this._credentialService.saveCredential(`${this._fileName}-key`, this._keyBuffer.toString('hex'));
|
||||
} catch (ex) {
|
||||
console.log(ex);
|
||||
|
||||
if (!await this.saveEncryptionKey(ivCredId, this._ivBuffer.toString(this._bufferEncoding))
|
||||
|| !await this.saveEncryptionKey(keyCredId, this._keyBuffer.toString(this._bufferEncoding))) {
|
||||
Logger.error(`Encryption keys could not be saved in credential store, this will cause access token persistence issues.`);
|
||||
await this.showCredSaveErrorOnWindows();
|
||||
}
|
||||
} else {
|
||||
this._ivBuffer = Buffer.from(iv.password, 'hex');
|
||||
this._keyBuffer = Buffer.from(key.password, 'hex');
|
||||
this._ivBuffer = Buffer.from(iv, this._bufferEncoding);
|
||||
this._keyBuffer = Buffer.from(key, this._bufferEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,21 +54,68 @@ export class FileEncryptionHelper {
|
||||
if (!this._keyBuffer || !this._ivBuffer) {
|
||||
await this.init();
|
||||
}
|
||||
const cipherIv = crypto.createCipheriv('aes-256-gcm', this._keyBuffer!, this._ivBuffer!);
|
||||
return `${cipherIv.update(content, 'utf8', 'hex')}${cipherIv.final('hex')}%${cipherIv.getAuthTag().toString('hex')}`;
|
||||
};
|
||||
const cipherIv = crypto.createCipheriv(this._algorithm, this._keyBuffer!, this._ivBuffer!);
|
||||
let cipherText = `${cipherIv.update(content, 'utf8', this._binaryEncoding)}${cipherIv.final(this._binaryEncoding)}`;
|
||||
if (this._authLibrary === AuthLibrary.ADAL) {
|
||||
cipherText += `%${(cipherIv as crypto.CipherGCM).getAuthTag().toString(this._binaryEncoding)}`;
|
||||
}
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
fileOpener = async (content: string): Promise<string> => {
|
||||
if (!this._keyBuffer || !this._ivBuffer) {
|
||||
await this.init();
|
||||
}
|
||||
const decipherIv = crypto.createDecipheriv('aes-256-gcm', this._keyBuffer!, this._ivBuffer!);
|
||||
const split = content.split('%');
|
||||
if (split.length !== 2) {
|
||||
throw new Error('File didn\'t contain the auth tag.');
|
||||
let plaintext = content;
|
||||
const decipherIv = crypto.createDecipheriv(this._algorithm, this._keyBuffer!, this._ivBuffer!);
|
||||
if (this._authLibrary === AuthLibrary.ADAL) {
|
||||
const split = content.split('%');
|
||||
if (split.length !== 2) {
|
||||
throw new Error('File didn\'t contain the auth tag.');
|
||||
}
|
||||
(decipherIv as crypto.DecipherGCM).setAuthTag(Buffer.from(split[1], this._binaryEncoding));
|
||||
plaintext = split[0];
|
||||
}
|
||||
decipherIv.setAuthTag(Buffer.from(split[1], 'hex'));
|
||||
return `${decipherIv.update(split[0], 'hex', 'utf8')}${decipherIv.final('utf8')}`;
|
||||
};
|
||||
return `${decipherIv.update(plaintext, this._binaryEncoding, 'utf8')}${decipherIv.final('utf8')}`;
|
||||
}
|
||||
|
||||
protected async readEncryptionKey(credentialId: string): Promise<string | undefined> {
|
||||
return (await this._credentialService.readCredential(credentialId))?.password;
|
||||
}
|
||||
|
||||
protected async saveEncryptionKey(credentialId: string, password: string): Promise<boolean> {
|
||||
let status: boolean = false;
|
||||
try {
|
||||
await this._credentialService.saveCredential(credentialId, password)
|
||||
.then((result) => {
|
||||
status = result;
|
||||
if (result) {
|
||||
Logger.info(`FileEncryptionHelper: Successfully saved encryption key ${credentialId} for ${this._authLibrary} persistent cache encryption in system credential store.`);
|
||||
}
|
||||
}, (e => {
|
||||
throw Error(`FileEncryptionHelper: Could not save encryption key: ${credentialId}: ${e}`);
|
||||
}));
|
||||
} catch (ex) {
|
||||
if (os.platform() === 'win32') {
|
||||
Logger.error(`FileEncryptionHelper: Please try cleaning saved credentials from Windows Credential Manager created by Azure Data Studio to allow creating new credentials.`);
|
||||
}
|
||||
Logger.error(ex);
|
||||
throw ex;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
protected async showCredSaveErrorOnWindows(): Promise<void> {
|
||||
if (os.platform() === 'win32') {
|
||||
await vscode.window.showWarningMessage(LocalizedConstants.azureCredStoreSaveFailedError,
|
||||
LocalizedConstants.reloadChoice, LocalizedConstants.cancel)
|
||||
.then(async (selection) => {
|
||||
if (selection === LocalizedConstants.reloadChoice) {
|
||||
await vscode.commands.executeCommand('workbench.action.reloadWindow');
|
||||
}
|
||||
}, error => {
|
||||
Logger.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,23 +8,26 @@ import { promises as fsPromises } from 'fs';
|
||||
|
||||
import * as lockFile from 'lockfile';
|
||||
import * as path from 'path';
|
||||
import { AccountsClearTokenCacheCommand } from '../../constants';
|
||||
import * as azdata from 'azdata';
|
||||
import { AccountsClearTokenCacheCommand, AuthLibrary } from '../../constants';
|
||||
import { Logger } from '../../utils/Logger';
|
||||
import { FileEncryptionHelper } from './fileEncryptionHelper';
|
||||
|
||||
export class MsalCachePluginProvider {
|
||||
constructor(
|
||||
private readonly _serviceName: string,
|
||||
private readonly _msalFilePath: string,
|
||||
private readonly _credentialService: azdata.CredentialProvider
|
||||
) {
|
||||
this._msalFilePath = path.join(this._msalFilePath, this._serviceName);
|
||||
this._serviceName = this._serviceName.replace(/-/, '_');
|
||||
Logger.verbose(`MsalCachePluginProvider: Using cache path ${_msalFilePath} and serviceName ${_serviceName}`);
|
||||
this._fileEncryptionHelper = new FileEncryptionHelper(AuthLibrary.MSAL, this._credentialService, this._serviceName);
|
||||
}
|
||||
|
||||
private _lockTaken: boolean = false;
|
||||
private _fileEncryptionHelper: FileEncryptionHelper;
|
||||
|
||||
private getLockfilePath(): string {
|
||||
return this._msalFilePath + '.lock';
|
||||
return this._msalFilePath + '.lockfile';
|
||||
}
|
||||
|
||||
public getCachePlugin(): ICachePlugin {
|
||||
@@ -33,8 +36,9 @@ export class MsalCachePluginProvider {
|
||||
await this.waitAndLock(lockFilePath);
|
||||
try {
|
||||
const cache = await fsPromises.readFile(this._msalFilePath, { encoding: 'utf8' });
|
||||
const decryptedData = await this._fileEncryptionHelper.fileOpener(cache!);
|
||||
try {
|
||||
cacheContext.tokenCache.deserialize(cache);
|
||||
cacheContext.tokenCache.deserialize(decryptedData);
|
||||
} catch (e) {
|
||||
// Handle deserialization error in cache file in case file gets corrupted.
|
||||
// Clearing cache here will ensure account is marked stale so re-authentication can be triggered.
|
||||
@@ -49,7 +53,8 @@ export class MsalCachePluginProvider {
|
||||
}
|
||||
else {
|
||||
Logger.error(`MsalCachePlugin: Failed to read from cache file: ${e}`);
|
||||
throw e;
|
||||
Logger.verbose(`MsalCachePlugin: Error occurred when trying to read cache file, file contents will be cleared: ${e.message}`);
|
||||
await fsPromises.writeFile(this._msalFilePath, '', { encoding: 'utf8' });
|
||||
}
|
||||
} finally {
|
||||
lockFile.unlockSync(lockFilePath);
|
||||
@@ -62,7 +67,8 @@ export class MsalCachePluginProvider {
|
||||
await this.waitAndLock(lockFilePath);
|
||||
try {
|
||||
const data = cacheContext.tokenCache.serialize();
|
||||
await fsPromises.writeFile(this._msalFilePath, data, { encoding: 'utf8' });
|
||||
const encryptedData = await this._fileEncryptionHelper.fileSaver(data!);
|
||||
await fsPromises.writeFile(this._msalFilePath, encryptedData, { encoding: 'utf8' });
|
||||
Logger.verbose(`MsalCachePlugin: Token written to cache successfully.`);
|
||||
} catch (e) {
|
||||
Logger.error(`MsalCachePlugin: Failed to write to cache file. ${e}`);
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as keytarType from 'keytar';
|
||||
import { join, parse } from 'path';
|
||||
import { FileDatabase } from './utils/fileDatabase';
|
||||
import { FileDatabase } from './fileDatabase';
|
||||
import * as azdata from 'azdata';
|
||||
import { FileEncryptionHelper } from './utils/fileEncryptionHelper';
|
||||
import { FileEncryptionHelper } from './fileEncryptionHelper';
|
||||
import { AuthLibrary } from '../../constants';
|
||||
|
||||
function getSystemKeytar(): Keytar | undefined {
|
||||
try {
|
||||
@@ -25,7 +26,7 @@ const separator = '§';
|
||||
|
||||
async function getFileKeytar(filePath: string, credentialService: azdata.CredentialProvider): Promise<Keytar | undefined> {
|
||||
const fileName = parse(filePath).base;
|
||||
const fileEncryptionHelper: FileEncryptionHelper = new FileEncryptionHelper(credentialService, fileName);
|
||||
const fileEncryptionHelper: FileEncryptionHelper = new FileEncryptionHelper(AuthLibrary.ADAL, credentialService, fileName);
|
||||
const db = new FileDatabase(filePath, fileEncryptionHelper.fileOpener, fileEncryptionHelper.fileSaver);
|
||||
await db.initialize();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import * as http from 'http';
|
||||
import * as url from 'url';
|
||||
import { AddressInfo } from 'net';
|
||||
import { Logger } from '../../utils/Logger';
|
||||
|
||||
export type WebHandler = (req: http.IncomingMessage, reqUrl: url.UrlWithParsedQuery, res: http.ServerResponse) => void;
|
||||
|
||||
@@ -24,7 +25,7 @@ export class SimpleWebServer {
|
||||
const time = new Date().getTime();
|
||||
|
||||
if (time - this.lastUsed > this.autoShutoffTimer) {
|
||||
console.log('Shutting off webserver...');
|
||||
Logger.verbose('Shutting off webserver...');
|
||||
this.shutdown().catch(console.error);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
@@ -12,6 +12,7 @@ import * as azureResourceUtils from './azureResource/utils';
|
||||
import * as constants from './constants';
|
||||
import * as loc from './localizedConstants';
|
||||
import * as utils from './utils';
|
||||
import { Logger } from './utils/Logger';
|
||||
|
||||
const typesClause = [
|
||||
azureResource.AzureResourceType.sqlDatabase,
|
||||
@@ -60,10 +61,10 @@ export class AzureDataGridProvider implements azdata.DataGridProvider {
|
||||
});
|
||||
items.push(...newItems);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
Logger.error(err);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
Logger.error(err);
|
||||
}
|
||||
}));
|
||||
}));
|
||||
|
||||
@@ -10,6 +10,7 @@ import { IAzureResourceService } from '../interfaces';
|
||||
import { AzureResourceErrorMessageUtil } from '../utils';
|
||||
import { ResourceGraphClient } from '@azure/arm-resourcegraph';
|
||||
import { AzureAccount, azureResource } from 'azurecore';
|
||||
import { Logger } from '../../utils/Logger';
|
||||
|
||||
export abstract class ResourceTreeDataProviderBase<T extends azureResource.AzureResource> implements azureResource.IAzureResourceTreeDataProvider {
|
||||
public browseConnectionMode: boolean = false;
|
||||
@@ -32,7 +33,7 @@ export abstract class ResourceTreeDataProviderBase<T extends azureResource.Azure
|
||||
treeItem: this.getTreeItemForResource(resource, element.account)
|
||||
}).sort((a, b) => (<any>a.treeItem.label).localeCompare(b.treeItem.label));
|
||||
} catch (error) {
|
||||
console.log(AzureResourceErrorMessageUtil.getErrorMessage(error));
|
||||
Logger.error(AzureResourceErrorMessageUtil.getErrorMessage(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -102,7 +103,7 @@ export async function queryGraphResources<T extends GraphData>(resourceClient: R
|
||||
}
|
||||
} catch (err2) {
|
||||
// Just log, we still want to throw the original error if something happens parsing the error
|
||||
console.log(`Unexpected error while parsing error from querying resources : ${err2}`);
|
||||
Logger.error(`Unexpected error while parsing error from querying resources : ${err2}`);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -50,10 +50,12 @@ export class AzureResourceSubscriptionService implements IAzureResourceSubscript
|
||||
void vscode.window.showWarningMessage(errorMsg);
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMsg = localize('azure.resource.tenantSubscriptionsError', "Failed to get subscriptions for account {0} (tenant '{1}'). {2}", account.displayInfo.displayName, tenantId, AzureResourceErrorMessageUtil.getErrorMessage(error));
|
||||
Logger.error(`Failed to get subscriptions for account ${account.displayInfo.displayName} (tenant '${tenantId}'). ${AzureResourceErrorMessageUtil.getErrorMessage(error)}`);
|
||||
errors.push(error);
|
||||
void vscode.window.showWarningMessage(errorMsg);
|
||||
if (!account.isStale) {
|
||||
const errorMsg = localize('azure.resource.tenantSubscriptionsError', "Failed to get subscriptions for account {0} (tenant '{1}'). {2}", account.displayInfo.displayName, tenantId, AzureResourceErrorMessageUtil.getErrorMessage(error));
|
||||
Logger.error(`Failed to get subscriptions for account ${account.displayInfo.displayName} (tenant '${tenantId}'). ${AzureResourceErrorMessageUtil.getErrorMessage(error)}`);
|
||||
errors.push(error);
|
||||
void vscode.window.showWarningMessage(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!gotSubscriptions) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as WS from 'ws';
|
||||
|
||||
import { IAzureTerminalService } from '../interfaces';
|
||||
import { AzureAccount, Tenant } from 'azurecore';
|
||||
import { Logger } from '../../utils/Logger';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -68,8 +69,8 @@ export class AzureTerminalService implements IAzureTerminalService {
|
||||
let userSettingsResult: AxiosResponse<any>;
|
||||
try {
|
||||
userSettingsResult = await axios.get(userSettingsUri, settings);
|
||||
} catch (ex) {
|
||||
console.log(ex, ex.response);
|
||||
} catch (ex) {// Log as info as exception is handled
|
||||
Logger.info(ex, ex.response);
|
||||
await handleNeverUsed();
|
||||
return;
|
||||
}
|
||||
@@ -85,8 +86,8 @@ export class AzureTerminalService implements IAzureTerminalService {
|
||||
let provisionResult: AxiosResponse<any>;
|
||||
try {
|
||||
provisionResult = await axios.put(consoleRequestUri, {}, settings);
|
||||
} catch (ex) {
|
||||
console.log(ex, ex.response);
|
||||
} catch (ex) {// Log as info as exception is handled
|
||||
Logger.info(ex, ex.response);
|
||||
await handleNeverUsed();
|
||||
return;
|
||||
}
|
||||
@@ -215,7 +216,7 @@ class AzureTerminal implements vscode.Pseudoterminal {
|
||||
this.socket?.ping();
|
||||
}, 5000);
|
||||
} catch (ex) {
|
||||
console.log(ex);
|
||||
Logger.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +235,7 @@ class AzureTerminal implements vscode.Pseudoterminal {
|
||||
}
|
||||
});
|
||||
} catch (ex) {
|
||||
console.log(`Error establishing terminal. ${ex}, ${ex.response}`);
|
||||
Logger.info(`Error establishing terminal. ${ex}, ${ex.response}`);
|
||||
await handleNeverUsed();
|
||||
return undefined;
|
||||
}
|
||||
@@ -246,8 +247,8 @@ class AzureTerminal implements vscode.Pseudoterminal {
|
||||
}
|
||||
|
||||
if (!terminalUri) {
|
||||
console.log(terminalResult);
|
||||
throw new Error(terminalResult.data);
|
||||
Logger.error(terminalResult);
|
||||
throw Error(terminalResult.data);
|
||||
}
|
||||
|
||||
return terminalUri;
|
||||
|
||||
@@ -39,6 +39,8 @@ export const AzureTenantConfigSection = AzureSection + '.' + TenantSection + '.'
|
||||
|
||||
export const NoSystemKeyChainSection = 'noSystemKeychain';
|
||||
|
||||
export const oldMsalCacheFileName = 'azureTokenCacheMsal-azure_publicCloud';
|
||||
|
||||
/** MSAL Account version */
|
||||
export const AccountVersion = '2.0';
|
||||
|
||||
@@ -61,6 +63,8 @@ export const dataGridProviderId = 'azure-resources';
|
||||
|
||||
export const AzureTokenFolderName = 'Azure Accounts';
|
||||
|
||||
export const MSALCacheName = 'accessTokenCache';
|
||||
|
||||
export const DefaultAuthLibrary = 'ADAL';
|
||||
|
||||
export enum BuiltInCommands {
|
||||
|
||||
@@ -100,7 +100,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<azurec
|
||||
updatePiiLoggingLevel();
|
||||
|
||||
// Create the provider service and activate
|
||||
initAzureAccountProvider(extensionContext, storagePath, authLibrary!).catch((err) => console.log(err));
|
||||
initAzureAccountProvider(extensionContext, storagePath, authLibrary!).catch((err) => Logger.error(err));
|
||||
|
||||
registerAzureServices(appContext);
|
||||
const azureResourceTree = new AzureResourceTreeProvider(appContext, authLibrary);
|
||||
@@ -119,7 +119,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<azurec
|
||||
if (portalEndpoint && subscriptionId && resourceGroup && type && name) {
|
||||
await vscode.env.openExternal(vscode.Uri.parse(`${portalEndpoint}/#resource/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/${type}/${name}`));
|
||||
} else {
|
||||
console.log(`Missing required values - subscriptionId : ${subscriptionId} resourceGroup : ${resourceGroup} type: ${type} name: ${name}`);
|
||||
Logger.error(`Missing required values - subscriptionId : ${subscriptionId} resourceGroup : ${resourceGroup} type: ${type} name: ${name}`);
|
||||
void vscode.window.showErrorMessage(loc.unableToOpenAzureLink);
|
||||
}
|
||||
});
|
||||
@@ -248,7 +248,7 @@ async function findOrMakeStoragePath() {
|
||||
await fs.mkdir(defaultLogLocation, { recursive: true });
|
||||
} catch (e) {
|
||||
if (e.code !== 'EEXIST') {
|
||||
console.log(`Creating the base directory failed... ${e}`);
|
||||
Logger.error(`Creating the base directory failed... ${e}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -257,13 +257,13 @@ async function findOrMakeStoragePath() {
|
||||
await fs.mkdir(storagePath, { recursive: true });
|
||||
} catch (e) {
|
||||
if (e.code !== 'EEXIST') {
|
||||
console.error(`Initialization of Azure account extension storage failed: ${e}`);
|
||||
console.error('Azure accounts will not be available');
|
||||
Logger.error(`Initialization of Azure account extension storage failed: ${e}`);
|
||||
Logger.error('Azure accounts will not be available');
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Initialized Azure account extension storage.');
|
||||
Logger.verbose('Initialized Azure account extension storage.');
|
||||
return storagePath;
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ async function initAzureAccountProvider(extensionContext: vscode.ExtensionContex
|
||||
extensionContext.subscriptions.push(accountProviderService);
|
||||
await accountProviderService.activate();
|
||||
} catch (err) {
|
||||
console.log('Unexpected error starting account provider: ' + err.message);
|
||||
Logger.error('Unexpected error starting account provider: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ export const extensionName = localize('azurecore.extensionName', "Azure Accounts
|
||||
|
||||
export const requiresReload = localize('azurecore.requiresReload', "Modifying this setting requires reloading the window for all changes to take effect.");
|
||||
export const reload = localize('azurecore.reload', "Reload");
|
||||
export const cancel = localize('azurecore.reload', "Cancel");
|
||||
|
||||
export const australiaCentral = localize('azurecore.australiacentral', "Australia Central");
|
||||
export const australiaCentral2 = localize('azurecore.australiacentral2', "Australia Central 2");
|
||||
@@ -84,3 +85,6 @@ export const invalidTenant = localize('azurecore.invalidTenant', "Invalid tenant
|
||||
export function unableToFetchTokenError(tenant: string): string {
|
||||
return localize('azurecore.unableToFetchToken', "Unable to get token for tenant {0}", tenant);
|
||||
}
|
||||
|
||||
// Error Messages
|
||||
export const azureCredStoreSaveFailedError = localize('azure.credStoreSaveFailedError', `Keys for token cache could not be saved in credential store, this may cause Azure access token persistence issues and connection instabilities. It's likely that SqlTools has reached credential storage limit on Windows, please clear at least 2 credentials that start with "Microsoft.SqlTools|" in Windows Credential Manager and reload.`);
|
||||
|
||||
@@ -47,7 +47,15 @@ export class Logger {
|
||||
this.write(LogLevel.Verbose, msg, vals);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logs a message containing PII (when enabled).
|
||||
* @param msg The initial message to log
|
||||
*/
|
||||
static pii(msg: any, ...vals: any[]) {
|
||||
if (this.piiLogging) {
|
||||
Logger.write(LogLevel.Pii, msg, vals);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message containing PII (when enabled). Provides the ability to sanitize or shorten values to hide information or reduce the amount logged.
|
||||
@@ -56,7 +64,7 @@ export class Logger {
|
||||
* @param stringsToShorten Set of strings to shorten
|
||||
* @param vals Any other values to add on to the end of the log message
|
||||
*/
|
||||
static pii(msg: any, objsToSanitize: { name: string, objOrArray: any | any[] }[], stringsToShorten: { name: string, value: string }[], ...vals: any[]) {
|
||||
static piiSanitized(msg: any, objsToSanitize: { name: string, objOrArray: any | any[] }[], stringsToShorten: { name: string, value: string }[], ...vals: any[]) {
|
||||
if (this.piiLogging) {
|
||||
msg = [
|
||||
msg,
|
||||
|
||||
@@ -690,10 +690,12 @@ crypt@~0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
|
||||
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
|
||||
|
||||
crypto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037"
|
||||
integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==
|
||||
debug@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@3.1.0:
|
||||
version "3.1.0"
|
||||
|
||||
@@ -5,7 +5,7 @@ Adds a Jupyter Book that has several utilities for Azure SQL Hybrid Cloud. To op
|
||||
* Type <code>Jupyter Books: Azure SQL Hybrid Cloud Toolkit</code> and select it as a command
|
||||
* Press Enter
|
||||
|
||||
The Book will be displayed in the <code>Provided Books</code> panel in the Notebooks viewlet.
|
||||
The Book will be displayed in the <code>Provided Books</code> panel in the Notebooks viewlet.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
@@ -13,7 +13,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -30,9 +30,13 @@ This extension lives in the [azuredatastudio repo](https://github.com/microsoft/
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Telemetry
|
||||
|
||||
This extension collects telemetry data, which is used to help understand how to improve the product. For example, this usage data helps to debug issues, such as slow start-up times, and to prioritize new features. While we appreciate the insights this data provides, we also know that not everyone wants to send usage data and you can disable telemetry as described in the Azure Data Studio [disable telemetry reporting](https://github.com/Microsoft/azuredatastudio/wiki/How-to-Disable-Telemetry-Reporting#how-to-disable-telemetry-reporting) documentation.
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "3.0.0-release.133",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-net5.0.zip",
|
||||
"Windows_64": "win-x64-net5.0.zip",
|
||||
"Windows": "win-x64-net5.0.zip",
|
||||
"OSX": "osx-x64-net5.0.tar.gz",
|
||||
"Linux": "rhel-x64-net5.0.tar.gz"
|
||||
},
|
||||
|
||||
@@ -212,8 +212,8 @@
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.1",
|
||||
"figures": "^2.0.0",
|
||||
"find-remove": "1.2.1",
|
||||
"@microsoft/ads-service-downloader": "1.0.4",
|
||||
"@microsoft/ads-extension-telemetry": "^1.3.2",
|
||||
"@microsoft/ads-service-downloader": "^1.2.1",
|
||||
"@microsoft/ads-extension-telemetry": "^2.0.0",
|
||||
"vscode-languageclient": "5.2.1",
|
||||
"vscode-nls": "^4.0.0"
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as path from 'path';
|
||||
import { getCommonLaunchArgsAndCleanupOldLogFiles } from './utils';
|
||||
import { Telemetry, LanguageClientErrorHandler } from './telemetry';
|
||||
import { TelemetryReporter, LanguageClientErrorHandler } from './telemetry';
|
||||
import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client';
|
||||
import { SerializationFeature } from './features/serializationFeature';
|
||||
import { TelemetryFeature } from './features/telemetryFeature';
|
||||
@@ -48,7 +48,7 @@ export class AzureMonitorServer {
|
||||
vscode.commands.registerCommand('azuremonitor.loadCompletionExtension', (params: CompletionExtensionParams) => {
|
||||
this.client.sendRequest(CompletionExtLoadRequest.type, params);
|
||||
});
|
||||
Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', {
|
||||
TelemetryReporter.sendTelemetryEvent('startup/LanguageClientStarted', {
|
||||
installationTime: String(installationComplete - installationStart),
|
||||
processStartupTime: String(processEnd - processStart),
|
||||
totalTime: String(processEnd - installationStart),
|
||||
@@ -61,7 +61,7 @@ export class AzureMonitorServer {
|
||||
await Promise.all([clientReadyPromise]);
|
||||
return this.client;
|
||||
} catch (e) {
|
||||
Telemetry.sendTelemetryEvent('ServiceInitializingFailed');
|
||||
TelemetryReporter.sendTelemetryEvent('ServiceInitializingFailed');
|
||||
vscode.window.showErrorMessage(localize('failedToStartServiceErrorMsg', "Failed to start {0}", Constants.serviceName));
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export const serviceName = 'AzureMonitor Tools Service';
|
||||
export const providerId = 'LOGANALYTICS';
|
||||
export const serviceCrashLink = 'https://github.com/Microsoft/azuredatastudio/issues';
|
||||
export const extensionConfigSectionName = 'azuremonitor';
|
||||
export const packageName = 'Microsoft.azuremonitor';
|
||||
|
||||
// DATA PROTOCOL VALUES ///////////////////////////////////////////////////////////
|
||||
export const azureMonitorClusterProviderName = 'azureMonitorCluster';
|
||||
@@ -22,4 +23,4 @@ export const objectExplorerPrefix: string = 'objectexplorer://';
|
||||
export const ViewType = 'view';
|
||||
|
||||
export const azuremonitorClusterNewNotebookTask = 'azuremonitorCluster.task.newNotebook';
|
||||
export const azuremonitorClusterOpenNotebookTask = 'azuremonitorCluster.task.openNotebook';
|
||||
export const azuremonitorClusterOpenNotebookTask = 'azuremonitorCluster.task.openNotebook';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { NotificationType, RequestType } from 'vscode-languageclient';
|
||||
import { ITelemetryEventProperties, ITelemetryEventMeasures } from '../telemetry';
|
||||
import * as telemetry from '@microsoft/ads-extension-telemetry';
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
// ------------------------------- < Telemetry Sent Event > ------------------------------------
|
||||
@@ -22,8 +22,8 @@ export namespace TelemetryNotification {
|
||||
export class TelemetryParams {
|
||||
public params!: {
|
||||
eventName: string;
|
||||
properties: ITelemetryEventProperties;
|
||||
measures: ITelemetryEventMeasures;
|
||||
properties: telemetry.TelemetryEventProperties;
|
||||
measures: telemetry.TelemetryEventMeasures;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { SqlOpsDataClient } from 'dataprotocol-client';
|
||||
import { ClientCapabilities, StaticFeature } from 'vscode-languageclient';
|
||||
import { Telemetry } from '../telemetry';
|
||||
import { TelemetryReporter } from '../telemetry';
|
||||
import * as contracts from './contracts';
|
||||
import * as Utils from '../utils';
|
||||
|
||||
@@ -19,7 +19,7 @@ export class TelemetryFeature implements StaticFeature {
|
||||
|
||||
initialize(): void {
|
||||
this._client.onNotification(contracts.TelemetryNotification.type, e => {
|
||||
Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures);
|
||||
TelemetryReporter.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import { AzureMonitorIconProvider } from './iconProvider';
|
||||
import { createAzureMonitorApi } from './azuremonitorApiFactory';
|
||||
import { AzureMonitorServer } from './azuremonitorServer';
|
||||
import { promises as fs } from 'fs';
|
||||
import { TelemetryReporter } from './telemetry';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -54,7 +55,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<IExten
|
||||
const server = new AzureMonitorServer();
|
||||
context.subscriptions.push(server);
|
||||
await server.start(appContext);
|
||||
|
||||
context.subscriptions.push(TelemetryReporter);
|
||||
return createAzureMonitorApi(appContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,118 +4,18 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import TelemetryReporter from '@microsoft/ads-extension-telemetry';
|
||||
import AdsTelemetryReporter from '@microsoft/ads-extension-telemetry';
|
||||
import { ErrorAction, ErrorHandler, Message, CloseAction } from 'vscode-languageclient';
|
||||
|
||||
import * as Utils from './utils';
|
||||
import * as Constants from './constants';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
const packageJson = require('../package.json');
|
||||
const viewKnownIssuesAction = localize('viewKnownIssuesText', "View Known Issues");
|
||||
|
||||
export interface ITelemetryEventProperties {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface ITelemetryEventMeasures {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters error paths to only include source files. Exported to support testing
|
||||
*/
|
||||
export function FilterErrorPath(line: string): string | undefined {
|
||||
if (line) {
|
||||
let values: string[] = line.split('/out/');
|
||||
if (values.length <= 1) {
|
||||
// Didn't match expected format
|
||||
return line;
|
||||
} else {
|
||||
return values[1];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export class Telemetry {
|
||||
private static reporter: TelemetryReporter;
|
||||
private static disabled: boolean;
|
||||
|
||||
/**
|
||||
* Disable telemetry reporting
|
||||
*/
|
||||
public static disable(): void {
|
||||
this.disabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the telemetry reporter for use.
|
||||
*/
|
||||
public static initialize(): void {
|
||||
if (typeof this.reporter === 'undefined') {
|
||||
// Check if the user has opted out of telemetry
|
||||
if (!vscode.workspace.getConfiguration('telemetry').get<boolean>('enableTelemetry', true)) {
|
||||
this.disable();
|
||||
return;
|
||||
}
|
||||
|
||||
let packageInfo = Utils.getPackageInfo(packageJson)!;
|
||||
this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a telemetry event for an exception
|
||||
*/
|
||||
public static sendTelemetryEventForException(
|
||||
err: any, methodName: string, _extensionConfigName: string): void {
|
||||
let stackArray: string[];
|
||||
let firstLine: string = '';
|
||||
if (err !== undefined && err.stack !== undefined) {
|
||||
stackArray = err.stack.split('\n');
|
||||
if (stackArray !== undefined && stackArray.length >= 2) {
|
||||
firstLine = stackArray[1]; // The fist line is the error message and we don't want to send that telemetry event
|
||||
firstLine = FilterErrorPath(firstLine)!;
|
||||
}
|
||||
}
|
||||
|
||||
// Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII
|
||||
this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine });
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a telemetry event using application insights
|
||||
*/
|
||||
public static sendTelemetryEvent(
|
||||
eventName: string,
|
||||
properties?: ITelemetryEventProperties,
|
||||
measures?: ITelemetryEventMeasures): void {
|
||||
|
||||
if (typeof this.disabled === 'undefined') {
|
||||
this.disabled = false;
|
||||
}
|
||||
|
||||
if (this.disabled || typeof (this.reporter) === 'undefined') {
|
||||
// Don't do anything if telemetry is disabled
|
||||
return;
|
||||
}
|
||||
|
||||
if (!properties || typeof properties === 'undefined') {
|
||||
properties = {};
|
||||
}
|
||||
|
||||
try {
|
||||
this.reporter.sendTelemetryEvent(eventName, properties, measures);
|
||||
} catch (telemetryErr) {
|
||||
// If sending telemetry event fails ignore it so it won't break the extension
|
||||
console.error('Failed to send telemetry event. error: ' + telemetryErr);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
const packageInfo = vscode.extensions.getExtension(Constants.packageName)?.packageJSON;
|
||||
export const TelemetryReporter = new AdsTelemetryReporter<string, string>(packageInfo?.name, packageInfo?.version, packageInfo?.aiKey);
|
||||
|
||||
/**
|
||||
* Handle Language Service client errors
|
||||
@@ -127,12 +27,12 @@ export class LanguageClientErrorHandler implements ErrorHandler {
|
||||
* @memberOf LanguageClientErrorHandler
|
||||
*/
|
||||
showOnErrorPrompt(): void {
|
||||
Telemetry.sendTelemetryEvent(Constants.serviceName + 'Crash');
|
||||
vscode.window.showErrorMessage(
|
||||
TelemetryReporter.sendTelemetryEvent(Constants.serviceName + 'Crash');
|
||||
void vscode.window.showErrorMessage(
|
||||
localize('serviceCrashMessage', "{0} component exited unexpectedly. Please restart Azure Data Studio.", Constants.serviceName),
|
||||
viewKnownIssuesAction).then(action => {
|
||||
if (action && action === viewKnownIssuesAction) {
|
||||
vscode.env.openExternal(vscode.Uri.parse(Constants.serviceCrashLink));
|
||||
void vscode.env.openExternal(vscode.Uri.parse(Constants.serviceCrashLink));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -163,5 +63,3 @@ export class LanguageClientErrorHandler implements ErrorHandler {
|
||||
return CloseAction.DoNotRestart;
|
||||
}
|
||||
}
|
||||
|
||||
Telemetry.initialize();
|
||||
|
||||
@@ -2,77 +2,43 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.3":
|
||||
version "3.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760"
|
||||
integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ==
|
||||
"@microsoft/ads-extension-telemetry@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-2.0.0.tgz#18ce267c7ed05c3b9dd99604a743e59f684c4e7c"
|
||||
integrity sha512-hExe/akhgq15v/h19LAFqiKNV6N9VxD19lOwGxEmO55yoWUm3E2cYealxvoYCwGDmSJfCbjR9fz/KM8Yz4XWAA==
|
||||
dependencies:
|
||||
"@microsoft/applicationinsights-core-js" "2.8.9"
|
||||
"@microsoft/applicationinsights-shims" "^2.0.2"
|
||||
"@microsoft/dynamicproto-js" "^1.1.7"
|
||||
"@vscode/extension-telemetry" "0.6.1"
|
||||
|
||||
"@microsoft/1ds-post-js@^3.2.3":
|
||||
version "3.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110"
|
||||
integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA==
|
||||
dependencies:
|
||||
"@microsoft/1ds-core-js" "3.2.8"
|
||||
"@microsoft/applicationinsights-shims" "^2.0.2"
|
||||
"@microsoft/dynamicproto-js" "^1.1.7"
|
||||
|
||||
"@microsoft/ads-extension-telemetry@^1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-1.3.2.tgz#d9cfb4bc7099df73e000b7bafa48bb748db924fe"
|
||||
integrity sha512-TG1TE7FPp5rBA9zYPVjralZut8Bq/b5XCgm0kmkLyoQyn3c9ntmWXFuNQPOXmgbIemg5YY1/7DHKrfNcO/igkQ==
|
||||
dependencies:
|
||||
"@vscode/extension-telemetry" "^0.6.2"
|
||||
|
||||
"@microsoft/ads-service-downloader@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-service-downloader/-/ads-service-downloader-1.0.4.tgz#94e13461d655d0864cbf93978247cbd1097e7863"
|
||||
integrity sha512-XVJ3RW4X5mzlPYeJnwTii5/6ywVib4UqCtrvxwRWSFe214Hi8jO2zNxzcpamiTCnHm2b8wZAuWGfsvIShbf/yg==
|
||||
"@microsoft/ads-service-downloader@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-service-downloader/-/ads-service-downloader-1.2.1.tgz#b0216cb0486db6697ccf9e166ec81a9764bdb3aa"
|
||||
integrity sha512-xB3VUaEYauXtm3zFko5clHnhF7l7QbX0AnnULGDrd2JANu1zThBR6toUQ9+zAMO+0KCHs71XLSuoP2A24G3WCw==
|
||||
dependencies:
|
||||
async-retry "^1.2.3"
|
||||
eventemitter2 "^5.0.1"
|
||||
http-proxy-agent "^2.1.0"
|
||||
https-proxy-agent "^2.2.3"
|
||||
http-proxy-agent "^5.0.0"
|
||||
https-proxy-agent "^5.0.1"
|
||||
mkdirp "1.0.4"
|
||||
tar "^6.1.11"
|
||||
tmp "^0.0.33"
|
||||
yauzl "^2.10.0"
|
||||
|
||||
"@microsoft/applicationinsights-core-js@2.8.9":
|
||||
version "2.8.9"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b"
|
||||
integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w==
|
||||
"@tootallnate/once@2":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
|
||||
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
|
||||
|
||||
"@vscode/extension-telemetry@0.6.1":
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.1.tgz#f8d1f7145baf932b75077c48107edff48501fc14"
|
||||
integrity sha512-Y4Oc8yGURGVF4WhCZcu+EVy+MAIeQDLDVeDlLn59H0C1w+7xr8dL2ZtDBioy+Hog1Edrd6zOwr3Na7xe1iC/UA==
|
||||
|
||||
agent-base@6:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||
dependencies:
|
||||
"@microsoft/applicationinsights-shims" "2.0.2"
|
||||
"@microsoft/dynamicproto-js" "^1.1.7"
|
||||
|
||||
"@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085"
|
||||
integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg==
|
||||
|
||||
"@microsoft/dynamicproto-js@^1.1.7":
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2"
|
||||
integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA==
|
||||
|
||||
"@vscode/extension-telemetry@^0.6.2":
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e"
|
||||
integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w==
|
||||
dependencies:
|
||||
"@microsoft/1ds-core-js" "^3.2.3"
|
||||
"@microsoft/1ds-post-js" "^3.2.3"
|
||||
|
||||
agent-base@4, agent-base@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
|
||||
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
debug "4"
|
||||
|
||||
async-retry@^1.2.3:
|
||||
version "1.3.1"
|
||||
@@ -115,31 +81,12 @@ concat-map@0.0.1:
|
||||
dependencies:
|
||||
vscode-languageclient "5.2.1"
|
||||
|
||||
debug@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||
debug@4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^3.1.0:
|
||||
version "3.2.7"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
||||
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
es6-promise@^4.0.3:
|
||||
version "4.2.8"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
||||
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
|
||||
|
||||
es6-promisify@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
|
||||
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
|
||||
dependencies:
|
||||
es6-promise "^4.0.3"
|
||||
ms "2.1.2"
|
||||
|
||||
escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
@@ -202,21 +149,22 @@ glob@^7.0.5:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
http-proxy-agent@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
|
||||
integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
|
||||
http-proxy-agent@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
|
||||
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
|
||||
dependencies:
|
||||
agent-base "4"
|
||||
debug "3.1.0"
|
||||
"@tootallnate/once" "2"
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
https-proxy-agent@^2.2.3:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
||||
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
|
||||
https-proxy-agent@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
||||
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
|
||||
dependencies:
|
||||
agent-base "^4.3.0"
|
||||
debug "^3.1.0"
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
@@ -258,15 +206,10 @@ mkdirp@1.0.4, mkdirp@^1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
|
||||
@@ -19,7 +19,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -179,6 +179,20 @@
|
||||
"isRequired": false,
|
||||
"isArray": false
|
||||
},
|
||||
{
|
||||
"specialValueType": null,
|
||||
"isIdentity": false,
|
||||
"name": "commandTimeout",
|
||||
"displayName": "%cms.connectionOptions.commandTimeout.displayName%",
|
||||
"description": "%cms.connectionOptions.commandTimeout.description%",
|
||||
"groupName": "%cms.connectionOptions.groupName.initialization%",
|
||||
"valueType": "number",
|
||||
"defaultValue": "30",
|
||||
"objectType": null,
|
||||
"categoryValues": null,
|
||||
"isRequired": false,
|
||||
"isArray": false
|
||||
},
|
||||
{
|
||||
"specialValueType": null,
|
||||
"isIdentity": false,
|
||||
@@ -223,10 +237,23 @@
|
||||
"displayName": "%cms.connectionOptions.encrypt.displayName%",
|
||||
"description": "%cms.connectionOptions.encrypt.description%",
|
||||
"groupName": "%cms.connectionOptions.groupName.security%",
|
||||
"valueType": "boolean",
|
||||
"valueType": "category",
|
||||
"defaultValue": "true",
|
||||
"objectType": null,
|
||||
"categoryValues": null,
|
||||
"categoryValues": [
|
||||
{
|
||||
"displayName": "%cms.connectionOptions.encrypt.categoryValues.true%",
|
||||
"name": "true"
|
||||
},
|
||||
{
|
||||
"displayName": "%cms.connectionOptions.encrypt.categoryValues.strict%",
|
||||
"name": "strict"
|
||||
},
|
||||
{
|
||||
"displayName": "%cms.connectionOptions.encrypt.categoryValues.false%",
|
||||
"name": "false"
|
||||
}
|
||||
],
|
||||
"isRequired": false,
|
||||
"isArray": false,
|
||||
"showOnConnectionDialog": true
|
||||
@@ -245,6 +272,20 @@
|
||||
"isRequired": false,
|
||||
"isArray": false
|
||||
},
|
||||
{
|
||||
"specialValueType": null,
|
||||
"isIdentity": false,
|
||||
"name": "hostNameInCertificate",
|
||||
"displayName": "%cms.connectionOptions.hostNameInCertificate.displayName%",
|
||||
"description": "%cms.connectionOptions.hostNameInCertificate.description%",
|
||||
"groupName": "%cms.connectionOptions.groupName.security%",
|
||||
"valueType": "string",
|
||||
"defaultValue": null,
|
||||
"objectType": null,
|
||||
"categoryValues": null,
|
||||
"isRequired": false,
|
||||
"isArray": false
|
||||
},
|
||||
{
|
||||
"specialValueType": null,
|
||||
"isIdentity": false,
|
||||
|
||||
@@ -62,16 +62,23 @@
|
||||
"cms.connectionOptions.applicationIntent.description": "Declares the application workload type when connecting to a server",
|
||||
"cms.connectionOptions.connectTimeout.displayName": "Connect timeout",
|
||||
"cms.connectionOptions.connectTimeout.description": "The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error",
|
||||
"cms.connectionOptions.commandTimeout.displayName": "Command timeout",
|
||||
"cms.connectionOptions.commandTimeout.description": "The length of time (in seconds) to wait for a command to execute before terminating the attempt and generating an error",
|
||||
"cms.connectionOptions.currentLanguage.displayName": "Current language",
|
||||
"cms.connectionOptions.currentLanguage.description": "The SQL Server language record name",
|
||||
"cms.connectionOptions.columnEncryptionSetting.displayName": "Column encryption",
|
||||
"cms.connectionOptions.columnEncryptionSetting.description": "Default column encryption setting for all the commands on the connection",
|
||||
"cms.connectionOptions.encrypt.displayName": "Encrypt",
|
||||
"cms.connectionOptions.encrypt.description": "When true, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed",
|
||||
"cms.connectionOptions.encrypt.description": "When 'Mandatory' or 'Strict', SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. When set to 'Strict', SQL Server uses TDS 8.0 for all data transfer between the client and server. 'Strict' is supported on SQL Server 2022 onwards.",
|
||||
"cms.connectionOptions.encrypt.categoryValues.strict": "Strict",
|
||||
"cms.connectionOptions.encrypt.categoryValues.true": "Mandatory (True)",
|
||||
"cms.connectionOptions.encrypt.categoryValues.false": "Optional (False)",
|
||||
"cms.connectionOptions.persistSecurityInfo.displayName": "Persist security info",
|
||||
"cms.connectionOptions.persistSecurityInfo.description": "When false, security-sensitive information, such as the password, is not returned as part of the connection",
|
||||
"cms.connectionOptions.hostNameInCertificate.displayName": "Host name in certificate",
|
||||
"cms.connectionOptions.hostNameInCertificate.description": "Indicates the host name specified in TLS certificate that will be used by SQL Server to validate server certificate. When not specified, the server name is used by default for certificate validation.",
|
||||
"cms.connectionOptions.trustServerCertificate.displayName": "Trust server certificate",
|
||||
"cms.connectionOptions.trustServerCertificate.description": "When true (and encrypt=true), SQL Server uses SSL encryption for all data sent between the client and server without validating the server certificate",
|
||||
"cms.connectionOptions.trustServerCertificate.description": "When true (and encrypt=true), SQL Server uses SSL encryption for all data sent between the client and server without validating the server certificate. This property is not supported with Encrypt=Strict.",
|
||||
"cms.connectionOptions.attachedDBFileName.displayName": "Attached DB file name",
|
||||
"cms.connectionOptions.attachedDBFileName.description": "The name of the primary file, including the full path name, of an attachable database",
|
||||
"cms.connectionOptions.contextConnection.displayName": "Context connection",
|
||||
|
||||
@@ -23,9 +23,13 @@ To learn more about Data-Tier Applications and working with dacpac and bacpac fi
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Telemetry
|
||||
|
||||
This extension collects telemetry data, which is used to help understand how to improve the product. For example, this usage data helps to debug issues, such as slow start-up times, and to prioritize new features. While we appreciate the insights this data provides, we also know that not everyone wants to send usage data and you can disable telemetry as described in the Azure Data Studio [disable telemetry reporting](https://github.com/Microsoft/azuredatastudio/wiki/How-to-Disable-Telemetry-Reporting#how-to-disable-telemetry-reporting) documentation.
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "dacpac",
|
||||
"displayName": "SQL Server Dacpac",
|
||||
"description": "SQL Server Dacpac for Azure Data Studio.",
|
||||
"version": "1.12.0",
|
||||
"version": "1.13.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": false,
|
||||
"engines": {
|
||||
@@ -92,7 +92,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/ads-extension-telemetry": "^1.3.2",
|
||||
"@microsoft/ads-extension-telemetry": "^2.0.0",
|
||||
"htmlparser2": "^3.10.1",
|
||||
"vscode-nls": "^4.0.0"
|
||||
},
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { DataTierApplicationWizard } from './wizard/dataTierApplicationWizard';
|
||||
import { TelemetryReporter } from './telemetry';
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand('dacFx.start', (profile: azdata.IConnectionProfile) => new DataTierApplicationWizard(undefined, context).start(profile));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('dacFx.start', (profile: azdata.IConnectionProfile) => new DataTierApplicationWizard(undefined, context).start(profile)));
|
||||
context.subscriptions.push(TelemetryReporter);
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
|
||||
@@ -16,5 +16,5 @@ export interface DacFxDataModel {
|
||||
filePath: string;
|
||||
version: string;
|
||||
upgradeExisting: boolean;
|
||||
potentialDataLoss: boolean;
|
||||
potentialDataLoss?: boolean;
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ export class DataTierApplicationWizard {
|
||||
additionalMeasurements.totalDurationMs = (new Date().getTime() - deployStartTime);
|
||||
additionalMeasurements.deployDacpacFileSizeBytes = await utils.tryGetFileSize(this.model.filePath);
|
||||
additionalProps.upgradeExistingDatabase = this.model.upgradeExisting.toString();
|
||||
additionalProps.potentialDataLoss = this.model.potentialDataLoss.toString();
|
||||
additionalProps.potentialDataLoss = this.model.potentialDataLoss?.toString();
|
||||
|
||||
this.sendDacFxOperationTelemetryEvent(result, TelemetryAction.DeployDacpac, additionalProps, additionalMeasurements);
|
||||
|
||||
@@ -493,7 +493,7 @@ export class DataTierApplicationWizard {
|
||||
.withAdditionalMeasurements(additionalMeasurements)
|
||||
.send();
|
||||
} else {
|
||||
TelemetryReporter.createErrorEvent(TelemetryViews.DataTierApplicationWizard, telemetryAction)
|
||||
TelemetryReporter.createErrorEvent2(TelemetryViews.DataTierApplicationWizard, telemetryAction)
|
||||
.withAdditionalProperties(additionalProps)
|
||||
.withAdditionalMeasurements(additionalMeasurements)
|
||||
.send();
|
||||
|
||||
@@ -137,7 +137,8 @@ export class DeployConfigPage extends DacFxConfigPage {
|
||||
protected async createDeployDatabaseDropdown(): Promise<azdata.FormComponent> {
|
||||
const targetDatabaseTitle = loc.databaseName;
|
||||
this.databaseDropdown = this.view.modelBuilder.dropDown().withProps({
|
||||
ariaLabel: targetDatabaseTitle
|
||||
ariaLabel: targetDatabaseTitle,
|
||||
required: true
|
||||
}).component();
|
||||
|
||||
//Handle database changes
|
||||
|
||||
@@ -182,43 +182,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
|
||||
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
|
||||
|
||||
"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3":
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7"
|
||||
integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g==
|
||||
"@microsoft/ads-extension-telemetry@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-2.0.0.tgz#18ce267c7ed05c3b9dd99604a743e59f684c4e7c"
|
||||
integrity sha512-hExe/akhgq15v/h19LAFqiKNV6N9VxD19lOwGxEmO55yoWUm3E2cYealxvoYCwGDmSJfCbjR9fz/KM8Yz4XWAA==
|
||||
dependencies:
|
||||
"@microsoft/applicationinsights-core-js" "2.8.4"
|
||||
"@microsoft/applicationinsights-shims" "^2.0.1"
|
||||
"@microsoft/dynamicproto-js" "^1.1.6"
|
||||
|
||||
"@microsoft/1ds-post-js@^3.2.3":
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216"
|
||||
integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ==
|
||||
dependencies:
|
||||
"@microsoft/1ds-core-js" "3.2.3"
|
||||
"@microsoft/applicationinsights-shims" "^2.0.1"
|
||||
"@microsoft/dynamicproto-js" "^1.1.6"
|
||||
|
||||
"@microsoft/ads-extension-telemetry@^1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-1.3.2.tgz#d9cfb4bc7099df73e000b7bafa48bb748db924fe"
|
||||
integrity sha512-TG1TE7FPp5rBA9zYPVjralZut8Bq/b5XCgm0kmkLyoQyn3c9ntmWXFuNQPOXmgbIemg5YY1/7DHKrfNcO/igkQ==
|
||||
dependencies:
|
||||
"@vscode/extension-telemetry" "^0.6.2"
|
||||
|
||||
"@microsoft/applicationinsights-core-js@2.8.4":
|
||||
version "2.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596"
|
||||
integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ==
|
||||
dependencies:
|
||||
"@microsoft/applicationinsights-shims" "2.0.1"
|
||||
"@microsoft/dynamicproto-js" "^1.1.6"
|
||||
|
||||
"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd"
|
||||
integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ==
|
||||
"@vscode/extension-telemetry" "0.6.1"
|
||||
|
||||
"@microsoft/azdata-test@^2.0.3":
|
||||
version "2.0.3"
|
||||
@@ -232,11 +201,6 @@
|
||||
rimraf "^2.6.3"
|
||||
typemoq "^2.1.0"
|
||||
|
||||
"@microsoft/dynamicproto-js@^1.1.6":
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc"
|
||||
integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg==
|
||||
|
||||
"@microsoft/vscodetestcover@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/vscodetestcover/-/vscodetestcover-1.2.1.tgz#65f25132075a465a7a99688204486ee2b65ac07b"
|
||||
@@ -331,13 +295,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz#681df970358c82836b42f989188d133e218c458e"
|
||||
integrity sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==
|
||||
|
||||
"@vscode/extension-telemetry@^0.6.2":
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e"
|
||||
integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w==
|
||||
dependencies:
|
||||
"@microsoft/1ds-core-js" "^3.2.3"
|
||||
"@microsoft/1ds-post-js" "^3.2.3"
|
||||
"@vscode/extension-telemetry@0.6.1":
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.1.tgz#f8d1f7145baf932b75077c48107edff48501fc14"
|
||||
integrity sha512-Y4Oc8yGURGVF4WhCZcu+EVy+MAIeQDLDVeDlLn59H0C1w+7xr8dL2ZtDBioy+Hog1Edrd6zOwr3Na7xe1iC/UA==
|
||||
|
||||
agent-base@4, agent-base@^4.3.0:
|
||||
version "4.3.0"
|
||||
|
||||
@@ -16,9 +16,13 @@ This extension is built into Azure Data Studio and will show up under the list o
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Telemetry
|
||||
|
||||
This extensions collects telemetry data, which is used to help understand how to improve the product. For example, this usage data helps to debug issues, such as slow start-up times, and to prioritize new features. While we appreciate the insights this data provides, we also know that not everyone wants to send usage data and you can disable telemetry as described [here for Azure Data Studio](https://github.com/Microsoft/azuredatastudio/wiki/How-to-Disable-Telemetry-Reporting#how-to-disable-telemetry-reporting) or [here for VS Code](https://code.visualstudio.com/docs/getstarted/telemetry#_disable-telemetry-reporting).
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
"azdata": ">=1.25.0"
|
||||
},
|
||||
"activationEvents": [
|
||||
"*"
|
||||
"onView:dataworkspace.views.main",
|
||||
"onCommand:projects.openExisting",
|
||||
"onCommand:projects.new",
|
||||
"onCommand:sqlDatabaseProjects.createProjectFromDatabase",
|
||||
"onCommand:sqlDatabaseProjects.updateProjectFromDatabase"
|
||||
],
|
||||
"main": "./out/main",
|
||||
"repository": {
|
||||
@@ -158,8 +162,7 @@
|
||||
"id": "dataworkspace.views.main",
|
||||
"name": "%main-view-name%",
|
||||
"contextualTitle": "%data-workspace-view-container-name%",
|
||||
"icon": "images/data-workspace.svg",
|
||||
"when": "isProjectProviderAvailable"
|
||||
"icon": "images/data-workspace.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -178,7 +181,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"fast-glob": "^3.2.7",
|
||||
"@microsoft/ads-extension-telemetry": "^1.3.2",
|
||||
"@microsoft/ads-extension-telemetry": "^2.0.0",
|
||||
"vscode-nls": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -21,6 +21,9 @@ export const noPreviousData = (tableName: string): string => { return localize('
|
||||
export const gitCloneMessage = (url: string): string => { return localize('gitCloneMessage', "Cloning git repository '{0}'...", url); };
|
||||
export const gitCloneError = localize('gitCloneError', "Error during git clone. View git output for more details");
|
||||
export const openedProjectsUndefinedAfterRefresh = localize('openedProjectsUndefinedAfterRefresh', "List of opened projects should not be undefined after refresh from disk.");
|
||||
export const dragAndDropNotSupported = localize('dragAndDropNotSupported', "This project type does not support drag and drop.");
|
||||
export const onlyMovingOneFileIsSupported = localize('onlyMovingOneFileIsSupported', "Only moving one file at a time is supported.");
|
||||
export const noProjectProvidingExtensionsInstalled = localize('noProjectProvidingExtensionsInstalled', "No database project extensions are installed. Please install a database project extension to use this feature.");
|
||||
|
||||
// UI
|
||||
export const OkButtonText = localize('dataworkspace.ok', "OK");
|
||||
@@ -58,6 +61,14 @@ export const SdkLearnMorePlaceholder = localize('dataworkspace.sdkLearnMorePlace
|
||||
export const Default = localize('dataworkspace.default', "Default");
|
||||
export const SelectTargetPlatform = localize('dataworkspace.selectTargetPlatform', "Select Target Platform");
|
||||
export const LocalDevInfo = (target: string) => localize('LocalDevInfo', "Click \"Learn more\" button for more information about local development experience to {0}", target);
|
||||
export const undefinedFilenameErrorMessage = localize('undefinedFilenameErrorMessage', "Undefined name");
|
||||
export const filenameEndingIsPeriodErrorMessage = localize('filenameEndingInPeriodErrorMessage', "File name cannot end with a period");
|
||||
export const whitespaceFilenameErrorMessage = localize('whitespaceFilenameErrorMessage', "File name cannot be whitespace");
|
||||
export const invalidFileCharsErrorMessage = localize('invalidFileCharsErrorMessage', "Invalid file characters");
|
||||
export const reservedWindowsFilenameErrorMessage = localize('reservedWindowsFilenameErrorMessage', "This file name is reserved for use by Windows. Choose another name and try again");
|
||||
export const reservedValueErrorMessage = localize('reservedValueErrorMessage', "Reserved file name. Choose another name and try again");
|
||||
export const trailingWhitespaceErrorMessage = localize('trailingWhitespaceErrorMessage', "File name cannot end with a whitespace");
|
||||
export const tooLongFilenameErrorMessage = localize('tooLongFilenameErrorMessage', "File name cannot be over 255 characters");
|
||||
|
||||
//Open Existing Dialog
|
||||
export const OpenExistingDialogTitle = localize('dataworkspace.openExistingDialogTitle', "Open Existing Project");
|
||||
|
||||
@@ -8,6 +8,8 @@ import { IExtension, IProjectType } from 'dataworkspace';
|
||||
import { WorkspaceService } from '../services/workspaceService';
|
||||
import { defaultProjectSaveLocation } from './projectLocationHelper';
|
||||
import { openSpecificProjectNewProjectDialog } from '../dialogs/newProjectDialog';
|
||||
import { isValidBasename, isValidBasenameErrorMessage, isValidFilenameCharacter, sanitizeStringForFilename } from './pathUtilsHelper';
|
||||
import { noProjectProvidingExtensionsInstalled } from './constants';
|
||||
|
||||
export class DataWorkspaceExtension implements IExtension {
|
||||
constructor(private workspaceService: WorkspaceService) {
|
||||
@@ -38,7 +40,27 @@ export class DataWorkspaceExtension implements IExtension {
|
||||
}
|
||||
|
||||
openSpecificProjectNewProjectDialog(projectType: IProjectType): Promise<vscode.Uri | undefined> {
|
||||
if (!this.workspaceService.isProjectProviderAvailable) {
|
||||
void vscode.window.showErrorMessage(noProjectProvidingExtensionsInstalled);
|
||||
}
|
||||
|
||||
return openSpecificProjectNewProjectDialog(projectType, this.workspaceService);
|
||||
}
|
||||
|
||||
isValidFilenameCharacter(c: string): boolean {
|
||||
return isValidFilenameCharacter(c);
|
||||
}
|
||||
|
||||
sanitizeStringForFilename(s: string): string {
|
||||
return sanitizeStringForFilename(s);
|
||||
}
|
||||
|
||||
isValidBasename(name?: string): boolean {
|
||||
return isValidBasename(name);
|
||||
}
|
||||
|
||||
isValidBasenameErrorMessage(name?: string): string {
|
||||
return isValidBasenameErrorMessage(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,8 +85,6 @@ export interface IWorkspaceService {
|
||||
*/
|
||||
gitCloneProject(url: string, localClonePath: string): Promise<void>;
|
||||
|
||||
readonly isProjectProviderAvailable: boolean;
|
||||
|
||||
/**
|
||||
* Event fires when projects in workspace changes
|
||||
*/
|
||||
|
||||
136
extensions/data-workspace/src/common/pathUtilsHelper.ts
Normal file
136
extensions/data-workspace/src/common/pathUtilsHelper.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as constants from './constants';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g;
|
||||
const UNIX_INVALID_FILE_CHARS = /[\\/]/g;
|
||||
const isWindows = os.platform() === 'win32';
|
||||
const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i;
|
||||
|
||||
/**
|
||||
* Determines if a given character is a valid filename character
|
||||
* @param c Character to validate
|
||||
*/
|
||||
export function isValidFilenameCharacter(c: string): boolean {
|
||||
// only a character should be passed
|
||||
if (!c || c.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
WINDOWS_INVALID_FILE_CHARS.lastIndex = 0;
|
||||
UNIX_INVALID_FILE_CHARS.lastIndex = 0;
|
||||
if (isWindows && WINDOWS_INVALID_FILE_CHARS.test(c)) {
|
||||
return false;
|
||||
} else if (!isWindows && UNIX_INVALID_FILE_CHARS.test(c)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces invalid filename characters in a string with underscores
|
||||
* @param s The string to be sanitized for a filename
|
||||
*/
|
||||
export function sanitizeStringForFilename(s: string): string {
|
||||
// replace invalid characters with an underscore
|
||||
let result = '';
|
||||
for (let i = 0; i < s.length; ++i) {
|
||||
result += isValidFilenameCharacter(s[i]) ? s[i] : '_';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the string is a valid filename
|
||||
* Logic is copied from src\vs\base\common\extpath.ts
|
||||
* @param name filename to check
|
||||
*/
|
||||
export function isValidBasename(name?: string): boolean {
|
||||
const invalidFileChars = isWindows ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS;
|
||||
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isWindows && name[name.length - 1] === '.') {
|
||||
return false; // Windows: file cannot end with a "."
|
||||
}
|
||||
|
||||
let basename = path.parse(name).name;
|
||||
if (!basename || basename.length === 0 || /^\s+$/.test(basename)) {
|
||||
return false; // require a name that is not just whitespace
|
||||
}
|
||||
|
||||
invalidFileChars.lastIndex = 0;
|
||||
if (invalidFileChars.test(basename)) {
|
||||
return false; // check for certain invalid file characters
|
||||
}
|
||||
|
||||
if (isWindows && WINDOWS_FORBIDDEN_NAMES.test(basename)) {
|
||||
return false; // check for certain invalid file names
|
||||
}
|
||||
|
||||
if (basename === '.' || basename === '..') {
|
||||
return false; // check for reserved values
|
||||
}
|
||||
|
||||
if (isWindows && basename.length !== basename.trim().length) {
|
||||
return false; // Windows: file cannot end with a whitespace
|
||||
}
|
||||
|
||||
if (basename.length > 255) {
|
||||
return false; // most file systems do not allow files > 255 length
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns specific error message if file name is invalid
|
||||
* Logic is copied from src\vs\base\common\extpath.ts
|
||||
* @param name filename to check
|
||||
*/
|
||||
export function isValidBasenameErrorMessage(name?: string): string {
|
||||
const invalidFileChars = isWindows ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS;
|
||||
if (!name) {
|
||||
return constants.undefinedFilenameErrorMessage;
|
||||
}
|
||||
|
||||
if (isWindows && name[name.length - 1] === '.') {
|
||||
return constants.filenameEndingIsPeriodErrorMessage; // Windows: file cannot end with a "."
|
||||
}
|
||||
|
||||
let basename = path.parse(name).name;
|
||||
if (!basename || basename.length === 0 || /^\s+$/.test(basename)) {
|
||||
return constants.whitespaceFilenameErrorMessage; // require a name that is not just whitespace
|
||||
}
|
||||
|
||||
invalidFileChars.lastIndex = 0;
|
||||
if (invalidFileChars.test(basename)) {
|
||||
return constants.invalidFileCharsErrorMessage; // check for certain invalid file characters
|
||||
}
|
||||
|
||||
if (isWindows && WINDOWS_FORBIDDEN_NAMES.test(basename)) {
|
||||
return constants.reservedWindowsFilenameErrorMessage; // check for certain invalid file names
|
||||
}
|
||||
|
||||
if (basename === '.' || basename === '..') {
|
||||
return constants.reservedValueErrorMessage; // check for reserved values
|
||||
}
|
||||
|
||||
if (isWindows && basename.length !== basename.trim().length) {
|
||||
return constants.trailingWhitespaceErrorMessage; // Windows: file cannot end with a whitespace
|
||||
}
|
||||
|
||||
if (basename.length > 255) {
|
||||
return constants.tooLongFilenameErrorMessage; // most file systems do not allow files > 255 length
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { IWorkspaceService } from './interfaces';
|
||||
import { ProjectsFailedToLoad, UnknownProjectsError } from './constants';
|
||||
import { dragAndDropNotSupported, onlyMovingOneFileIsSupported, ProjectsFailedToLoad, UnknownProjectsError } from './constants';
|
||||
import { WorkspaceTreeItem } from 'dataworkspace';
|
||||
import { TelemetryReporter } from './telemetry';
|
||||
import Logger from './logger';
|
||||
@@ -14,11 +14,16 @@ import Logger from './logger';
|
||||
/**
|
||||
* Tree data provider for the workspace main view
|
||||
*/
|
||||
export class WorkspaceTreeDataProvider implements vscode.TreeDataProvider<WorkspaceTreeItem>{
|
||||
export class WorkspaceTreeDataProvider implements vscode.TreeDataProvider<WorkspaceTreeItem>, vscode.TreeDragAndDropController<WorkspaceTreeItem> {
|
||||
dropMimeTypes = ['application/vnd.code.tree.WorkspaceTreeDataProvider'];
|
||||
dragMimeTypes = []; // The recommended mime type of the tree (`application/vnd.code.tree.WorkspaceTreeDataProvider`) is automatically added.
|
||||
|
||||
constructor(private _workspaceService: IWorkspaceService) {
|
||||
this._workspaceService.onDidWorkspaceProjectsChange(() => {
|
||||
return this.refresh();
|
||||
});
|
||||
|
||||
vscode.window.createTreeView('dataworkspace.views.main', { canSelectMany: false, treeDataProvider: this, dragAndDropController: this });
|
||||
}
|
||||
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<void | WorkspaceTreeItem | null | undefined> | undefined = new vscode.EventEmitter<WorkspaceTreeItem | undefined | void>();
|
||||
@@ -108,4 +113,42 @@ export class WorkspaceTreeDataProvider implements vscode.TreeDataProvider<Worksp
|
||||
|
||||
typeMetric[ext]++;
|
||||
}
|
||||
|
||||
handleDrag(treeItems: readonly WorkspaceTreeItem[], dataTransfer: vscode.DataTransfer): void | Thenable<void> {
|
||||
dataTransfer.set('application/vnd.code.tree.WorkspaceTreeDataProvider', new vscode.DataTransferItem(treeItems.map(t => t.element)));
|
||||
}
|
||||
|
||||
async handleDrop(target: WorkspaceTreeItem | undefined, sources: vscode.DataTransfer): Promise<void> {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
const transferItem = sources.get('application/vnd.code.tree.WorkspaceTreeDataProvider');
|
||||
|
||||
// Only support moving one file at a time
|
||||
// canSelectMany is set to false for the WorkspaceTreeDataProvider, so this condition should never be true
|
||||
if (transferItem?.value.length > 1) {
|
||||
void vscode.window.showErrorMessage(onlyMovingOneFileIsSupported);
|
||||
return;
|
||||
}
|
||||
|
||||
const projectUri = transferItem?.value[0].projectFileUri;
|
||||
if (!projectUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
const projectProvider = await this._workspaceService.getProjectProvider(projectUri);
|
||||
if (!projectProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!projectProvider?.supportsDragAndDrop || !projectProvider.moveFile) {
|
||||
void vscode.window.showErrorMessage(dragAndDropNotSupported);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the file
|
||||
await projectProvider!.moveFile(projectUri, transferItem?.value[0], target);
|
||||
void this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
42
extensions/data-workspace/src/dataworkspace.d.ts
vendored
42
extensions/data-workspace/src/dataworkspace.d.ts
vendored
@@ -54,6 +54,32 @@ declare module 'dataworkspace' {
|
||||
* @returns the uri of the created the project or undefined if no project was created
|
||||
*/
|
||||
openSpecificProjectNewProjectDialog(projectType: IProjectType): Promise<vscode.Uri | undefined>;
|
||||
|
||||
/**
|
||||
* Determines if a given character is a valid filename character
|
||||
* @param c Character to validate
|
||||
*/
|
||||
isValidFilenameCharacter(c: string): boolean;
|
||||
|
||||
/**
|
||||
* Replaces invalid filename characters in a string with underscores
|
||||
* @param s The string to be sanitized for a filename
|
||||
*/
|
||||
sanitizeStringForFilename(s: string): string;
|
||||
|
||||
/**
|
||||
* Returns true if the string is a valid filename
|
||||
* Logic is copied from src\vs\base\common\extpath.ts
|
||||
* @param name filename to check
|
||||
*/
|
||||
isValidBasename(name: string | null | undefined): boolean;
|
||||
|
||||
/**
|
||||
* Returns specific error message if file name is invalid
|
||||
* Logic is copied from src\vs\base\common\extpath.ts
|
||||
* @param name filename to check
|
||||
*/
|
||||
isValidBasenameErrorMessage(name: string | null | undefined): string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +120,20 @@ declare module 'dataworkspace' {
|
||||
/**
|
||||
* Gets the project image to be used as background in dashboard container
|
||||
*/
|
||||
readonly image?: azdata.ThemedIconPath;
|
||||
readonly image?: azdata.ThemedIconPath;
|
||||
|
||||
/**
|
||||
* Whether or not the tree data provider supports drag and drop
|
||||
*/
|
||||
readonly supportsDragAndDrop?: boolean;
|
||||
|
||||
/**
|
||||
* Moves a file from the source to target location. Must be implemented if supportsDragAndDrop is true
|
||||
* @param projectUri
|
||||
* @param source
|
||||
* @param target
|
||||
*/
|
||||
moveFile?(projectUri: vscode.Uri, source: any, target: WorkspaceTreeItem): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,4 +272,5 @@ declare module 'dataworkspace' {
|
||||
* Union type representing data types in dashboard table
|
||||
*/
|
||||
export type IDashboardColumnType = 'string' | 'icon';
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { IconPathHelper } from '../common/iconHelper';
|
||||
import { defaultProjectSaveLocation } from '../common/projectLocationHelper';
|
||||
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
||||
import { WorkspaceService } from '../services/workspaceService';
|
||||
import { isValidBasename, isValidBasenameErrorMessage } from '../common/pathUtilsHelper';
|
||||
|
||||
class NewProjectDialogModel {
|
||||
projectTypeId: string = '';
|
||||
@@ -94,7 +95,7 @@ export class NewProjectDialog extends DialogBase {
|
||||
}
|
||||
catch (err) {
|
||||
|
||||
TelemetryReporter.createErrorEvent(TelemetryViews.NewProjectDialog, TelemetryActions.NewProjectDialogCompleted)
|
||||
TelemetryReporter.createErrorEvent2(TelemetryViews.NewProjectDialog, TelemetryActions.NewProjectDialogCompleted, err)
|
||||
.withAdditionalProperties({ projectFileExtension: this.model.projectFileExtension, projectTemplateId: this.model.projectTypeId, error: err?.message ? err.message : err })
|
||||
.send();
|
||||
|
||||
@@ -174,16 +175,25 @@ export class NewProjectDialog extends DialogBase {
|
||||
}
|
||||
}));
|
||||
|
||||
const projectNameTextBox = view.modelBuilder.inputBox().withProps({
|
||||
ariaLabel: constants.ProjectNameTitle,
|
||||
placeHolder: constants.ProjectNamePlaceholder,
|
||||
required: true,
|
||||
width: constants.DefaultInputWidth
|
||||
}).component();
|
||||
const projectNameTextBox = view.modelBuilder.inputBox().withValidation(
|
||||
component => isValidBasename(component.value)
|
||||
)
|
||||
.withProps({
|
||||
ariaLabel: constants.ProjectNameTitle,
|
||||
placeHolder: constants.ProjectNamePlaceholder,
|
||||
required: true,
|
||||
width: constants.DefaultInputWidth
|
||||
}).component();
|
||||
|
||||
this.register(projectNameTextBox.onTextChanged(() => {
|
||||
this.model.name = projectNameTextBox.value!;
|
||||
return projectNameTextBox.updateProperty('title', projectNameTextBox.value);
|
||||
this.register(projectNameTextBox.onTextChanged(text => {
|
||||
const errorMessage = isValidBasenameErrorMessage(text);
|
||||
if (errorMessage) {
|
||||
// Set validation error message if project name is invalid
|
||||
return void projectNameTextBox.updateProperty('validationErrorMessage', errorMessage);
|
||||
} else {
|
||||
this.model.name = projectNameTextBox.value!;
|
||||
return projectNameTextBox.updateProperty('title', projectNameTextBox.value);
|
||||
}
|
||||
}));
|
||||
|
||||
const locationTextBox = view.modelBuilder.inputBox().withProps({
|
||||
|
||||
@@ -9,6 +9,7 @@ import * as constants from '../common/constants';
|
||||
import { directoryExist, showInfoMessageWithLearnMoreLink } from '../common/utils';
|
||||
import { defaultProjectSaveLocation } from '../common/projectLocationHelper';
|
||||
import { WorkspaceService } from '../services/workspaceService';
|
||||
import { isValidBasename, isValidBasenameErrorMessage } from '../common/pathUtilsHelper';
|
||||
|
||||
/**
|
||||
* Create flow for a New Project using only VS Code-native APIs such as QuickPick
|
||||
@@ -39,7 +40,7 @@ export async function createNewProjectWithQuickpick(workspaceService: WorkspaceS
|
||||
{
|
||||
title: constants.EnterProjectName,
|
||||
validateInput: (value) => {
|
||||
return value ? undefined : constants.NameCannotBeEmpty;
|
||||
return isValidBasename(value) ? undefined : isValidBasenameErrorMessage(value);
|
||||
},
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
|
||||
@@ -10,12 +10,13 @@ import { WorkspaceTreeItem, IExtension } from 'dataworkspace';
|
||||
import { DataWorkspaceExtension } from './common/dataWorkspaceExtension';
|
||||
import { NewProjectDialog } from './dialogs/newProjectDialog';
|
||||
import { browseForProject, OpenExistingDialog } from './dialogs/openExistingDialog';
|
||||
import { IWorkspaceService } from './common/interfaces';
|
||||
import { IconPathHelper } from './common/iconHelper';
|
||||
import { ProjectDashboard } from './dialogs/projectDashboard';
|
||||
import { getAzdataApi } from './common/utils';
|
||||
import { createNewProjectWithQuickpick } from './dialogs/newProjectQuickpick';
|
||||
import Logger from './common/logger';
|
||||
import { TelemetryReporter } from './common/telemetry';
|
||||
import { noProjectProvidingExtensionsInstalled } from './common/constants';
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<IExtension> {
|
||||
const startTime = new Date().getTime();
|
||||
@@ -41,22 +42,16 @@ export async function activate(context: vscode.ExtensionContext): Promise<IExten
|
||||
const dataWorkspaceExtension = new DataWorkspaceExtension(workspaceService);
|
||||
Logger.log(`DataWorkspaceExtension constructor took ${new Date().getTime() - dataWorkspaceExtensionStartTime}ms`);
|
||||
|
||||
const registerTreeDataProvidertartTime = new Date().getTime();
|
||||
context.subscriptions.push(vscode.window.registerTreeDataProvider('dataworkspace.views.main', workspaceTreeDataProvider));
|
||||
Logger.log(`registerTreeDataProvider took ${new Date().getTime() - registerTreeDataProvidertartTime}ms`);
|
||||
|
||||
const settingProjectProviderContextStartTime = new Date().getTime();
|
||||
context.subscriptions.push(vscode.extensions.onDidChange(() => {
|
||||
setProjectProviderContextValue(workspaceService);
|
||||
}));
|
||||
setProjectProviderContextValue(workspaceService);
|
||||
Logger.log(`setProjectProviderContextValue took ${new Date().getTime() - settingProjectProviderContextStartTime}ms`);
|
||||
|
||||
const registerCommandStartTime = new Date().getTime();
|
||||
context.subscriptions.push(vscode.commands.registerCommand('projects.new', async () => {
|
||||
// Make sure all project providing extensions are activated to be sure the project templates show up
|
||||
await workspaceService.ensureProviderExtensionLoaded(undefined, true);
|
||||
|
||||
if (!workspaceService.isProjectProviderAvailable) {
|
||||
void vscode.window.showErrorMessage(noProjectProvidingExtensionsInstalled);
|
||||
return;
|
||||
}
|
||||
|
||||
if (azdataApi) {
|
||||
const dialog = new NewProjectDialog(workspaceService);
|
||||
await dialog.open();
|
||||
@@ -69,6 +64,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<IExten
|
||||
// Make sure all project providing extensions are activated so that all supported project types show up in the file filter
|
||||
await workspaceService.ensureProviderExtensionLoaded(undefined, true);
|
||||
|
||||
if (!workspaceService.isProjectProviderAvailable) {
|
||||
void vscode.window.showErrorMessage(noProjectProvidingExtensionsInstalled);
|
||||
return;
|
||||
}
|
||||
|
||||
if (azdataApi) {
|
||||
const dialog = new OpenExistingDialog(workspaceService);
|
||||
await dialog.open();
|
||||
@@ -99,17 +99,18 @@ export async function activate(context: vscode.ExtensionContext): Promise<IExten
|
||||
}));
|
||||
Logger.log(`Registering commands took ${new Date().getTime() - registerCommandStartTime}ms`);
|
||||
|
||||
context.subscriptions.push(vscode.extensions.onDidChange(() => {
|
||||
workspaceService.updateIfProjectProviderAvailable();
|
||||
}));
|
||||
|
||||
const iconPathHelperTime = new Date().getTime();
|
||||
IconPathHelper.setExtensionContext(context);
|
||||
Logger.log(`IconPathHelper took ${new Date().getTime() - iconPathHelperTime}ms`);
|
||||
|
||||
context.subscriptions.push(TelemetryReporter);
|
||||
Logger.log(`Finished activating Data Workspace extension. Total time = ${new Date().getTime() - startTime}ms`);
|
||||
return Promise.resolve(dataWorkspaceExtension);
|
||||
}
|
||||
|
||||
function setProjectProviderContextValue(workspaceService: IWorkspaceService): void {
|
||||
void vscode.commands.executeCommand('setContext', 'isProjectProviderAvailable', workspaceService.isProjectProviderAvailable);
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
}
|
||||
|
||||
@@ -25,25 +25,24 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
|
||||
private openedProjects: vscode.Uri[] | undefined = undefined;
|
||||
private excludedProjects: string[] | undefined;
|
||||
private _isProjectProviderAvailable: boolean = false;
|
||||
|
||||
constructor() {
|
||||
Logger.log(`Calling getProjectsInWorkspace() from WorkspaceService constructor`);
|
||||
this.updateIfProjectProviderAvailable();
|
||||
this.getProjectsInWorkspace(undefined, true).catch(err => Logger.error(`Error initializing projects in workspace ${err}`));
|
||||
}
|
||||
|
||||
get isProjectProviderAvailable(): boolean {
|
||||
return this._isProjectProviderAvailable;
|
||||
}
|
||||
|
||||
public updateIfProjectProviderAvailable(): void {
|
||||
Logger.log(`Checking ${vscode.extensions.all.length} extensions to see if there is a project provider is available`);
|
||||
const startTime = new Date().getTime();
|
||||
for (const extension of vscode.extensions.all) {
|
||||
const projectTypes = extension.packageJSON.contributes && extension.packageJSON.contributes.projects as string[];
|
||||
if (projectTypes && projectTypes.length > 0) {
|
||||
Logger.log(`Project provider found. Total time = ${new Date().getTime() - startTime}ms`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.log(`No project providers found. Total time = ${new Date().getTime() - startTime}ms`);
|
||||
return false;
|
||||
this._isProjectProviderAvailable = vscode.extensions.all.some(e => e.packageJSON.contributes?.projects?.length > 0);
|
||||
Logger.log(`isProjectProviderAvailable is ${this._isProjectProviderAvailable}. Total time = ${new Date().getTime() - startTime}ms`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import * as constants from '../../common/constants';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { isValidBasename, isValidBasenameErrorMessage } from '../../common/pathUtilsHelper';
|
||||
|
||||
const isWindows = os.platform() === 'win32';
|
||||
|
||||
suite('Check for invalid filename tests', function (): void {
|
||||
test('Should determine invalid filenames', async () => {
|
||||
// valid filename
|
||||
should(isValidBasename(formatFileName('ValidName.sqlproj'))).equal(true);
|
||||
|
||||
// invalid for both Windows and non-Windows
|
||||
let invalidNames: string[] = [
|
||||
' .sqlproj',
|
||||
' .sqlproj',
|
||||
' .sqlproj',
|
||||
'..sqlproj',
|
||||
'...sqlproj',
|
||||
// most file systems do not allow files > 255 length
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.sqlproj'
|
||||
];
|
||||
|
||||
for (let invalidName of invalidNames) {
|
||||
should(isValidBasename(formatFileName(invalidName))).equal(false);
|
||||
}
|
||||
|
||||
should(isValidBasename(undefined)).equal(false);
|
||||
should(isValidBasename('\\')).equal(false);
|
||||
should(isValidBasename('/')).equal(false);
|
||||
});
|
||||
|
||||
test('Should determine invalid Windows filenames', async () => {
|
||||
let invalidNames: string[] = [
|
||||
// invalid characters only for Windows
|
||||
'?.sqlproj',
|
||||
':.sqlproj',
|
||||
'*.sqlproj',
|
||||
'<.sqlproj',
|
||||
'>.sqlproj',
|
||||
'|.sqlproj',
|
||||
'".sqlproj',
|
||||
// Windows filenames cannot end with a whitespace
|
||||
'test .sqlproj',
|
||||
'test .sqlproj'
|
||||
];
|
||||
|
||||
for (let invalidName of invalidNames) {
|
||||
should(isValidBasename(formatFileName(invalidName))).equal(isWindows ? false : true);
|
||||
}
|
||||
});
|
||||
|
||||
test('Should determine Windows forbidden filenames', async () => {
|
||||
let invalidNames: string[] = [
|
||||
// invalid only for Windows
|
||||
'CON.sqlproj',
|
||||
'PRN.sqlproj',
|
||||
'AUX.sqlproj',
|
||||
'NUL.sqlproj',
|
||||
'COM1.sqlproj',
|
||||
'COM2.sqlproj',
|
||||
'COM3.sqlproj',
|
||||
'COM4.sqlproj',
|
||||
'COM5.sqlproj',
|
||||
'COM6.sqlproj',
|
||||
'COM7.sqlproj',
|
||||
'COM8.sqlproj',
|
||||
'COM9.sqlproj',
|
||||
'LPT1.sqlproj',
|
||||
'LPT2.sqlproj',
|
||||
'LPT3.sqlproj',
|
||||
'LPT4.sqlproj',
|
||||
'LPT5.sqlproj',
|
||||
'LPT6.sqlproj',
|
||||
'LPT7.sqlproj',
|
||||
'LPT8.sqlproj',
|
||||
'LPT9.sqlproj',
|
||||
];
|
||||
|
||||
for (let invalidName of invalidNames) {
|
||||
should(isValidBasename(formatFileName(invalidName))).equal(isWindows ? false : true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite('Check for invalid filename error tests', function (): void {
|
||||
test('Should determine invalid filenames', async () => {
|
||||
// valid filename
|
||||
should(isValidBasenameErrorMessage(formatFileName('ValidName.sqlproj'))).equal('');
|
||||
|
||||
// invalid for both Windows and non-Windows
|
||||
should(isValidBasenameErrorMessage(formatFileName(' .sqlproj'))).equal(constants.whitespaceFilenameErrorMessage);
|
||||
should(isValidBasenameErrorMessage(formatFileName(' .sqlproj'))).equal(constants.whitespaceFilenameErrorMessage);
|
||||
should(isValidBasenameErrorMessage(formatFileName(' .sqlproj'))).equal(constants.whitespaceFilenameErrorMessage);
|
||||
should(isValidBasenameErrorMessage(formatFileName('..sqlproj'))).equal(constants.reservedValueErrorMessage);
|
||||
should(isValidBasenameErrorMessage(formatFileName('...sqlproj'))).equal(constants.reservedValueErrorMessage);
|
||||
should(isValidBasenameErrorMessage(undefined)).equal(constants.undefinedFilenameErrorMessage);
|
||||
should(isValidBasenameErrorMessage('\\')).equal(isWindows ? constants.whitespaceFilenameErrorMessage : constants.invalidFileCharsErrorMessage);
|
||||
should(isValidBasenameErrorMessage('/')).equal(constants.whitespaceFilenameErrorMessage);
|
||||
|
||||
// most file systems do not allow files > 255 length
|
||||
should(isValidBasenameErrorMessage(formatFileName('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.sqlproj'))).equal(constants.tooLongFilenameErrorMessage);
|
||||
});
|
||||
|
||||
test('Should determine invalid Windows filenames', async () => {
|
||||
let invalidNames: string[] = [
|
||||
// invalid characters only for Windows
|
||||
'?.sqlproj',
|
||||
':.sqlproj',
|
||||
'*.sqlproj',
|
||||
'<.sqlproj',
|
||||
'>.sqlproj',
|
||||
'|.sqlproj',
|
||||
'".sqlproj'
|
||||
];
|
||||
|
||||
for (let invalidName of invalidNames) {
|
||||
should(isValidBasenameErrorMessage(formatFileName(invalidName))).equal(isWindows ? constants.invalidFileCharsErrorMessage : '');
|
||||
}
|
||||
// Windows filenames cannot end with a whitespace
|
||||
should(isValidBasenameErrorMessage(formatFileName('test .sqlproj'))).equal(isWindows ? constants.trailingWhitespaceErrorMessage : '');
|
||||
should(isValidBasenameErrorMessage(formatFileName('test .sqlproj'))).equal(isWindows ? constants.trailingWhitespaceErrorMessage : '');
|
||||
});
|
||||
|
||||
test('Should determine Windows forbidden filenames', async () => {
|
||||
let invalidNames: string[] = [
|
||||
// invalid only for Windows
|
||||
'CON.sqlproj',
|
||||
'PRN.sqlproj',
|
||||
'AUX.sqlproj',
|
||||
'NUL.sqlproj',
|
||||
'COM1.sqlproj',
|
||||
'COM2.sqlproj',
|
||||
'COM3.sqlproj',
|
||||
'COM4.sqlproj',
|
||||
'COM5.sqlproj',
|
||||
'COM6.sqlproj',
|
||||
'COM7.sqlproj',
|
||||
'COM8.sqlproj',
|
||||
'COM9.sqlproj',
|
||||
'LPT1.sqlproj',
|
||||
'LPT2.sqlproj',
|
||||
'LPT3.sqlproj',
|
||||
'LPT4.sqlproj',
|
||||
'LPT5.sqlproj',
|
||||
'LPT6.sqlproj',
|
||||
'LPT7.sqlproj',
|
||||
'LPT8.sqlproj',
|
||||
'LPT9.sqlproj',
|
||||
];
|
||||
|
||||
for (let invalidName of invalidNames) {
|
||||
should(isValidBasenameErrorMessage(formatFileName(invalidName))).equal(isWindows ? constants.reservedWindowsFilenameErrorMessage : '');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function formatFileName(filename: string): string {
|
||||
return path.join(os.tmpdir(), filename);
|
||||
}
|
||||
|
||||
@@ -182,43 +182,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
|
||||
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
|
||||
|
||||
"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3":
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7"
|
||||
integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g==
|
||||
"@microsoft/ads-extension-telemetry@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-2.0.0.tgz#18ce267c7ed05c3b9dd99604a743e59f684c4e7c"
|
||||
integrity sha512-hExe/akhgq15v/h19LAFqiKNV6N9VxD19lOwGxEmO55yoWUm3E2cYealxvoYCwGDmSJfCbjR9fz/KM8Yz4XWAA==
|
||||
dependencies:
|
||||
"@microsoft/applicationinsights-core-js" "2.8.4"
|
||||
"@microsoft/applicationinsights-shims" "^2.0.1"
|
||||
"@microsoft/dynamicproto-js" "^1.1.6"
|
||||
|
||||
"@microsoft/1ds-post-js@^3.2.3":
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216"
|
||||
integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ==
|
||||
dependencies:
|
||||
"@microsoft/1ds-core-js" "3.2.3"
|
||||
"@microsoft/applicationinsights-shims" "^2.0.1"
|
||||
"@microsoft/dynamicproto-js" "^1.1.6"
|
||||
|
||||
"@microsoft/ads-extension-telemetry@^1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-1.3.2.tgz#d9cfb4bc7099df73e000b7bafa48bb748db924fe"
|
||||
integrity sha512-TG1TE7FPp5rBA9zYPVjralZut8Bq/b5XCgm0kmkLyoQyn3c9ntmWXFuNQPOXmgbIemg5YY1/7DHKrfNcO/igkQ==
|
||||
dependencies:
|
||||
"@vscode/extension-telemetry" "^0.6.2"
|
||||
|
||||
"@microsoft/applicationinsights-core-js@2.8.4":
|
||||
version "2.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596"
|
||||
integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ==
|
||||
dependencies:
|
||||
"@microsoft/applicationinsights-shims" "2.0.1"
|
||||
"@microsoft/dynamicproto-js" "^1.1.6"
|
||||
|
||||
"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd"
|
||||
integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ==
|
||||
"@vscode/extension-telemetry" "0.6.1"
|
||||
|
||||
"@microsoft/azdata-test@^2.0.3":
|
||||
version "2.0.3"
|
||||
@@ -232,11 +201,6 @@
|
||||
rimraf "^2.6.3"
|
||||
typemoq "^2.1.0"
|
||||
|
||||
"@microsoft/dynamicproto-js@^1.1.6":
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc"
|
||||
integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg==
|
||||
|
||||
"@microsoft/vscodetestcover@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/vscodetestcover/-/vscodetestcover-1.2.1.tgz#65f25132075a465a7a99688204486ee2b65ac07b"
|
||||
@@ -326,13 +290,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz#681df970358c82836b42f989188d133e218c458e"
|
||||
integrity sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==
|
||||
|
||||
"@vscode/extension-telemetry@^0.6.2":
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e"
|
||||
integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w==
|
||||
dependencies:
|
||||
"@microsoft/1ds-core-js" "^3.2.3"
|
||||
"@microsoft/1ds-post-js" "^3.2.3"
|
||||
"@vscode/extension-telemetry@0.6.1":
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.1.tgz#f8d1f7145baf932b75077c48107edff48501fc14"
|
||||
integrity sha512-Y4Oc8yGURGVF4WhCZcu+EVy+MAIeQDLDVeDlLn59H0C1w+7xr8dL2ZtDBioy+Hog1Edrd6zOwr3Na7xe1iC/UA==
|
||||
|
||||
agent-base@4, agent-base@^4.3.0:
|
||||
version "4.3.0"
|
||||
|
||||
@@ -32,3 +32,17 @@ This extension adds Data Virtualization support for SQL Server 2019 and above. T
|
||||
# Known Issues
|
||||
* You will not be able to preview files in HDFS which are over 30MB.
|
||||
* All the files in the HDFS folder for Virtualize Data From CSV Files to work properly would need to have the same file extension (.csv) and conform to the same schema. If there are .csv files which are of different schema then the wizard will still open but you will not be able to create the external table.
|
||||
|
||||
## Telemetry
|
||||
|
||||
This extension collects telemetry data, which is used to help understand how to improve the product. For example, this usage data helps to debug issues, such as slow start-up times, and to prioritize new features. While we appreciate the insights this data provides, we also know that not everyone wants to send usage data and you can disable telemetry as described in the Azure Data Studio [disable telemetry reporting](https://github.com/Microsoft/azuredatastudio/wiki/How-to-Disable-Telemetry-Reporting#how-to-disable-telemetry-reporting) documentation.
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704).
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt).
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
{
|
||||
"downloadUrl": "https://sqlopsextensions.blob.core.windows.net/extensions/datavirtualization/service/{#version#}/scaleoutdataservice-{#fileName#}",
|
||||
"version": "1.10.0",
|
||||
"version": "1.11.0",
|
||||
"downloadFileNames": {
|
||||
"Windows_64": "win-x64.zip",
|
||||
"Windows": "win-x64.zip",
|
||||
"Windows_86": "win-x86.zip",
|
||||
"OSX": "osx-x64.tar.gz",
|
||||
"Linux_64": "linux-x64.tar.gz",
|
||||
"CentOS_7": "linux-x64.tar.gz",
|
||||
"Debian_8": "linux-x64.tar.gz",
|
||||
"Fedora_23": "linux-x64.tar.gz",
|
||||
"OpenSUSE_13_2": "linux-x64.tar.gz",
|
||||
"RHEL_7": "linux-x64.tar.gz",
|
||||
"SLES_12_2": "linux-x64.tar.gz",
|
||||
"Ubuntu_14": "linux-x64.tar.gz",
|
||||
"Ubuntu_16": "linux-x64.tar.gz",
|
||||
"Ubuntu_18": "linux-x64.tar.gz"
|
||||
"Linux": "linux-x64.tar.gz"
|
||||
},
|
||||
"installDirectory": "scaleoutdataservice/{#platform#}/{#version#}",
|
||||
"executableFiles": [
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "datavirtualization",
|
||||
"displayName": "%title.datavirtualization%",
|
||||
"description": "%config.extensionDescription%",
|
||||
"version": "1.13.0",
|
||||
"version": "1.13.1",
|
||||
"publisher": "Microsoft",
|
||||
"icon": "resources/extension.png",
|
||||
"aiKey": "29a207bb14f84905966a8f22524cb730-25407f35-11b6-4d4e-8114-ab9e843cb52f-7380",
|
||||
@@ -105,8 +105,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/ads-extension-telemetry": "^1.3.2",
|
||||
"@microsoft/ads-service-downloader": "^1.0.4",
|
||||
"@microsoft/ads-extension-telemetry": "^2.0.0",
|
||||
"@microsoft/ads-service-downloader": "^1.2.1",
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#1.3.2",
|
||||
"vscode-nls": "^5.2.0"
|
||||
},
|
||||
|
||||
@@ -53,6 +53,7 @@ export const sqlFileExtension = 'sql';
|
||||
export const virtualizeDataCommand = 'virtualizedatawizard.cmd.open';
|
||||
export const virtualizeDataTask = 'virtualizedatawizard.task.open';
|
||||
export const mssqlHdfsTableFromFileCommand = 'mssqlHdfsTableWizard.cmd.open';
|
||||
export const packageName = 'Microsoft.datavirtualization';
|
||||
|
||||
export const ctp24Version = 'CTP2.4';
|
||||
export const ctp25Version = 'CTP2.5';
|
||||
|
||||
@@ -16,6 +16,7 @@ import { DataSourceWizardService } from './services/contracts';
|
||||
import { managerInstance, ApiType } from './services/serviceApiManager';
|
||||
import { OpenVirtualizeDataWizardCommand, OpenVirtualizeDataWizardTask, OpenMssqlHdfsTableFromFileWizardCommand } from './wizards/wizardCommands';
|
||||
import { ServiceClient } from './services/serviceClient';
|
||||
import { TelemetryReporter } from './services/telemetry';
|
||||
|
||||
export function activate(extensionContext: vscode.ExtensionContext): void {
|
||||
let apiWrapper = new ApiWrapper();
|
||||
@@ -38,4 +39,5 @@ export function activate(extensionContext: vscode.ExtensionContext): void {
|
||||
serviceClient.startService(extensionContext).then(success => undefined, err => {
|
||||
apiWrapper.showErrorMessage(utils.getErrorMessage(err));
|
||||
});
|
||||
extensionContext.subscriptions.push(TelemetryReporter);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ClientCapabilities as VSClientCapabilities, RequestType, NotificationType } from 'vscode-languageclient';
|
||||
import * as types from 'dataprotocol-client/lib/types';
|
||||
import { RequestType, NotificationType } from 'vscode-languageclient';
|
||||
import * as telemetry from '@microsoft/ads-extension-telemetry';
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
/**
|
||||
@@ -29,19 +29,11 @@ export namespace TelemetryNotification {
|
||||
export class TelemetryParams {
|
||||
public params: {
|
||||
eventName: string;
|
||||
properties: ITelemetryEventProperties;
|
||||
measures: ITelemetryEventMeasures;
|
||||
properties: telemetry.TelemetryEventProperties;
|
||||
measures: telemetry.TelemetryEventMeasures;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ITelemetryEventProperties {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface ITelemetryEventMeasures {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------- </ Telemetry Sent Event > ----------------------------------
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user