Compare commits

..

12 Commits

Author SHA1 Message Date
Karl Burtram
661384637d Port fix for vscode issue 160827 to release/1.40 (#21466)
* port fix for vscode issue 160827 (#21457)

* Update build version

Co-authored-by: Hai Cao <hacao@microsoft.com>
2022-12-21 11:01:44 -08:00
Charles Gagnon
dc838c7bf5 Update changelog for 1.40.1 release (#21297) (#21299)
(cherry picked from commit 8d13d4054a)
2022-11-23 09:38:23 -08:00
Charles Gagnon
3a32679ab5 Update SMO to 4.3.1.2 (#21292)
* Update SMO to 4.3.1.2

* vBump
2022-11-22 09:16:10 -08:00
Lucy Zhang
3fbaa3de93 Fix headers being exported twice from notebooks (#21195) (#21201)
* fix headers being exported twice

* simplify

* fix test
2022-11-10 22:04:01 -08:00
Cory Rivera
ad1c91e3ec Bring back old editor resolving logic to fix saving in notebooks. (#21186) (#21200) 2022-11-10 14:16:58 -08:00
Aasim Khan
a20b3a5d27 Updating id generation logic to avoid substring overlaps (#21181) (#21188) 2022-11-10 13:42:17 -08:00
Cheena Malhotra
59d32e8e89 Fix custom option support in CMS + update providername in provided profile (#21185) 2022-11-10 12:19:38 -08:00
Karl Burtram
dfa72add10 Fix casing on execution plan command titles (#21170) (#21172) 2022-11-08 19:24:35 -08:00
Hai Cao
de8eb2c98e bump STS (#21168) 2022-11-08 16:17:17 -08:00
Karl Burtram
ec073def25 Port VS Code issue 149177 (#21153) (#21159) 2022-11-08 13:04:04 -08:00
Alex Ma
d8279f1a7c Langpack source files update for November release (main branch PR) (#21147) (#21149)
* updated localized XLFS

* update to langpack source files
2022-11-08 10:40:52 -08:00
Alex Ma
9c55574ddd Juno: check in to lego/hb_04604851-bac4-4681-9f74-73de611d6e48_20221107163900168. (#21128) (#21137)
Co-authored-by: Alex Hsu <csigs@users.noreply.github.com>
2022-11-07 16:26:39 -08:00
1661 changed files with 150952 additions and 138022 deletions

View File

@@ -24,6 +24,8 @@
**/node_modules/** **/node_modules/**
**/extensions/**/out/** **/extensions/**/out/**
**/extensions/**/build/** **/extensions/**/build/**
/extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts
/extensions/big-data-cluster/src/bigDataCluster/controller/clusterApiGenerated2.ts
**/extensions/**/colorize-fixtures/** **/extensions/**/colorize-fixtures/**
**/extensions/html-language-features/server/lib/jquery.d.ts **/extensions/html-language-features/server/lib/jquery.d.ts
/extensions/markdown-language-features/media/** /extensions/markdown-language-features/media/**

View File

@@ -770,6 +770,7 @@
"chart.js", "chart.js",
"plotly.js", "plotly.js",
"angular2-grid", "angular2-grid",
"kburtram-query-plan",
"html-to-image", "html-to-image",
"turndown", "turndown",
"gridstack", "gridstack",
@@ -1146,6 +1147,7 @@
"extensions/azuremonitor/src/prompts/**", "extensions/azuremonitor/src/prompts/**",
"extensions/azuremonitor/src/typings/findRemove.d.ts", "extensions/azuremonitor/src/typings/findRemove.d.ts",
"extensions/kusto/src/prompts/**", "extensions/kusto/src/prompts/**",
"extensions/mssql/src/hdfs/webhdfs.ts",
"extensions/mssql/src/prompts/**", "extensions/mssql/src/prompts/**",
"extensions/mssql/src/typings/bufferStreamReader.d.ts", "extensions/mssql/src/typings/bufferStreamReader.d.ts",
"extensions/mssql/src/typings/findRemove.d.ts", "extensions/mssql/src/typings/findRemove.d.ts",

5
.github/CODEOWNERS vendored
View File

@@ -6,17 +6,14 @@
/extensions/arc/ @Charles-Gagnon @swells @candiceye /extensions/arc/ @Charles-Gagnon @swells @candiceye
/extensions/azcli/ @Charles-Gagnon @swells @candiceye /extensions/azcli/ @Charles-Gagnon @swells @candiceye
/extensions/azurecore/ @cssuh @cheenamalhotra /extensions/azurecore/ @cssuh @cheenamalhotra
/extensions/big-data-cluster/ @Charles-Gagnon
/extensions/dacpac/ @kisantia /extensions/dacpac/ @kisantia
/extensions/datavirtualization @Charles-Gagnon
/extensions/import @aasimkhan30
/extensions/machine-learning @llali
/extensions/notebook @azure-data-studio-notebook-devs /extensions/notebook @azure-data-studio-notebook-devs
/extensions/query-history/ @Charles-Gagnon /extensions/query-history/ @Charles-Gagnon
/extensions/resource-deployment/ @Charles-Gagnon /extensions/resource-deployment/ @Charles-Gagnon
/extensions/schema-compare/ @kisantia /extensions/schema-compare/ @kisantia
/extensions/sql-bindings/ @vasubhog @Charles-Gagnon @lucyzhang929 @chlafreniere @MaddyDev /extensions/sql-bindings/ @vasubhog @Charles-Gagnon @lucyzhang929 @chlafreniere @MaddyDev
/extensions/sql-database-projects/ @Benjin @kisantia /extensions/sql-database-projects/ @Benjin @kisantia
/extensions/sql-migration @AkshayMata @raymondtruong @brian-harris @junierch @siyangMicrosoft
/extensions/mssql/config.json @Charles-Gagnon @alanrenmsft @kburtram /extensions/mssql/config.json @Charles-Gagnon @alanrenmsft @kburtram
/src/sql/*.d.ts @alanrenmsft @Charles-Gagnon /src/sql/*.d.ts @alanrenmsft @Charles-Gagnon

View File

@@ -18,7 +18,6 @@ Next there are two types of logs to collect:
- Save this text into a file named console.log and attach it to this issue. - Save this text into a file named console.log and attach it to this issue.
- Developer Tools can be closed via Help -> Toggle Developer Tools
**Application Logs** **Application Logs**
@@ -47,5 +46,8 @@ Needs Logs - Azure:
# actions for Out of Scope label # actions for Out of Scope label
Out of Scope: Out of Scope:
comment: "Thank you for your feedback! This feature is currently out of scope and we do not plan to work on it in a currently planned release. We will close this issue to keep our backlog focused on requests that we are planning to work on. Please note that users can continue to vote and comment on closed issues, which we encourage as it helps us understand user interest and can provide more details about why a feature is requested." comment: "Thank you for opening this suggestion! This enhancement is not planned in our
medium-term roadmap. The issue is being closed to reduce active issues to focus on
enhancements that are being considered for an upcoming release. We will review closed issues
with the 'Out of Scope' label when doing long-term planning."
close: true close: true

View File

@@ -142,9 +142,16 @@ jobs:
id: electron-unit-tests id: electron-unit-tests
run: DISPLAY=:10 ./scripts/test.sh --runGlob "**/sql/**/*.test.js" --coverage run: DISPLAY=:10 ./scripts/test.sh --runGlob "**/sql/**/*.test.js" --coverage
- name: Run Extension Unit Tests # {{SQL CARBON EDIT}} Rename to extension for clarity - name: Run Extension Unit Tests (Continue on Error) # {{SQL CARBON EDIT}} Run extension unit tests (continue on error)
id: electron-extension-unit-tests id: electron-extension-unit-tests-continue-on-error
run: DISPLAY=:10 ./scripts/test-extensions-unit.sh run: DISPLAY=:10 ./scripts/test-extensions-unit.sh
continueOnError: true
condition: and(succeeded(), eq(variables['EXTENSION_UNIT_TESTS_FAIL_ON_ERROR'], 'false'))
- name: Run Extension Unit Tests (Fail on Error) # {{SQL CARBON EDIT}} Run extension unit tests (fail on error)
id: electron-extension-unit-tests-fail-on-error
run: DISPLAY=:10 ./scripts/test-extensions-unit.sh
condition: and(succeeded(), ne(variables['EXTENSION_UNIT_TESTS_FAIL_ON_ERROR'], 'false'))
# {{SQL CARBON EDIT}} Add coveralls. We merge first to get around issue where parallel builds weren't being combined correctly # {{SQL CARBON EDIT}} Add coveralls. We merge first to get around issue where parallel builds weren't being combined correctly
- name: Combine code coverage files - name: Combine code coverage files

View File

@@ -1,4 +1,4 @@
disturl "https://electronjs.org/headers" disturl "https://electronjs.org/headers"
target "19.1.8" target "17.4.11"
runtime "electron" runtime "electron"
build_from_source "true" build_from_source "true"

View File

@@ -1,160 +1,11 @@
# Change Log # Change Log
## Version 1.42.0
* Release number: 1.42.0
* Release date: March 22, 2023
### What's new in 1.42.0
| New Item | Details |
| --- | --- |
| ARM64 Support for macOS | Implemented native arm64 SqlToolsService support for arm64 Windows and macOS. |
| Connection | Changed the icon under Linked Accounts when adding a new Azure account. |
| Connection | Introduced support for the Command Timeout connection property. |
| Connection | Added support for all three connection encryption options: Strict, Mandatory, and Optional. |
| Connection | Introduced HostNameInCertificate connection property under Security on the Advanced tab, for server with a certificate configured. |
| Connection | Added new advanced option in the Connection dialog to support Secure Enclaves. |
| Connection | Introduced a new setting, Mssql Enable Sql Authentication Provider to allow connections to be maintained without the concern of losing access token lifetime or getting dropped by server. Access tokens will be refreshed internally by the SqlClient driver whenever they are found to be expired. This option is disabled by default. |
| Connection | Added support for connections to Microsoft Dataverse using the TDS endpoint. |
| Connection | Introduced additional error reporting for Azure connections. |
| Connection | Introduced support for change password. |
| Connection | Added support for encryption options for Arc SQL Managed Instance when server certificates are not installed. |
| Deployment | Moved the New Deployment option from the Connections breadcrumb to the File Menu. |
| Object Explorer | Introduced ability to group objects in Object Explorer by database schema. This applies to all MSSQL connections when enabled or disabled. |
| Object Explorer | Introduced a new option to allow a custom timeout to be configured for Object Explorer. Within Settings, enable Mssql > Object Explorer: Expand Timeout. |
| Query Results | Added option to disable special handling for JSON strings. |
### Bug fixes in 1.42.0
| New Item | Details |
| --- | --- |
| Accessibility | Updated server group color display to improve visibility and contrast. |
| Backup | Addressed inability to select "Backup Set" checkbox. |
| Connection | Removed refresh action for connections which are disconnected. |
| Connection | Fixed issue with MSAL not properly set for connections. |
| Connection | Added ability to delete a server group if no connections exist for it. |
| Connection | Added connection display name to the Delete Connection dialog. |
| Connection | Azure connections with "Do not save" for the server group are no longer added to the default server group list. |
| Connection | Improved error handling in the connection dialog. |
| Connection | Fixed issue where saved passwords were not retained for Azure SQL connections. |
| Connection | Improved method to retrieve database access when connecting to Azure SQL. |
| Connection | Improved connection experience for cloud users. |
| Connection | Improved account and tenant selection when connecting to Azure SQL in the connection dialog. |
| Deployment | Improved narration for deployment wizard. |
| Installation | Updated default install location for the Windows on ARM installer. |
| MySQL Extension | Addressed issue where dialog boxes in the MySQL connection pane were not editable. |
| Notebooks | Fixed issue with updating the relative path in a Notebook cell. |
| Notebooks | Fixed issue that caused internal notebook links to break when editing characters in the page. |
| Notebooks | Addressed error thrown when opening a Notebook via a link. |
| Object Explorer | Fixed issue with Object Explorer node not expanding. |
| Query Editor | Fixed database dropdown list for contained users to display correctly. |
| Query Editor | Addressed issue where database dropdown list was not ordered the same as in Object Explorer. |
| Query Editor | Added the ability to properly escape special characters when they exist in object names. |
| Query Editor | Fixed issue which caused query timer to continue to run even though execution was complete. |
| Query Plan Viewer | Addressed an issue where a query plan would not render when opened via a URL. |
| Query Results | Improved precision formatting for datetimeoffset data type. |
For a full list of bug fixes addressed for the March 2023 release, visit the [bugs and issues list on GitHub](https://github.com/microsoft/azuredatastudio/milestone/95?closed=1).
#### Known issues
For a list of the current known issues, visit the [issues list on GitHub](https://github.com/microsoft/azuredatastudio/issues?q=is%3Aissue).
## 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 users 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
### Bug fixes in 1.40.2
* Fix potential elevation of privilege issue using Bash shell on Windows. VS Code issue [#160827](https://github.com/microsoft/vscode/issues/160827)
## Version 1.40.1 ## Version 1.40.1
* Release date: November 22, 2022 * Release date: November 22, 2022
* Release status: General Availability * Release status: General Availability
### Bug fixes in 1.40.1 ### Bug fixes in 1.40.1
* Fixed bug that caused folders in the servers tree to display incorrect contents [#21245](https://github.com/microsoft/azuredatastudio/issues/21245) * Fixed bug that caused folders in the servers tree to display incorrect contents [#21245](https://github.com/microsoft/azuredatastudio/issues/21245)
## Version 1.40.0 ## Version 1.40.0
* Release date: November 16, 2022 * Release date: November 16, 2022
@@ -207,14 +58,14 @@ For a list of the current known issues, visit the [issues list on GitHub](https:
## Version 1.39.1 ## Version 1.39.1
* Release date: August 30, 2022 * Release date: August 30, 2022
* Release status: General Availability * Release status: General Availability
## What's new in 1.39.1
### Bug fixes in 1.39.1 * Bug fixes in 1.39.1
* Fixed bug that caused Database Trees in server connections to not expand in the Object Explorer. * Fixed bug that caused Database Trees in server connections to not expand in the Object Explorer.
## Version 1.39.0 ## Version 1.39.0
* Release date: August 24, 2022 * Release date: August 24, 2022
* Release status: General Availability * Release status: General Availability
### What's new in 1.39.0 ## What's new in 1.39.0
* New Features: * New Features:
* Deployment Wizard - Azure Data Studio now supports SQL Server 2022 (Preview) in the Deployment Wizard for both local and container installation. * Deployment Wizard - Azure Data Studio now supports SQL Server 2022 (Preview) in the Deployment Wizard for both local and container installation.
* Object Explorer - Added Ledger icons and scripting support to Object Explorer for Ledger objects. * Object Explorer - Added Ledger icons and scripting support to Object Explorer for Ledger objects.
@@ -244,7 +95,7 @@ For a list of the current known issues, visit the [issues list on GitHub](https:
## Version 1.38.0 ## Version 1.38.0
* Release date: July 27, 2022 * Release date: July 27, 2022
* Release status: General Availability * Release status: General Availability
### What's new in 1.38.0 ## What's new in 1.38.0
* New Features: * New Features:
* VS Code merges to 1.62 - This release includes updates to VS Code from the three previous VS Code releases. Read [their release notes](https://code.visualstudio.com/updates/v1_62) to learn more. * VS Code merges to 1.62 - This release includes updates to VS Code from the three previous VS Code releases. Read [their release notes](https://code.visualstudio.com/updates/v1_62) to learn more.
* Table Designer - New column added to Table Designer for easier access to additional actions specific to individual rows. * Table Designer - New column added to Table Designer for easier access to additional actions specific to individual rows.
@@ -272,7 +123,7 @@ For a list of the current known issues, visit the [issues list on GitHub](https:
## Version 1.37.0 ## Version 1.37.0
* Release date: June 15, 2022 * Release date: June 15, 2022
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* New Features: * New Features:
* Backup & Restore - Backup & Restore to URL is now available in preview for Azure SQL Managed Instances. * Backup & Restore - Backup & Restore to URL is now available in preview for Azure SQL Managed Instances.
* Table Designer - Added API to support computed column capabilities on Table Designer. * Table Designer - Added API to support computed column capabilities on Table Designer.
@@ -295,7 +146,7 @@ For a list of the current known issues, visit the [issues list on GitHub](https:
## Version 1.36.2 ## Version 1.36.2
* Release date: May 20, 2022 * Release date: May 20, 2022
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
- Fix connectivity issue with PBI data source - Fix connectivity issue with PBI data source
- Fix query plan zoom and icon issues - Fix query plan zoom and icon issues
- Issues fixed in this release https://github.com/microsoft/azuredatastudio/milestone/89?closed=1 - Issues fixed in this release https://github.com/microsoft/azuredatastudio/milestone/89?closed=1
@@ -303,14 +154,14 @@ For a list of the current known issues, visit the [issues list on GitHub](https:
## Version 1.36.1 ## Version 1.36.1
* Release date: April 22, 2022 * Release date: April 22, 2022
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* April Hotfix addressing these issues https://github.com/microsoft/azuredatastudio/milestone/88?closed=1. * April Hotfix addressing these issues https://github.com/microsoft/azuredatastudio/milestone/88?closed=1.
* Hotfix RCA - https://github.com/microsoft/azuredatastudio/wiki/ADS-April-2022-Hotfix-RCA * Hotfix RCA - https://github.com/microsoft/azuredatastudio/wiki/ADS-April-2022-Hotfix-RCA
## Version 1.36.0 ## Version 1.36.0
* Release date: April 20, 2022 * Release date: April 20, 2022
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
- General Availability of the Azure SQL Migration Extension for ADS - General Availability of the Azure SQL Migration Extension for ADS
- Support for .NET Interactive Notebooks Extension - Support for .NET Interactive Notebooks Extension
- New Table Designer Features including support for System Versioned, Graph and Memory Optomized Tables - New Table Designer Features including support for System Versioned, Graph and Memory Optomized Tables
@@ -320,14 +171,14 @@ For a list of the current known issues, visit the [issues list on GitHub](https:
## Version 1.35.1 ## Version 1.35.1
* Release date: March 17, 2022 * Release date: March 17, 2022
* Release status: General Availability * Release status: General Availability
### Hotfix release ## Hotfix release
- Fix for [Excel number format #18615](https://github.com/microsoft/azuredatastudio/issues/18615) - Fix for [Excel number format #18615](https://github.com/microsoft/azuredatastudio/issues/18615)
- Fix for [Geometry Data Type Returned as Unknown Charset in Results Grid #18630](https://github.com/microsoft/azuredatastudio/issues/18630) - Fix for [Geometry Data Type Returned as Unknown Charset in Results Grid #18630](https://github.com/microsoft/azuredatastudio/issues/18630)
## Version 1.35.0 ## Version 1.35.0
* Release date: February 24, 2022 * Release date: February 24, 2022
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* New Features: * New Features:
* Table Designer - Added functionality for creation and management of tables for SQL Servers. Built using DacFx framework * Table Designer - Added functionality for creation and management of tables for SQL Servers. Built using DacFx framework
* Query Plan Viewer - Added functionality for users to view a graphic view of estimated and actual query plans without need for an extension * Query Plan Viewer - Added functionality for users to view a graphic view of estimated and actual query plans without need for an extension
@@ -340,7 +191,7 @@ For a list of the current known issues, visit the [issues list on GitHub](https:
## Version 1.34.0 ## Version 1.34.0
* Release date: December 15, 2021 * Release date: December 15, 2021
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* New Features: * New Features:
* Added “Currently restoring backup file” in the migration progress details page of Azure SQL Migration extension when backup files location is Azure Storage blob container * Added “Currently restoring backup file” in the migration progress details page of Azure SQL Migration extension when backup files location is Azure Storage blob container
* Enhancements to diagnostics in Azure SQL Migration extension * Enhancements to diagnostics in Azure SQL Migration extension
@@ -364,14 +215,14 @@ For a list of the current known issues, visit the [issues list on GitHub](https:
* Release date: Nov 4, 2021 * Release date: Nov 4, 2021
* Release status: General Availability * Release status: General Availability
### Hotfix release ## Hotfix release
- Fix for [#16535 Unable to See Saved Connections in Restricted Mode](https://github.com/microsoft/azuredatastudio/issues/17535) - Fix for [#16535 Unable to See Saved Connections in Restricted Mode](https://github.com/microsoft/azuredatastudio/issues/17535)
- Fix for [#17579 Can't type in Notebook code cell after editing text cell](https://github.com/microsoft/azuredatastudio/issues/17579) - Fix for [#17579 Can't type in Notebook code cell after editing text cell](https://github.com/microsoft/azuredatastudio/issues/17579)
## Version 1.33.0 ## Version 1.33.0
* Release date: October 27, 2021 * Release date: October 27, 2021
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* New Notebook Features: * New Notebook Features:
* Notebook Views * Notebook Views
* Split cell support * Split cell support
@@ -410,7 +261,7 @@ For a list of the current known issues, visit the [issues list on GitHub](https:
## Version 1.31.1 ## Version 1.31.1
* Release date: July 29, 2021 * Release date: July 29, 2021
* Release status: General Availability * Release status: General Availability
### Hotfix Release ## Hotfix Release
- Fix for [#16436 Database Connection Toolbar Missing](https://github.com/microsoft/azuredatastudio/issues/16436) - Fix for [#16436 Database Connection Toolbar Missing](https://github.com/microsoft/azuredatastudio/issues/16436)
## Version 1.31.0 ## Version 1.31.0
@@ -701,7 +552,7 @@ For a list of the current known issues, visit the [issues list on GitHub](https:
* GA status for Big Data Cluster/SQL 2019 features [#8269](https://github.com/microsoft/azuredatastudio/issues/8269) * GA status for Big Data Cluster/SQL 2019 features [#8269](https://github.com/microsoft/azuredatastudio/issues/8269)
* Resolved [bugs and issues](https://github.com/microsoft/azuredatastudio/milestone/44?closed=1). * Resolved [bugs and issues](https://github.com/microsoft/azuredatastudio/milestone/44?closed=1).
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
## Version 1.13.1 ## Version 1.13.1
@@ -715,7 +566,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* General Availability release for Schema Compare and DACPAC extensions * General Availability release for Schema Compare and DACPAC extensions
* Resolved [bugs and issues](https://github.com/microsoft/azuredatastudio/milestone/43?closed=1). * Resolved [bugs and issues](https://github.com/microsoft/azuredatastudio/milestone/43?closed=1).
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* aspnerd for `Use selected DB for import wizard schema list` [#7878](https://github.com/microsoft/azuredatastudio/pull/7878) * aspnerd for `Use selected DB for import wizard schema list` [#7878](https://github.com/microsoft/azuredatastudio/pull/7878)
@@ -733,7 +584,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: October 2, 2019 * Release date: October 2, 2019
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Announcing the Query History panel * Announcing the Query History panel
* Improved Query Results Grid copy selection support * Improved Query Results Grid copy selection support
* TempDB page added to Server Reports extension * TempDB page added to Server Reports extension
@@ -744,14 +595,14 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: September 10, 2019 * Release date: September 10, 2019
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Resolved [bugs and issues](https://github.com/microsoft/azuredatastudio/milestone/41?closed=1). * Resolved [bugs and issues](https://github.com/microsoft/azuredatastudio/milestone/41?closed=1).
## Version 1.10.0 ## Version 1.10.0
* Release date: August 14, 2019 * Release date: August 14, 2019
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* [SandDance](https://github.com/microsoft/SandDance) integration — A new way to interact with data. Download the extension [here](https://docs.microsoft.com/sql/azure-data-studio/sanddance-extension) * [SandDance](https://github.com/microsoft/SandDance) integration — A new way to interact with data. Download the extension [here](https://docs.microsoft.com/sql/azure-data-studio/sanddance-extension)
* Notebook improvements * Notebook improvements
* Better loading performance * Better loading performance
@@ -767,7 +618,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: July 11, 2019 * Release date: July 11, 2019
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Release of [SentryOne Plan Explorer Extension](https://www.sentryone.com/products/sentryone-plan-explorer-extension-azure-data-studio) * Release of [SentryOne Plan Explorer Extension](https://www.sentryone.com/products/sentryone-plan-explorer-extension-azure-data-studio)
* **Schema Compare** * **Schema Compare**
* Schema Compare File Support (.SCMP) * Schema Compare File Support (.SCMP)
@@ -793,7 +644,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: June 6, 2019 * Release date: June 6, 2019
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Initial release of the Database Admin Tool Extensions for Windows *Preview* extension * Initial release of the Database Admin Tool Extensions for Windows *Preview* extension
* Initial release of the Central Management Servers extension * Initial release of the Central Management Servers extension
* **Schema Compare** * **Schema Compare**
@@ -814,24 +665,24 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: May 8, 2019 * Release date: May 8, 2019
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Announcing Schema Compare *Preview* extension * Announcing Schema Compare *Preview* extension
* Tasks Panel UX improvement * Tasks Panel UX improvement
* Announcing new Welcome page * Announcing new Welcome page
* Resolved [bugs and issues](https://github.com/microsoft/azuredatastudio/milestone/31?closed=1). * Resolved [bugs and issues](https://github.com/microsoft/azuredatastudio/milestone/31?closed=1).
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues. We would like to thank all our users who raised issues.
## Version 1.6.0 ## Version 1.6.0
* Release date: April 18, 2019 * Release date: April 18, 2019
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Align with latest VS Code editor platform (currently 1.33.1) * Align with latest VS Code editor platform (currently 1.33.1)
* Resolved [bugs and issues](https://github.com/Microsoft/azuredatastudio/milestone/26?closed=1). * Resolved [bugs and issues](https://github.com/Microsoft/azuredatastudio/milestone/26?closed=1).
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* yamatoya for `fix the format (#4899)` * yamatoya for `fix the format (#4899)`
@@ -840,13 +691,13 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: March 18, 2019 * Release date: March 18, 2019
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Announcing T-SQL Notebooks * Announcing T-SQL Notebooks
* Announcing PostgreSQL extension * Announcing PostgreSQL extension
* Announcing SQL Server Dacpac extension * Announcing SQL Server Dacpac extension
* Resolved [bugs and issues](https://github.com/Microsoft/azuredatastudio/milestone/25?closed=1). * Resolved [bugs and issues](https://github.com/Microsoft/azuredatastudio/milestone/25?closed=1).
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* GeoffYoung for `Fix sqlDropColumn description #4422` * GeoffYoung for `Fix sqlDropColumn description #4422`
@@ -855,7 +706,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: February 13, 2019 * Release date: February 13, 2019
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Added **Admin pack for SQL Server** extension pack to make it easier to install SQL Server admin-related extensions. This includes: * Added **Admin pack for SQL Server** extension pack to make it easier to install SQL Server admin-related extensions. This includes:
* [SQL Server Agent](https://docs.microsoft.com/en-us/sql/azure-data-studio/sql-server-agent-extension?view=sql-server-2017) * [SQL Server Agent](https://docs.microsoft.com/en-us/sql/azure-data-studio/sql-server-agent-extension?view=sql-server-2017)
* [SQL Server Profiler](https://docs.microsoft.com/en-us/sql/azure-data-studio/sql-server-profiler-extension?view=sql-server-2017) * [SQL Server Profiler](https://docs.microsoft.com/en-us/sql/azure-data-studio/sql-server-profiler-extension?view=sql-server-2017)
@@ -869,7 +720,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Results streaming enabled by default for long running queries * Results streaming enabled by default for long running queries
* Resolved [bugs and issues](https://github.com/Microsoft/azuredatastudio/milestone/23?closed=1). * Resolved [bugs and issues](https://github.com/Microsoft/azuredatastudio/milestone/23?closed=1).
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* AlexFsmn for `Added context menu for DBs in explorer view to backup & restore db. #2277` * AlexFsmn for `Added context menu for DBs in explorer view to backup & restore db. #2277`
@@ -880,7 +731,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: January 9, 2019 * Release date: January 9, 2019
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* #13 Feature Request: Azure Active Directory Authentication * #13 Feature Request: Azure Active Directory Authentication
* #1040 Stream initial query results as they become available * #1040 Stream initial query results as they become available
* #3298 Сan't add an azure account. * #3298 Сan't add an azure account.
@@ -890,7 +741,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Updates to [SQL Server 2019 extension](https://docs.microsoft.com/sql/azure-data-studio/sql-server-2019-extension?view=sql-server-ver15) * Updates to [SQL Server 2019 extension](https://docs.microsoft.com/sql/azure-data-studio/sql-server-2019-extension?view=sql-server-ver15)
* **sp_executesql to SQL** and **New Database** extensions * **sp_executesql to SQL** and **New Database** extensions
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* Tarig0 for `Add Routine_Type to CreateStoredProc fixes #3257 (#3286)` * Tarig0 for `Add Routine_Type to CreateStoredProc fixes #3257 (#3286)`
@@ -902,7 +753,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: November 6, 2018 * Release date: November 6, 2018
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Update to the SQL Server 2019 Preview extension * Update to the SQL Server 2019 Preview extension
* Introducing Paste the Plan extension * Introducing Paste the Plan extension
* Introducing High Color queries extension, including SSMS editor theme * Introducing High Color queries extension, including SSMS editor theme
@@ -911,7 +762,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Upgrade SQL Tools Service to .Net Core 2.2 Preview 3 (for eventual AAD support) * Upgrade SQL Tools Service to .Net Core 2.2 Preview 3 (for eventual AAD support)
* Fix customer reported GitHub issues * Fix customer reported GitHub issues
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* rdaniels6813 for `Add query plan theme support #3031` * rdaniels6813 for `Add query plan theme support #3031`
@@ -925,12 +776,12 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: October 18, 2018 * Release date: October 18, 2018
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Introducing the Azure Resource Explorer to browse Azure SQL Databases * Introducing the Azure Resource Explorer to browse Azure SQL Databases
* Improve Object Explorer and Query Editor connectivity robustness * Improve Object Explorer and Query Editor connectivity robustness
* SQL Server 2019 and SQL Agent extension improvements * SQL Server 2019 and SQL Agent extension improvements
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* philoushka for `center the icon #2760` * philoushka for `center the icon #2760`
@@ -942,7 +793,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: September 24, 2018 * Release date: September 24, 2018
* Release status: General Availability * Release status: General Availability
### What's new in this version ## What's new in this version
* Announcing the SQL Server 2019 Preview extension. * Announcing the SQL Server 2019 Preview extension.
* Support for SQL Server 2019 preview features including Big Data Cluster support. * Support for SQL Server 2019 preview features including Big Data Cluster support.
* Azure Data Studio Notebooks * Azure Data Studio Notebooks
@@ -953,7 +804,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Accessibility improvements for screen reader, keyboard navigation and high-contrast. * Accessibility improvements for screen reader, keyboard navigation and high-contrast.
* Added Connection name option to provide an alternative display name in the Servers viewlet. * Added Connection name option to provide an alternative display name in the Servers viewlet.
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* AlexFsmn `Feature: Ability to add connection name #2332` * AlexFsmn `Feature: Ability to add connection name #2332`
@@ -963,7 +814,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: August 30, 2018 * Release date: August 30, 2018
* Release status: Public Preview * Release status: Public Preview
### What's new in this version ## What's new in this version
* Announcing the SQL Server Import Extension * Announcing the SQL Server Import Extension
* SQL Server Profiler Session management * SQL Server Profiler Session management
* SQL Server Agent improvements * SQL Server Agent improvements
@@ -971,7 +822,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Quality of Life improvements: Connection strings * Quality of Life improvements: Connection strings
* Fix many customer reported GitHub issues * Fix many customer reported GitHub issues
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* SebastianPfliegel `Added more saveAsCsv options #2099` * SebastianPfliegel `Added more saveAsCsv options #2099`
@@ -989,7 +840,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: July 19, 2018 * Release date: July 19, 2018
* Release status: Public Preview * Release status: Public Preview
### What's new in this version ## What's new in this version
* SQL Server Agent for Azure Data Studio extension improvements * SQL Server Agent for Azure Data Studio extension improvements
* Added view of Alerts, Operators, and Proxies and icons on left pane * Added view of Alerts, Operators, and Proxies and icons on left pane
* Added dialogs for New Job, New Job Step, New Alert, and New Operator * Added dialogs for New Job, New Job Step, New Alert, and New Operator
@@ -1010,7 +861,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: June 20, 2018 * Release date: June 20, 2018
* Release status: Public Preview * Release status: Public Preview
### What's new in this version ## What's new in this version
* **SQL Server Profiler for Azure Data Studio *Preview*** extension initial release * **SQL Server Profiler for Azure Data Studio *Preview*** extension initial release
* The new **SQL Data Warehouse** extension includes rich customizable dashboard widgets surfacing insights to your data warehouse. This unlocks key scenarios around managing and tuning your data warehouse to ensure it is optimized for consistent performance. * The new **SQL Data Warehouse** extension includes rich customizable dashboard widgets surfacing insights to your data warehouse. This unlocks key scenarios around managing and tuning your data warehouse to ensure it is optimized for consistent performance.
* **Edit Data "Filtering and Sorting"** support * **Edit Data "Filtering and Sorting"** support
@@ -1023,7 +874,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: May 7, 2018 * Release date: May 7, 2018
* Release status: Public Preview * Release status: Public Preview
### What's new in this version ## What's new in this version
The May release is focused on stabilization and bug fixes leading up to the Build conference. This build contains the following highlights. The May release is focused on stabilization and bug fixes leading up to the Build conference. This build contains the following highlights.
* Announcing **Redgate SQL Search** extension available in Extension Manager * Announcing **Redgate SQL Search** extension available in Extension Manager
@@ -1038,7 +889,7 @@ The May release is focused on stabilization and bug fixes leading up to the Buil
* Release date: April 25, 2018 * Release date: April 25, 2018
* Release status: Public Preview * Release status: Public Preview
### What's new in this version ## What's new in this version
The April Public Preview release contains some of the following highlights. The April Public Preview release contains some of the following highlights.
* Improvements to SQL Agent *Preview* extension * Improvements to SQL Agent *Preview* extension
@@ -1053,7 +904,7 @@ The April Public Preview release contains some of the following highlights.
* Release date: March 28, 2017 * Release date: March 28, 2017
* Release status: Public Preview * Release status: Public Preview
### What's new in this version ## What's new in this version
The March Public Preview release enables some key aspects of the Azure Data Studio The March Public Preview release enables some key aspects of the Azure Data Studio
extensibility story. Here are some highlights in this release. extensibility story. Here are some highlights in this release.
@@ -1068,14 +919,14 @@ extensibility story. Here are some highlights in this release.
* Release date: February 16, 2017 * Release date: February 16, 2017
* Release status: Public Preview Hotfix 1 * Release status: Public Preview Hotfix 1
### What's new in this version ## What's new in this version
* Bug fix for `#717 Selecting partial query and hitting Cmd or Ctrl+C opens terminal with Error message` * Bug fix for `#717 Selecting partial query and hitting Cmd or Ctrl+C opens terminal with Error message`
## Version 0.26.6 ## Version 0.26.6
* Release date: February 15, 2017 * Release date: February 15, 2017
* Release status: Public Preview * Release status: Public Preview
### What's new in this version ## What's new in this version
The February release fixes several important customer reported issues, as well as various feature improvements. We've also introduced auto-update support in February which will simplify keeping updated with the lastest changes. The February release fixes several important customer reported issues, as well as various feature improvements. We've also introduced auto-update support in February which will simplify keeping updated with the lastest changes.
Here's some of the highlights in the February release. Here's some of the highlights in the February release.
@@ -1095,7 +946,7 @@ Here's some of the highlights in the February release.
* VS Code Editor 1.19 integration * VS Code Editor 1.19 integration
* Update JustinPealing/html-query-plan component to pick-up several Query Plan viewer improvements * Update JustinPealing/html-query-plan component to pick-up several Query Plan viewer improvements
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* SebastianPfliegel for `Add cursor snippet (#475)` * SebastianPfliegel for `Add cursor snippet (#475)`
@@ -1106,7 +957,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: January 17, 2017 * Release date: January 17, 2017
* Release status: Public Preview * Release status: Public Preview
### What's new in this version ## What's new in this version
The January release focuses on addressing a few of the top upvoted feature suggestions, as well as fixing high-priority bugs. This release period coincides with holiday vacations, so the churn in this release is The January release focuses on addressing a few of the top upvoted feature suggestions, as well as fixing high-priority bugs. This release period coincides with holiday vacations, so the churn in this release is
relatively scoped. relatively scoped.
@@ -1121,7 +972,7 @@ Here's some of the highlights in the January release.
* Fix missing Azure Account branding icon * Fix missing Azure Account branding icon
* Change "Server name" to "Server" in Connection Dialog * Change "Server name" to "Server" in Connection Dialog
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* alextercete for `Fix "No extension gallery service configured" error (#427)` * alextercete for `Fix "No extension gallery service configured" error (#427)`
@@ -1131,7 +982,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Release date: December 19, 2017 * Release date: December 19, 2017
* Release status: Public Preview * Release status: Public Preview
### What's new in this version ## What's new in this version
* Azure Integration with Create Firewall Rule * Azure Integration with Create Firewall Rule
* Windows Setup, Linux DEB and Linux RPM installation packages * Windows Setup, Linux DEB and Linux RPM installation packages
* Manage Dashboard visual layout editor * Manage Dashboard visual layout editor
@@ -1155,7 +1006,7 @@ We would like to thank all our users who raised issues, and in particular the fo
* Allow expanding databases not in certain non-Online states * Allow expanding databases not in certain non-Online states
* Connection Dialog selects most common default authentication method based on platform * Connection Dialog selects most common default authentication method based on platform
### Contributions and "thank you" ## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* mwiedemeyer for `Fix #58: Default sort order for DB size widget (#111)` * mwiedemeyer for `Fix #58: Default sort order for DB size widget (#111)`
* AlexTroshkin for `Show disconnect in context menu only when connectionProfile connected (#150)` * AlexTroshkin for `Show disconnect in context menu only when connectionProfile connected (#150)`

View File

@@ -1,3 +0,0 @@
# Data Collection
The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft's privacy statement. Our privacy statement is located at <https://go.microsoft.com/fwlink/?LinkID=824704>. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.

View File

@@ -8,56 +8,27 @@ Azure Data Studio is a data management tool that enables you to work with SQL Se
## **Download the latest Azure Data Studio release** ## **Download the latest Azure Data Studio release**
|Platform |Type |Download | | Platform |
| --------|-----------------|----------------------- | | --------------------------------------- |
|Windows |User Installer |[64 bit][win-user]&emsp;[ARM][win-user-arm64] | | [Windows User Installer][win-user] |
| |System Installer |[64 bit][win-system]&emsp;[ARM][win-system-arm64] | | [Windows System Installer][win-system] |
| |.zip |[64 bit][win-zip]&emsp;[ARM][win-zip-arm64] | | [Windows ZIP][win-zip] |
|Linux |.tar.gz |[64 bit][linux-zip] | | [macOS ZIP][osx-zip] |
| |.deb |[64 bit][linux-deb] | | [Linux TAR.GZ][linux-zip] |
| |.rpm |[64 bit][linux-rpm] | | [Linux RPM][linux-rpm] |
|Mac |.zip |[Universal][osx-universal]&emsp;[Intel Chip][osx-zip]&emsp;[Apple Silicon][osx-arm64] | | [Linux DEB][linux-deb] |
[win-user]: https://go.microsoft.com/fwlink/?linkid=2228644
[win-system]: https://go.microsoft.com/fwlink/?linkid=2228645
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2228646
[win-user-arm64]: https://go.microsoft.com/fwlink/?linkid=2229004
[win-system-arm64]: https://go.microsoft.com/fwlink/?linkid=2228647
[win-zip-arm64]: https://go.microsoft.com/fwlink/?linkid=2229005
[osx-universal]: https://go.microsoft.com/fwlink/?linkid=2228649
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2228179
[osx-arm64]: https://go.microsoft.com/fwlink/?linkid=2228648
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2229006
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2228650
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2228180
Go to our [download page](https://aka.ms/getazuredatastudio) for more specific instructions. Go to our [download page](https://aka.ms/getazuredatastudio) for more specific instructions.
## Try out the latest insiders build from `main` branch: ## Try out the latest insiders build from `main`:
- [Windows User Installer - **Insiders build**](https://azuredatastudio-update.azurewebsites.net/latest/win32-x64-user/insider)
|Platform |Type |Download - Insiders Build | - [Windows System Installer - **Insiders build**](https://azuredatastudio-update.azurewebsites.net/latest/win32-x64/insider)
| --------|-----------------|----------------------- | - [Windows ZIP - **Insiders build**](https://azuredatastudio-update.azurewebsites.net/latest/win32-x64-archive/insider)
|Windows |User Installer |[64 bit][in-win-user]&emsp;[ARM][in-win-user-arm64] | - [macOS ZIP (Universal) - **Insiders build**](https://azuredatastudio-update.azurewebsites.net/latest/darwin-universal/insider)
| |System Installer |[64 bit][in-win-system]&emsp;[ARM][in-win-system-arm64] | - [macOS ZIP (Intel Chip) - **Insiders build**](https://azuredatastudio-update.azurewebsites.net/latest/darwin/insider)
| |.zip |[64 bit][in-win-zip]&emsp;[ARM][in-win-zip-arm64] | - [macOS ZIP (Apple Silicon) - **Insiders build**](https://azuredatastudio-update.azurewebsites.net/latest/darwin-arm64/insider)
|Linux |.tar.gz |[64 bit][in-linux-zip] | - [Linux TAR.GZ - **Insiders build**](https://azuredatastudio-update.azurewebsites.net/latest/linux-x64/insider)
| |.deb |[64 bit][in-linux-deb] |
| |.rpm |[64 bit][in-linux-rpm] |
|Mac |.zip |[Universal][in-osx-universal]&emsp;[Intel Chip][in-osx-zip]&emsp;[Apple Silicon][in-osx-arm64] |
[in-win-user]: https://azuredatastudio-update.azurewebsites.net/latest/win32-x64-user/insider
[in-win-system]: https://azuredatastudio-update.azurewebsites.net/latest/win32-x64/insider
[in-win-zip]: https://azuredatastudio-update.azurewebsites.net/latest/win32-x64-archive/insider
[in-win-user-arm64]: https://azuredatastudio-update.azurewebsites.net/latest/win32-arm64-user/insider
[in-win-system-arm64]: https://azuredatastudio-update.azurewebsites.net/latest/win32-arm64/insider
[in-win-zip-arm64]: https://azuredatastudio-update.azurewebsites.net/latest/win32-arm64-archive/insider
[in-linux-zip]:https://azuredatastudio-update.azurewebsites.net/latest/linux-x64/insider
[in-linux-deb]:https://azuredatastudio-update.azurewebsites.net/latest/linux-deb-x64/insider
[in-linux-rpm]:https://azuredatastudio-update.azurewebsites.net/latest/linux-rpm-x64/insider
[in-osx-universal]: https://azuredatastudio-update.azurewebsites.net/latest/darwin-universal/insider
[in-osx-zip]: https://azuredatastudio-update.azurewebsites.net/latest/darwin/insider
[in-osx-arm64]: https://azuredatastudio-update.azurewebsites.net/latest/darwin-arm64/insider
See the [change log](https://github.com/Microsoft/azuredatastudio/blob/main/CHANGELOG.md) for additional details of what's in this release. See the [change log](https://github.com/Microsoft/azuredatastudio/blob/main/CHANGELOG.md) for additional details of what's in this release.
Go to our [download page](https://aka.ms/getazuredatastudio) for more specific instructions. Go to our [download page](https://aka.ms/getazuredatastudio) for more specific instructions.
@@ -95,12 +66,8 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
## Localization ## Localization
Azure Data Studio is localized into 10 languages: French, Italian, German, Spanish, Simplified Chinese, Traditional Chinese, Japanese, Korean, Russian, and Portuguese (Brazil). The language packs are available in the Extension Manager marketplace. Simply, search for the specific language using the extension marketplace and install. Once you install the selected language, Azure Data Studio will prompt you to restart with the new language. Azure Data Studio is localized into 10 languages: French, Italian, German, Spanish, Simplified Chinese, Traditional Chinese, Japanese, Korean, Russian, and Portuguese (Brazil). The language packs are available in the Extension Manager marketplace. Simply, search for the specific language using the extension marketplace and install. Once you install the selected language, Azure Data Studio will prompt you to restart with the new language.
## Telemetry
Azure Data Studio 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 [disable telemetry reporting](https://aka.ms/ads-disable-telemetry) documentation.
## Privacy Statement ## Privacy Statement
The [Microsoft Privacy Statement](https://go.microsoft.com/fwlink/?LinkID=824704) describes the privacy statement of this software. The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
## Contributions and "Thank You" ## Contributions and "Thank You"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes: We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
@@ -165,3 +132,11 @@ And of course, we'd like to thank the authors of all upstream dependencies. Ple
Copyright (c) Microsoft Corporation. All rights reserved. Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the [Source EULA](LICENSE.txt). Licensed under the [Source EULA](LICENSE.txt).
[win-user]: https://go.microsoft.com/fwlink/?linkid=2204567
[win-system]: https://go.microsoft.com/fwlink/?linkid=2204568
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2204772
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2204569
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2204773
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2204774
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2204570

View File

@@ -12,7 +12,6 @@ const files = [
'.build/langpacks/**/*.vsix', '.build/langpacks/**/*.vsix',
'.build/extensions/**/*.vsix', '.build/extensions/**/*.vsix',
'.build/win32-x64/**/*.{exe,zip}', '.build/win32-x64/**/*.{exe,zip}',
'.build/win32-arm64/**/*.{exe,zip}',
'.build/linux/sha256hashes.txt', '.build/linux/sha256hashes.txt',
'.build/linux/deb/amd64/deb/*.deb', '.build/linux/deb/amd64/deb/*.deb',
'.build/linux/rpm/x86_64/*.rpm', '.build/linux/rpm/x86_64/*.rpm',

View File

@@ -13,8 +13,7 @@ import * as fs from 'fs';
const files = [ const files = [
'.build/langpacks/**/*.vsix', // langpacks '.build/langpacks/**/*.vsix', // langpacks
'.build/extensions/**/*.vsix', // external extensions '.build/extensions/**/*.vsix', // external extensions
'.build/win32-x64/**/*.{exe,zip}', // windows x64 binaries '.build/win32-x64/**/*.{exe,zip}', // windows binaries
'.build/win32-arm64/**/*.{exe,zip}', // windows arm64 binaries
'.build/linux/sha256hashes.txt', // linux hashes '.build/linux/sha256hashes.txt', // linux hashes
'.build/linux/deb/amd64/deb/*.deb', // linux debs '.build/linux/deb/amd64/deb/*.deb', // linux debs
'.build/linux/rpm/x86_64/*.rpm', // linux rpms '.build/linux/rpm/x86_64/*.rpm', // linux rpms

View File

@@ -48,7 +48,6 @@ stages:
variables: variables:
NPM_CONFIG_ARCH: arm64 NPM_CONFIG_ARCH: arm64
VSCODE_ARCH: arm64 VSCODE_ARCH: arm64
AZDATA_RUNTIME: OSX_ARM64
# Do not run tests for arm64 build # Do not run tests for arm64 build
RUN_TESTS: false RUN_TESTS: false
RUN_SMOKE_TESTS: false RUN_SMOKE_TESTS: false
@@ -103,32 +102,21 @@ stages:
steps: steps:
- template: linux/sql-product-build-linux.yml - template: linux/sql-product-build-linux.yml
parameters: parameters:
extensionsToUnitTest: ["admin-tool-ext-win", "agent", "azcli", "azurecore", "cms", "dacpac", "datavirtualization", "data-workspace", "import", "machine-learning", "notebook", "resource-deployment", "schema-compare", "sql-bindings", "sql-database-projects"] extensionsToUnitTest: ["admin-tool-ext-win", "agent", "azcli", "azurecore", "cms", "dacpac", "data-workspace", "import", "machine-learning", "notebook", "resource-deployment", "schema-compare", "sql-bindings", "sql-database-projects"]
timeoutInMinutes: 90 timeoutInMinutes: 90
- stage: Windows - stage: Windows
condition: and(succeeded(), or(eq(variables['VSCODE_BUILD_WIN32'], true), eq(variables['VSCODE_BUILD_WIN32_ARM64'], true))) condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], true))
pool: pool:
vmImage: 'windows-2019' vmImage: 'windows-2019'
dependsOn: dependsOn:
- Compile - Compile
jobs: jobs:
- job: Windows_x64 - job: Windows
variables:
VSCODE_ARCH: x64
steps: steps:
- template: win32/sql-product-build-win32.yml - template: win32/sql-product-build-win32.yml
condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true'))
timeoutInMinutes: 90 timeoutInMinutes: 90
- 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
timeoutInMinutes: 90
# disable due to invalid machine pool (karlb 3/9/2022) # disable due to invalid machine pool (karlb 3/9/2022)
# - job: Windows_Test # - job: Windows_Test
# condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true')) # condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true'))

View File

@@ -1,7 +1,7 @@
. build/azure-pipelines/win32/exec.ps1 . build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$Arch = $env:VSCODE_ARCH $Arch = "x64"
$Repo = "$(pwd)" $Repo = "$(pwd)"
$Root = "$Repo\.." $Root = "$Repo\.."

View File

@@ -48,7 +48,7 @@ steps:
- powershell: | - powershell: |
. build/azure-pipelines/win32/exec.ps1 . build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
exec { node build/azure-pipelines/common/sql-computeNodeModulesCacheKey.js $(VSCODE_ARCH) > .build/yarnlockhash } exec { node build/azure-pipelines/common/sql-computeNodeModulesCacheKey.js > .build/yarnlockhash }
displayName: Prepare yarn cache key displayName: Prepare yarn cache key
- task: Cache@2 - task: Cache@2
@@ -69,7 +69,6 @@ steps:
- powershell: | - powershell: |
. build/azure-pipelines/win32/exec.ps1 . build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$env:npm_config_arch="$(VSCODE_ARCH)"
$env:CHILD_CONCURRENCY="1" $env:CHILD_CONCURRENCY="1"
exec { yarn --frozen-lockfile } exec { yarn --frozen-lockfile }
env: env:
@@ -103,8 +102,8 @@ steps:
. build/azure-pipelines/win32/exec.ps1 . build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
exec { yarn gulp "package-rebuild-extensions" } exec { yarn gulp "package-rebuild-extensions" }
exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-min-ci" } exec { yarn gulp "vscode-win32-x64-min-ci" }
exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" } exec { yarn gulp "vscode-win32-x64-inno-updater" }
displayName: Build displayName: Build
env: env:
VSCODE_MIXIN_PASSWORD: $(github-distro-mixin-password) VSCODE_MIXIN_PASSWORD: $(github-distro-mixin-password)
@@ -118,7 +117,7 @@ steps:
vstsFeed: '2191dd5f-4aec-491b-ac50-568bbc331c8a' vstsFeed: '2191dd5f-4aec-491b-ac50-568bbc331c8a'
vstsFeedPackage: '2e355f03-a97e-499a-949b-f02d62b6160c' vstsFeedPackage: '2e355f03-a97e-499a-949b-f02d62b6160c'
vstsPackageVersion: '*' vstsPackageVersion: '*'
condition: and(succeeded(), eq(variables['VSCODE_QUALITY'], 'saw'), ne(variables['VSCODE_ARCH'], 'arm64')) condition: and(succeeded(), eq(variables['VSCODE_QUALITY'], 'saw'))
- powershell: | - powershell: |
# Install TSGOps specific extensions # Install TSGOps specific extensions
@@ -131,7 +130,7 @@ steps:
Move-Item $tempFilePath $zipFilePath Move-Item $tempFilePath $zipFilePath
Expand-Archive $zipFilePath -DestinationPath $adsExtensionPath Expand-Archive $zipFilePath -DestinationPath $adsExtensionPath
displayName: Install SAW Extensions displayName: Install SAW Extensions
condition: and(succeeded(), eq(variables['VSCODE_QUALITY'], 'saw'), ne(variables['VSCODE_ARCH'], 'arm64')) condition: and(succeeded(), eq(variables['VSCODE_QUALITY'], 'saw'))
# - powershell: | @anthonydresser unit tests timeout never existing the node process # - powershell: | @anthonydresser unit tests timeout never existing the node process
# . build/azure-pipelines/win32/exec.ps1 # . build/azure-pipelines/win32/exec.ps1
@@ -159,27 +158,8 @@ steps:
displayName: 'Sign out code' displayName: 'Sign out code'
inputs: inputs:
ConnectedServiceName: 'Code Signing' ConnectedServiceName: 'Code Signing'
FolderPath: '$(agent.builddirectory)/azuredatastudio-win32-$(VSCODE_ARCH)' FolderPath: '$(agent.builddirectory)/azuredatastudio-win32-x64'
Pattern: | Pattern: '*.exe,*.node,resources/app/node_modules.asar.unpacked/*.dll,swiftshader/*.dll,d3dcompiler_47.dll,vulkan-1.dll,libGLESv2.dll,ffmpeg.dll,libEGL.dll,Microsoft.SqlTools.Hosting.dll,Microsoft.SqlTools.ResourceProvider.Core.dll,Microsoft.SqlTools.ResourceProvider.DefaultImpl.dll,MicrosoftSqlToolsCredentials.dll,MicrosoftSqlToolsServiceLayer.dll,Newtonsoft.Json.dll,SqlSerializationService.dll,SqlToolsResourceProviderService.dll,Microsoft.SqlServer.*.dll,Microsoft.Data.Tools.Sql.BatchParser.dll'
*.exe
*.node
!**/node_modules/**/*
resources/app/node_modules.asar.unpacked/*.dll
d3dcompiler_47.dll
vulkan-1.dll
libGLESv2.dll
ffmpeg.dll
libEGL.dll
Microsoft.SqlTools.Hosting.dll
Microsoft.SqlTools.ResourceProvider.Core.dll
Microsoft.SqlTools.ResourceProvider.DefaultImpl.dll
MicrosoftSqlToolsCredentials.dll
MicrosoftSqlToolsServiceLayer.dll
Newtonsoft.Json.dll,SqlSerializationService.dll
SqlToolsResourceProviderService.dll
Microsoft.SqlServer.*.dll
Microsoft.Data.Tools.Sql.BatchParser.dll
useMinimatch: true
signConfigType: inlineSignParams signConfigType: inlineSignParams
inlineOperation: | inlineOperation: |
[ [
@@ -232,9 +212,9 @@ steps:
- powershell: | - powershell: |
. build/azure-pipelines/win32/exec.ps1 . build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-user-setup" } exec { yarn gulp "vscode-win32-x64-user-setup" }
exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-system-setup" } exec { yarn gulp "vscode-win32-x64-system-setup" }
exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-archive" } exec { yarn gulp "vscode-win32-x64-archive" }
displayName: Archive & User & System setup displayName: Archive & User & System setup
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1

View File

@@ -41,7 +41,7 @@ steps:
inputs: inputs:
azureSubscription: 'ClientToolsInfra_670062 (88d5392f-a34f-4769-b405-f597fc533613)' azureSubscription: 'ClientToolsInfra_670062 (88d5392f-a34f-4769-b405-f597fc533613)'
KeyVaultName: SqlToolsSecretStore KeyVaultName: SqlToolsSecretStore
SecretsFilter: 'ads-integration-test-azure-server,ads-integration-test-azure-server-password,ads-integration-test-azure-server-username,ads-integration-test-standalone-server,ads-integration-test-standalone-server-password,ads-integration-test-standalone-server-username,ads-integration-test-standalone-server-2019,ads-integration-test-standalone-server-password-2019,ads-integration-test-standalone-server-username-2019' SecretsFilter: 'ads-integration-test-azure-server,ads-integration-test-azure-server-password,ads-integration-test-azure-server-username,ads-integration-test-bdc-server,ads-integration-test-bdc-server-password,ads-integration-test-bdc-server-username,ads-integration-test-standalone-server,ads-integration-test-standalone-server-password,ads-integration-test-standalone-server-username,ads-integration-test-standalone-server-2019,ads-integration-test-standalone-server-password-2019,ads-integration-test-standalone-server-username-2019'
- powershell: | - powershell: |
. build/azure-pipelines/win32/exec.ps1 . build/azure-pipelines/win32/exec.ps1
@@ -54,6 +54,9 @@ steps:
condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), ne(variables['RUN_INTEGRATION_TESTS'], 'false'))) condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), ne(variables['RUN_INTEGRATION_TESTS'], 'false')))
displayName: Run stable tests displayName: Run stable tests
env: env:
BDC_BACKEND_USERNAME: $(ads-integration-test-bdc-server-username)
BDC_BACKEND_PWD: $(ads-integration-test-bdc-server-password)
BDC_BACKEND_HOSTNAME: $(ads-integration-test-bdc-server)
STANDALONE_SQL_USERNAME: $(ads-integration-test-standalone-server-username) STANDALONE_SQL_USERNAME: $(ads-integration-test-standalone-server-username)
STANDALONE_SQL_PWD: $(ads-integration-test-standalone-server-password) STANDALONE_SQL_PWD: $(ads-integration-test-standalone-server-password)
STANDALONE_SQL: $(ads-integration-test-standalone-server) STANDALONE_SQL: $(ads-integration-test-standalone-server)

View File

@@ -8,40 +8,32 @@ Param(
$env:AZURE_STORAGE_ACCESS_KEY_2 = $storageKey $env:AZURE_STORAGE_ACCESS_KEY_2 = $storageKey
$env:AZURE_DOCUMENTDB_MASTERKEY = $documentDbKey $env:AZURE_DOCUMENTDB_MASTERKEY = $documentDbKey
$Flavors = "x64","arm64" $ExeName = "AzureDataStudioSetup.exe"
$FlavorSuffixes = "","-arm64" $SystemExe = "$artifactsDir\win32-x64\system-setup\$ExeName"
$UserExe = "$artifactsDir\win32-x64\user-setup\$ExeName"
$UserExeName = "AzureDataStudioUserSetup.exe"
$ZipName = "azuredatastudio-win32-x64.zip"
$Zip = "$artifactsDir\win32-x64\archive\$ZipName"
For($i = 0; $i -lt $Flavors.Length; $i++) $VersionJson = Get-Content -Raw -Path "$artifactsDir\version.json" | ConvertFrom-Json
{ $Version = $VersionJson.version
$Flavor = $Flavors[$i] $Quality = $VersionJson.quality
$FlavorSuffix = $FlavorSuffixes[$i] $CommitId = $VersionJson.commit
$ExeName = "AzureDataStudioSetup.exe"
$SystemExe = "$artifactsDir\win32-$Flavor\system-setup\$ExeName"
$UserExe = "$artifactsDir\win32-$Flavor\user-setup\$ExeName"
$UserExeName = "AzureDataStudioUserSetup.exe"
$ZipName = "azuredatastudio-win32-$Flavor.zip"
$Zip = "$artifactsDir\win32-$Flavor\archive\$ZipName"
$VersionJson = Get-Content -Raw -Path "$artifactsDir\version.json" | ConvertFrom-Json $ZipUploadName = "azuredatastudio-windows-$Version"
$Version = $VersionJson.version $SetupUploadName = "azuredatastudio-windows-setup-$Version"
$Quality = $VersionJson.quality $UserUploadName = "azuredatastudio-windows-user-setup-$Version"
$CommitId = $VersionJson.commit
$ZipUploadName = "azuredatastudio-windows$FlavorSuffix-$Version" $assetPlatform = "win32-x64"
$SetupUploadName = "azuredatastudio-windows$FlavorSuffix-setup-$Version"
$UserUploadName = "azuredatastudio-windows$FlavorSuffix-user-setup-$Version"
$assetPlatform = "win32-$Flavor" If (-NOT ($Quality -eq "stable")) {
$ZipUploadName = "$ZipUploadName-$Quality"
If (-NOT ($Quality -eq "stable")) { $SetupUploadName = "$SetupUploadName-$Quality"
$ZipUploadName = "$ZipUploadName-$Quality" $UserUploadName = "$UserUploadName-$Quality"
$SetupUploadName = "$SetupUploadName-$Quality"
$UserUploadName = "$UserUploadName-$Quality"
}
node $sourcesDir/build/azure-pipelines/common/publish.js $Quality "$assetPlatform-archive" archive "$ZipUploadName.zip" $Version true $Zip $CommitId
node $sourcesDir/build/azure-pipelines/common/publish.js $Quality "$assetPlatform" setup "$SetupUploadName.exe" $Version true $SystemExe $CommitId
node $sourcesDir/build/azure-pipelines/common/publish.js $Quality "$assetPlatform-user" setup "$UserUploadName.exe" $Version true $UserExe $CommitId
} }
node $sourcesDir/build/azure-pipelines/common/publish.js $Quality "$assetPlatform-archive" archive "$ZipUploadName.zip" $Version true $Zip $CommitId
node $sourcesDir/build/azure-pipelines/common/publish.js $Quality "$assetPlatform" setup "$SetupUploadName.exe" $Version true $SystemExe $CommitId
node $sourcesDir/build/azure-pipelines/common/publish.js $Quality "$assetPlatform-user" setup "$UserUploadName.exe" $Version true $UserExe $CommitId

View File

@@ -30,7 +30,8 @@ app.once('ready', () => {
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
contextIsolation: false, contextIsolation: false,
enableWebSQL: false enableWebSQL: false,
nativeWindowOpen: true
} }
}); });
window.setMenuBarVisibility(false); window.setMenuBarVisibility(false);

View File

@@ -28,16 +28,17 @@ async function main() {
const outAppPath = path.join(buildDir, `azuredatastudio-darwin-${arch}`, appName); // {{SQL CARBON EDIT}} - CHANGE VSCode to azuredatastudio 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'); const productJsonPath = path.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json');
// {{SQL CARBON EDIT}} // {{SQL CARBON EDIT}}
// STS binaries for x64 and arm64 have different file count and cannot be combined // 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.
// Remove them from the package before the makeUniversalApp step and copy them to the universal package after it. // 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.
const stsPath = '/Contents/Resources/app/extensions/mssql/sqltoolsservice'; const stsPath = '/Contents/Resources/app/extensions/mssql/sqltoolsservice';
const tempSTSDir = path.join(buildDir, 'sqltoolsservice'); const tempSTSDir = path.join(buildDir, 'sqltoolsservice');
const x64STSDir = path.join(x64AppPath, stsPath); const x64STSDir = path.join(x64AppPath, stsPath);
const arm64STSDir = path.join(arm64AppPath, stsPath); const arm64STSDir = path.join(arm64AppPath, stsPath);
const targetSTSDirs = [x64STSDir, arm64STSDir]; const targetSTSDirs = [x64STSDir, arm64STSDir];
// backup the STS folders to a temporary directory, later they will be copied to the universal app directory. // backup the x64 STS to a temporary directory, later it will be copied to the universal app directory.
await fs.copy(x64STSDir, tempSTSDir); await fs.copy(x64STSDir, tempSTSDir);
await fs.copy(arm64STSDir, tempSTSDir);
// delete STS directories from both x64 ADS and arm64 ADS. // delete STS directories from both x64 ADS and arm64 ADS.
console.debug(`Removing SqlToolsService folders.`); console.debug(`Removing SqlToolsService folders.`);
targetSTSDirs.forEach(async (dir) => { targetSTSDirs.forEach(async (dir) => {
@@ -77,19 +78,14 @@ async function main() {
}); });
await fs.writeJson(productJsonPath, productJson); await fs.writeJson(productJsonPath, productJson);
// Verify if native module architecture is correct // Verify if native module architecture is correct
// {{SQL CARBON EDIT}} Some of our extensions have their own keytar so lookup const findOutput = await (0, cross_spawn_promise_1.spawn)('find', [outAppPath, '-name', 'keytar.node']);
// only in core modules since this code doesn't work with multiple found modules.
// We're assuming here the intent is just to check a single file for validation and not
// needing to check any others since this currently is ignoring all other native modules.
const findOutput = await (0, cross_spawn_promise_1.spawn)('find', [outAppPath, '-name', 'keytar.node', '-regex', '.*node_modules.asar.unpacked.*',]);
const lipoOutput = await (0, cross_spawn_promise_1.spawn)('lipo', ['-archs', findOutput.replace(/\n$/, '')]); const lipoOutput = await (0, cross_spawn_promise_1.spawn)('lipo', ['-archs', findOutput.replace(/\n$/, '')]);
if (lipoOutput.replace(/\n$/, '') !== 'x86_64 arm64') { if (lipoOutput.replace(/\n$/, '') !== 'x86_64 arm64') {
throw new Error(`Invalid arch, got : ${lipoOutput}`); throw new Error(`Invalid arch, got : ${lipoOutput}`);
} }
// {{SQL CARBON EDIT}} // {{SQL CARBON EDIT}}
console.debug(`Copying SqlToolsService to the universal app folder.`); console.debug(`Copying SqlToolsService to the universal app folder.`);
await fs.copy(path.join(tempSTSDir, 'OSX'), path.join(outAppPath, stsPath, 'OSX'), { overwrite: true }); await fs.copy(tempSTSDir, path.join(outAppPath, stsPath), { overwrite: true });
await fs.copy(path.join(tempSTSDir, 'OSX_ARM64'), path.join(outAppPath, stsPath, 'OSX_ARM64'), { overwrite: true });
} }
if (require.main === module) { if (require.main === module) {
main().catch(err => { main().catch(err => {

View File

@@ -34,16 +34,17 @@ async function main() {
const productJsonPath = path.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json'); const productJsonPath = path.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json');
// {{SQL CARBON EDIT}} // {{SQL CARBON EDIT}}
// STS binaries for x64 and arm64 have different file count and cannot be combined // 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.
// Remove them from the package before the makeUniversalApp step and copy them to the universal package after it. // 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.
const stsPath = '/Contents/Resources/app/extensions/mssql/sqltoolsservice'; const stsPath = '/Contents/Resources/app/extensions/mssql/sqltoolsservice';
const tempSTSDir = path.join(buildDir, 'sqltoolsservice'); const tempSTSDir = path.join(buildDir, 'sqltoolsservice');
const x64STSDir = path.join(x64AppPath, stsPath); const x64STSDir = path.join(x64AppPath, stsPath);
const arm64STSDir = path.join(arm64AppPath, stsPath); const arm64STSDir = path.join(arm64AppPath, stsPath);
const targetSTSDirs = [x64STSDir, arm64STSDir]; const targetSTSDirs = [x64STSDir, arm64STSDir];
// backup the STS folders to a temporary directory, later they will be copied to the universal app directory. // backup the x64 STS to a temporary directory, later it will be copied to the universal app directory.
await fs.copy(x64STSDir, tempSTSDir); await fs.copy(x64STSDir, tempSTSDir);
await fs.copy(arm64STSDir, tempSTSDir);
// delete STS directories from both x64 ADS and arm64 ADS. // delete STS directories from both x64 ADS and arm64 ADS.
console.debug(`Removing SqlToolsService folders.`); console.debug(`Removing SqlToolsService folders.`);
targetSTSDirs.forEach(async dir => { targetSTSDirs.forEach(async dir => {
@@ -87,11 +88,7 @@ async function main() {
await fs.writeJson(productJsonPath, productJson); await fs.writeJson(productJsonPath, productJson);
// Verify if native module architecture is correct // Verify if native module architecture is correct
// {{SQL CARBON EDIT}} Some of our extensions have their own keytar so lookup const findOutput = await spawn('find', [outAppPath, '-name', 'keytar.node']);
// only in core modules since this code doesn't work with multiple found modules.
// We're assuming here the intent is just to check a single file for validation and not
// needing to check any others since this currently is ignoring all other native modules.
const findOutput = await spawn('find', [outAppPath, '-name', 'keytar.node', '-regex', '.*node_modules.asar.unpacked.*',]);
const lipoOutput = await spawn('lipo', ['-archs', findOutput.replace(/\n$/, '')]); const lipoOutput = await spawn('lipo', ['-archs', findOutput.replace(/\n$/, '')]);
if (lipoOutput.replace(/\n$/, '') !== 'x86_64 arm64') { if (lipoOutput.replace(/\n$/, '') !== 'x86_64 arm64') {
throw new Error(`Invalid arch, got : ${lipoOutput}`); throw new Error(`Invalid arch, got : ${lipoOutput}`);
@@ -99,8 +96,7 @@ async function main() {
// {{SQL CARBON EDIT}} // {{SQL CARBON EDIT}}
console.debug(`Copying SqlToolsService to the universal app folder.`); console.debug(`Copying SqlToolsService to the universal app folder.`);
await fs.copy(path.join(tempSTSDir, 'OSX'), path.join(outAppPath, stsPath, 'OSX'), { overwrite: true }); await fs.copy(tempSTSDir, path.join(outAppPath, stsPath), { overwrite: true });
await fs.copy(path.join(tempSTSDir, 'OSX_ARM64'), path.join(outAppPath, stsPath, 'OSX_ARM64'), { overwrite: true });
} }
if (require.main === module) { if (require.main === module) {

View File

@@ -47,7 +47,6 @@ module.exports.unicodeFilter = [
'!build/win32/**', '!build/win32/**',
'!extensions/markdown-language-features/notebook-out/*.js', '!extensions/markdown-language-features/notebook-out/*.js',
'!extensions/markdown-math/notebook-out/**', '!extensions/markdown-math/notebook-out/**',
'!extensions/notebook-renderers/renderer-out/**',
'!extensions/php-language-features/src/features/phpGlobalFunctions.ts', '!extensions/php-language-features/src/features/phpGlobalFunctions.ts',
'!extensions/typescript-language-features/test-workspace/**', '!extensions/typescript-language-features/test-workspace/**',
'!extensions/vscode-api-tests/testWorkspace/**', '!extensions/vscode-api-tests/testWorkspace/**',
@@ -131,7 +130,6 @@ module.exports.indentationFilter = [
'!extensions/markdown-language-features/media/*.js', '!extensions/markdown-language-features/media/*.js',
'!extensions/markdown-language-features/notebook-out/*.js', '!extensions/markdown-language-features/notebook-out/*.js',
'!extensions/markdown-math/notebook-out/*.js', '!extensions/markdown-math/notebook-out/*.js',
'!extensions/notebook-renderers/renderer-out/*.js',
'!extensions/simple-browser/media/*.js', '!extensions/simple-browser/media/*.js',
// {{SQL CARBON EDIT}} Except for our stuff // {{SQL CARBON EDIT}} Except for our stuff
@@ -154,7 +152,8 @@ module.exports.indentationFilter = [
'!extensions/sql-database-projects/src/test/baselines/*.json', '!extensions/sql-database-projects/src/test/baselines/*.json',
'!extensions/sql-database-projects/src/test/baselines/*.sqlproj', '!extensions/sql-database-projects/src/test/baselines/*.sqlproj',
'!extensions/sql-database-projects/BuildDirectory/SystemDacpacs/**', '!extensions/sql-database-projects/BuildDirectory/SystemDacpacs/**',
'!extensions/datavirtualization/scaleoutdataservice/**', '!extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts',
'!extensions/big-data-cluster/src/bigDataCluster/controller/clusterApiGenerated2.ts',
'!resources/linux/snap/electron-launch', '!resources/linux/snap/electron-launch',
'!extensions/markdown-language-features/media/*.js', '!extensions/markdown-language-features/media/*.js',
'!extensions/simple-browser/media/*.js', '!extensions/simple-browser/media/*.js',
@@ -203,6 +202,7 @@ module.exports.copyrightFilter = [
'!extensions/import/flatfileimportservice/**', '!extensions/import/flatfileimportservice/**',
'!extensions/kusto/src/prompts/**', '!extensions/kusto/src/prompts/**',
'!extensions/mssql/sqltoolsservice/**', '!extensions/mssql/sqltoolsservice/**',
'!extensions/mssql/src/hdfs/webhdfs.ts',
'!extensions/mssql/src/prompts/**', '!extensions/mssql/src/prompts/**',
'!extensions/notebook/resources/jupyter_config/**', '!extensions/notebook/resources/jupyter_config/**',
'!extensions/notebook/src/intellisense/text.ts', '!extensions/notebook/src/intellisense/text.ts',
@@ -257,6 +257,8 @@ module.exports.tsFormattingFilter = [
'!extensions/html-language-features/server/lib/jquery.d.ts', '!extensions/html-language-features/server/lib/jquery.d.ts',
// {{SQL CARBON EDIT}} // {{SQL CARBON EDIT}}
'!extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts',
'!extensions/big-data-cluster/src/bigDataCluster/controller/tokenApiGenerated.ts',
'!src/vs/workbench/services/themes/common/textMateScopeMatcher.ts', // skip this because we have no plans on touching this and its not ours '!src/vs/workbench/services/themes/common/textMateScopeMatcher.ts', // skip this because we have no plans on touching this and its not ours
'!src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts', // skip this because known issue '!src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts', // skip this because known issue
'!build/**/*' '!build/**/*'

View File

@@ -24,6 +24,11 @@ const product = require('../product.json');
const extensionsPath = path.join(path.dirname(__dirname), 'extensions'); const extensionsPath = path.join(path.dirname(__dirname), 'extensions');
// {{SQL CARBON EDIT}} - TODO: Import needs to be updated to work with langpacks.
const sqlLocalizedExtensions = [
'import',
];
// {{SQL CARBON EDIT}} Not doing this for us right now // {{SQL CARBON EDIT}} Not doing this for us right now
// To save 250ms for each gulp startup, we are caching the result here // To save 250ms for each gulp startup, we are caching the result here
const compilations = glob.sync('**/tsconfig.json', { const compilations = glob.sync('**/tsconfig.json', {
@@ -69,6 +74,7 @@ const compilations = [
'vscode-api-tests/tsconfig.json', 'vscode-api-tests/tsconfig.json',
'vscode-colorize-tests/tsconfig.json', 'vscode-colorize-tests/tsconfig.json',
'vscode-custom-editor-tests/tsconfig.json', 'vscode-custom-editor-tests/tsconfig.json',
'vscode-notebook-tests/tsconfig.json',
'vscode-test-resolver/tsconfig.json' 'vscode-test-resolver/tsconfig.json'
]; ];
*/ */
@@ -155,7 +161,7 @@ const tasks = compilations.map(function (tsconfigFile) {
const cleanTask = task.define(`clean-extension-${name}`, util.rimraf(out)); const cleanTask = task.define(`clean-extension-${name}`, util.rimraf(out));
const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, () => { const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, () => {
const pipeline = createPipeline(false, true); const pipeline = createPipeline(sqlLocalizedExtensions.includes(name), true); // {{SQL CARBON EDIT}}
const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts']));
const input = es.merge(nonts, pipeline.tsProjectSrc()); const input = es.merge(nonts, pipeline.tsProjectSrc());

View File

@@ -448,7 +448,7 @@ function packagePkgTask(platform, arch, pkgTarget) {
// rebuild extensions that contain native npm modules or have conditional webpack rules // rebuild extensions that contain native npm modules or have conditional webpack rules
// when building with the web .yarnrc settings (e.g. runtime=node, etc.) // when building with the web .yarnrc settings (e.g. runtime=node, etc.)
// this is needed to have correct module set published with desired ABI // this is needed to have correct module set published with desired ABI
const rebuildExtensions = ['mssql', 'notebook']; const rebuildExtensions = ['big-data-cluster', 'mssql', 'notebook'];
const EXTENSIONS = path.join(REPO_ROOT, 'extensions'); const EXTENSIONS = path.join(REPO_ROOT, 'extensions');
function exec(cmdLine, cwd) { function exec(cmdLine, cwd) {
console.log(cmdLine); console.log(cmdLine);

View File

@@ -40,8 +40,8 @@ const formatFiles = (some) => {
}); });
}); });
return gulp.src(some, { return gulp.src(some, {
base: '.' base: '.'
}) })
.pipe(filter(f => !f.stat.isDirectory())) .pipe(filter(f => !f.stat.isDirectory()))
.pipe(formatting); .pipe(formatting);
@@ -102,19 +102,19 @@ gulp.task('package-external-extensions', task.series(
const extensionName = path.basename(extensionPath); const extensionName = path.basename(extensionPath);
return { name: extensionName, path: extensionPath }; return { name: extensionName, path: extensionPath };
}) })
.filter(element => ext.vscodeExternalExtensions.indexOf(element.name) === -1) // VS Code external extensions are bundled into ADS so no need to create a normal VSIX for them .filter(element => ext.vscodeExternalExtensions.indexOf(element.name) === -1) // VS Code external extensions are bundled into ADS so no need to create a normal VSIX for them
.map(element => { .map(element => {
const pkgJson = require(path.join(element.path, 'package.json')); const pkgJson = require(path.join(element.path, 'package.json'));
const vsixDirectory = path.join(root, '.build', 'extensions'); const vsixDirectory = path.join(root, '.build', 'extensions');
mkdirp.sync(vsixDirectory); mkdirp.sync(vsixDirectory);
const packagePath = path.join(vsixDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`); const packagePath = path.join(vsixDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`);
console.info('Creating vsix for ' + element.path + ' result:' + packagePath); console.info('Creating vsix for ' + element.path + ' result:' + packagePath);
return vsce.createVSIX({ return vsce.createVSIX({
cwd: element.path, cwd: element.path,
packagePath: packagePath, packagePath: packagePath,
useYarn: true useYarn: true
});
}); });
});
// Wait for all the initial VSIXes to be completed before making the VS Code ones since we'll be overwriting // Wait for all the initial VSIXes to be completed before making the VS Code ones since we'll be overwriting
// values in the package.json for those. // values in the package.json for those.
await Promise.all(vsixes); await Promise.all(vsixes);
@@ -133,26 +133,15 @@ gulp.task('package-external-extensions', task.series(
// And now use gulp-json-editor to modify the contents // And now use gulp-json-editor to modify the contents
const updateData = JSON.parse(fs.readFileSync(vscodeManifestFullPath)); // Read in the set of values to replace from package.vscode.json const updateData = JSON.parse(fs.readFileSync(vscodeManifestFullPath)); // Read in the set of values to replace from package.vscode.json
Object.keys(updateData).forEach(key => { Object.keys(updateData).forEach(key => {
if (key !== 'contributes') { data[key] = updateData[key];
data[key] = updateData[key];
}
}); });
if (data.contributes?.menus) { if(data.contributes?.menus){
// Remove ADS-only menus. This is a subset of the menus listed in https://github.com/microsoft/azuredatastudio/blob/main/src/vs/workbench/api/common/menusExtensionPoint.ts // Remove ADS-only menus. This is a subset of the menus listed in https://github.com/microsoft/azuredatastudio/blob/main/src/vs/workbench/api/common/menusExtensionPoint.ts
// More can be added to the list as needed. // More can be added to the list as needed.
['objectExplorer/item/context', 'dataExplorer/context', 'dashboard/toolbar'].forEach(menu => { ['objectExplorer/item/context', 'dataExplorer/context', 'dashboard/toolbar'].forEach(menu => {
delete data.contributes.menus[menu]; delete data.contributes.menus[menu];
}); });
} }
// Add any configuration properties from the package.vscode.json
// Currently only supports bringing over properties in the first config object found and doesn't support modifying the title
if (updateData.contributes?.configuration[0]?.properties) {
Object.keys(updateData.contributes.configuration[0].properties).forEach(key => {
data.contributes.configuration[0].properties[key] = updateData.contributes.configuration[0].properties[key];
});
}
return data; return data;
}, { beautify: false })) }, { beautify: false }))
.pipe(gulp.dest(packageDir)); .pipe(gulp.dest(packageDir));

View File

@@ -125,10 +125,9 @@ const extensionsFilter = filter([
'**/azcli.xlf', '**/azcli.xlf',
'**/azurecore.xlf', '**/azurecore.xlf',
'**/azurehybridtoolkit.xlf', '**/azurehybridtoolkit.xlf',
'**/azuremonitor.xlf', '**/big-data-cluster.xlf',
'**/cms.xlf', '**/cms.xlf',
'**/dacpac.xlf', '**/dacpac.xlf',
'**/datavirtualization.xlf',
'**/git.xlf', '**/git.xlf',
'**/data-workspace.xlf', '**/data-workspace.xlf',
'**/import.xlf', '**/import.xlf',

View File

@@ -104,8 +104,8 @@ function buildWin32Setup(arch, target) {
IncompatibleTargetAppId: { 'ia32': product.win32AppId, 'x64': product.win32x64AppId, 'arm64': product.win32arm64AppId }[arch], IncompatibleTargetAppId: { 'ia32': product.win32AppId, 'x64': product.win32x64AppId, 'arm64': product.win32arm64AppId }[arch],
IncompatibleArchAppId: { 'ia32': x64AppId, 'x64': ia32AppId, 'arm64': ia32AppId }[arch], IncompatibleArchAppId: { 'ia32': x64AppId, 'x64': ia32AppId, 'arm64': ia32AppId }[arch],
AppUserId: product.win32AppUserModelId, 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. ArchitecturesAllowed: { 'ia32': '', 'x64': 'x64', 'arm64': 'arm64' }[arch],
ArchitecturesInstallIn64BitMode: { 'ia32': '', 'x64': 'x64', 'arm64': 'arm64 x64' }[arch], //{{SQL CARBON EDIT}} - same as line above. ArchitecturesInstallIn64BitMode: { 'ia32': '', 'x64': 'x64', 'arm64': 'arm64' }[arch],
SourceDir: sourcePath, SourceDir: sourcePath,
RepoDir: repoPath, RepoDir: repoPath,
OutputDir: outputPath, OutputDir: outputPath,

View File

@@ -241,6 +241,7 @@ const excludedExtensions = [
'vscode-test-resolver', 'vscode-test-resolver',
'ms-vscode.node-debug', 'ms-vscode.node-debug',
'ms-vscode.node-debug2', 'ms-vscode.node-debug2',
'vscode-notebook-tests',
'vscode-custom-editor-tests', 'vscode-custom-editor-tests',
'integration-tests', // {{SQL CARBON EDIT}} 'integration-tests', // {{SQL CARBON EDIT}}
]; ];
@@ -260,9 +261,9 @@ const externalExtensions = [
'azuremonitor', 'azuremonitor',
'cms', 'cms',
'dacpac', 'dacpac',
'datavirtualization',
'import', 'import',
'kusto', 'kusto',
'liveshare',
'machine-learning', 'machine-learning',
'profiler', 'profiler',
'query-history', 'query-history',
@@ -281,6 +282,7 @@ exports.vscodeExternalExtensions = [
]; ];
// extensions that require a rebuild since they have native parts // extensions that require a rebuild since they have native parts
const rebuildExtensions = [ const rebuildExtensions = [
'big-data-cluster',
'mssql' 'mssql'
]; ];
const marketplaceWebExtensionsExclude = new Set([ const marketplaceWebExtensionsExclude = new Set([
@@ -473,7 +475,7 @@ const esbuildMediaScripts = [
'markdown-language-features/esbuild-notebook.js', 'markdown-language-features/esbuild-notebook.js',
'markdown-language-features/esbuild-preview.js', 'markdown-language-features/esbuild-preview.js',
'markdown-math/esbuild.js', 'markdown-math/esbuild.js',
'notebook-renderers/esbuild.js', // 'notebook-renderers/esbuild.js', {{SQL CARBON EDIT}} We don't have this extension
'simple-browser/esbuild-preview.js', 'simple-browser/esbuild-preview.js',
]; ];
async function webpackExtensions(taskName, isWatch, webpackConfigLocations) { async function webpackExtensions(taskName, isWatch, webpackConfigLocations) {

View File

@@ -285,6 +285,7 @@ const excludedExtensions = [
'vscode-test-resolver', 'vscode-test-resolver',
'ms-vscode.node-debug', 'ms-vscode.node-debug',
'ms-vscode.node-debug2', 'ms-vscode.node-debug2',
'vscode-notebook-tests',
'vscode-custom-editor-tests', 'vscode-custom-editor-tests',
'integration-tests', // {{SQL CARBON EDIT}} 'integration-tests', // {{SQL CARBON EDIT}}
]; ];
@@ -305,9 +306,9 @@ const externalExtensions = [
'azuremonitor', 'azuremonitor',
'cms', 'cms',
'dacpac', 'dacpac',
'datavirtualization',
'import', 'import',
'kusto', 'kusto',
'liveshare',
'machine-learning', 'machine-learning',
'profiler', 'profiler',
'query-history', 'query-history',
@@ -328,6 +329,7 @@ export const vscodeExternalExtensions = [
// extensions that require a rebuild since they have native parts // extensions that require a rebuild since they have native parts
const rebuildExtensions = [ const rebuildExtensions = [
'big-data-cluster',
'mssql' 'mssql'
]; ];
@@ -571,7 +573,7 @@ const esbuildMediaScripts = [
'markdown-language-features/esbuild-notebook.js', 'markdown-language-features/esbuild-notebook.js',
'markdown-language-features/esbuild-preview.js', 'markdown-language-features/esbuild-preview.js',
'markdown-math/esbuild.js', 'markdown-math/esbuild.js',
'notebook-renderers/esbuild.js', // 'notebook-renderers/esbuild.js', {{SQL CARBON EDIT}} We don't have this extension
'simple-browser/esbuild-preview.js', 'simple-browser/esbuild-preview.js',
]; ];

View File

@@ -169,43 +169,35 @@ const textFields = {
const VSCODEExtensions = [ const VSCODEExtensions = [
"bat", "bat",
"configuration-editing", "configuration-editing",
"csharp",
"dart",
"docker", "docker",
"fsharp", "git-ui",
"git", "git",
"git-base",
"github",
"github-authentication", "github-authentication",
"html", "github",
"image-preview", "image-preview",
"javascript",
"json",
"json-language-features", "json-language-features",
"julia", "json",
"markdown-basics", "markdown-basics",
"markdown-language-features", "markdown-language-features",
"markdown-math",
"merge-conflict", "merge-conflict",
"microsoft-authentication", "microsoft-authentication",
"notebook-renderers",
"powershell", "powershell",
"python", "python",
"r", "r",
"search-result", "search-result",
"simple-browser",
"sql", "sql",
"theme-abyss", "theme-abyss",
"theme-defaults", "theme-defaults",
"theme-kimbie-dark", "theme-kimbie-dark",
"theme-monokai",
"theme-monokai-dimmed", "theme-monokai-dimmed",
"theme-monokai",
"theme-quietlight", "theme-quietlight",
"theme-red", "theme-red",
"theme-seti", "theme-seti",
"theme-solarized-dark", "theme-solarized-dark",
"theme-solarized-light", "theme-solarized-light",
"theme-tomorrow-night-blue", "theme-tomorrow-night-blue",
"typescript-basics",
"xml", "xml",
"yaml" "yaml"
]; ];

View File

@@ -187,43 +187,35 @@ const textFields = {
const VSCODEExtensions = [ const VSCODEExtensions = [
"bat", "bat",
"configuration-editing", "configuration-editing",
"csharp",
"dart",
"docker", "docker",
"fsharp", "git-ui",
"git", "git",
"git-base",
"github",
"github-authentication", "github-authentication",
"html", "github",
"image-preview", "image-preview",
"javascript",
"json",
"json-language-features", "json-language-features",
"julia", "json",
"markdown-basics", "markdown-basics",
"markdown-language-features", "markdown-language-features",
"markdown-math",
"merge-conflict", "merge-conflict",
"microsoft-authentication", "microsoft-authentication",
"notebook-renderers",
"powershell", "powershell",
"python", "python",
"r", "r",
"search-result", "search-result",
"simple-browser",
"sql", "sql",
"theme-abyss", "theme-abyss",
"theme-defaults", "theme-defaults",
"theme-kimbie-dark", "theme-kimbie-dark",
"theme-monokai",
"theme-monokai-dimmed", "theme-monokai-dimmed",
"theme-monokai",
"theme-quietlight", "theme-quietlight",
"theme-red", "theme-red",
"theme-seti", "theme-seti",
"theme-solarized-dark", "theme-solarized-dark",
"theme-solarized-light", "theme-solarized-light",
"theme-tomorrow-night-blue", "theme-tomorrow-night-blue",
"typescript-basics",
"xml", "xml",
"yaml" "yaml"
]; ];

View File

@@ -65,7 +65,6 @@ exports.referenceGeneratedDepsByArch = {
'libc.so.6(GLIBC_2.8)(64bit)', 'libc.so.6(GLIBC_2.8)(64bit)',
'libc.so.6(GLIBC_2.9)(64bit)', 'libc.so.6(GLIBC_2.9)(64bit)',
'libcairo.so.2()(64bit)', 'libcairo.so.2()(64bit)',
'libcups.so.2()(64bit)',
'libcurl.so.4()(64bit)', 'libcurl.so.4()(64bit)',
'libdbus-1.so.3()(64bit)', 'libdbus-1.so.3()(64bit)',
'libdl.so.2()(64bit)', 'libdl.so.2()(64bit)',
@@ -148,7 +147,6 @@ exports.referenceGeneratedDepsByArch = {
'libc.so.6(GLIBC_2.8)', 'libc.so.6(GLIBC_2.8)',
'libc.so.6(GLIBC_2.9)', 'libc.so.6(GLIBC_2.9)',
'libcairo.so.2', 'libcairo.so.2',
'libcups.so.2',
'libcurl.so.4()(64bit)', 'libcurl.so.4()(64bit)',
'libdbus-1.so.3', 'libdbus-1.so.3',
'libdl.so.2', 'libdl.so.2',
@@ -239,7 +237,6 @@ exports.referenceGeneratedDepsByArch = {
'libc.so.6()(64bit)', 'libc.so.6()(64bit)',
'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.17)(64bit)',
'libcairo.so.2()(64bit)', 'libcairo.so.2()(64bit)',
'libcups.so.2()(64bit)',
'libcurl.so.4()(64bit)', 'libcurl.so.4()(64bit)',
'libdbus-1.so.3()(64bit)', 'libdbus-1.so.3()(64bit)',
'libdbus-1.so.3(LIBDBUS_1_3)(64bit)', 'libdbus-1.so.3(LIBDBUS_1_3)(64bit)',

View File

@@ -65,7 +65,6 @@ export const referenceGeneratedDepsByArch = {
'libc.so.6(GLIBC_2.8)(64bit)', 'libc.so.6(GLIBC_2.8)(64bit)',
'libc.so.6(GLIBC_2.9)(64bit)', 'libc.so.6(GLIBC_2.9)(64bit)',
'libcairo.so.2()(64bit)', 'libcairo.so.2()(64bit)',
'libcups.so.2()(64bit)',
'libcurl.so.4()(64bit)', 'libcurl.so.4()(64bit)',
'libdbus-1.so.3()(64bit)', 'libdbus-1.so.3()(64bit)',
'libdl.so.2()(64bit)', 'libdl.so.2()(64bit)',
@@ -148,7 +147,6 @@ export const referenceGeneratedDepsByArch = {
'libc.so.6(GLIBC_2.8)', 'libc.so.6(GLIBC_2.8)',
'libc.so.6(GLIBC_2.9)', 'libc.so.6(GLIBC_2.9)',
'libcairo.so.2', 'libcairo.so.2',
'libcups.so.2',
'libcurl.so.4()(64bit)', 'libcurl.so.4()(64bit)',
'libdbus-1.so.3', 'libdbus-1.so.3',
'libdl.so.2', 'libdl.so.2',
@@ -239,7 +237,6 @@ export const referenceGeneratedDepsByArch = {
'libc.so.6()(64bit)', 'libc.so.6()(64bit)',
'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.17)(64bit)',
'libcairo.so.2()(64bit)', 'libcairo.so.2()(64bit)',
'libcups.so.2()(64bit)',
'libcurl.so.4()(64bit)', 'libcurl.so.4()(64bit)',
'libdbus-1.so.3()(64bit)', 'libdbus-1.so.3()(64bit)',
'libdbus-1.so.3(LIBDBUS_1_3)(64bit)', 'libdbus-1.so.3(LIBDBUS_1_3)(64bit)',

View File

@@ -17,11 +17,11 @@ exports.dirs = [
'extensions/azurecore', 'extensions/azurecore',
'extensions/azurehybridtoolkit', 'extensions/azurehybridtoolkit',
'extensions/azuremonitor', 'extensions/azuremonitor',
'extensions/big-data-cluster',
'extensions/cms', 'extensions/cms',
'extensions/configuration-editing', 'extensions/configuration-editing',
'extensions/dacpac', 'extensions/dacpac',
'extensions/data-workspace', 'extensions/data-workspace',
'extensions/datavirtualization',
'extensions/git', 'extensions/git',
'extensions/git-base', 'extensions/git-base',
'extensions/github', 'extensions/github',
@@ -29,10 +29,10 @@ exports.dirs = [
'extensions/image-preview', 'extensions/image-preview',
'extensions/import', 'extensions/import',
'extensions/integration-tests', 'extensions/integration-tests',
'extensions/ipynb',
'extensions/json-language-features', 'extensions/json-language-features',
'extensions/json-language-features/server', 'extensions/json-language-features/server',
'extensions/kusto', 'extensions/kusto',
'extensions/liveshare',
'extensions/machine-learning', 'extensions/machine-learning',
'extensions/markdown-language-features', 'extensions/markdown-language-features',
'extensions/markdown-math', 'extensions/markdown-math',
@@ -40,7 +40,6 @@ exports.dirs = [
'extensions/microsoft-authentication', 'extensions/microsoft-authentication',
'extensions/mssql', 'extensions/mssql',
'extensions/notebook', 'extensions/notebook',
'extensions/notebook-renderers',
'extensions/profiler', 'extensions/profiler',
'extensions/python', 'extensions/python',
'extensions/query-history', 'extensions/query-history',

View File

@@ -4,7 +4,7 @@
"private": true, "private": true,
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"node-gyp": "^9.3.1" "node-gyp": "^8.4.1"
}, },
"scripts": { "scripts": {
} }

View File

@@ -2,33 +2,33 @@
# yarn lockfile v1 # yarn lockfile v1
"@gar/promisify@^1.1.3": "@gar/promisify@^1.0.1":
version "1.1.3" version "1.1.2"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==
"@npmcli/fs@^2.1.0": "@npmcli/fs@^1.0.0":
version "2.1.2" version "1.0.0"
resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.0.0.tgz#589612cfad3a6ea0feafcb901d29c63fd52db09f"
integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== integrity sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==
dependencies: dependencies:
"@gar/promisify" "^1.1.3" "@gar/promisify" "^1.0.1"
semver "^7.3.5" semver "^7.3.5"
"@npmcli/move-file@^2.0.0": "@npmcli/move-file@^1.0.1":
version "2.0.1" version "1.1.2"
resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674"
integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==
dependencies: dependencies:
mkdirp "^1.0.4" mkdirp "^1.0.4"
rimraf "^3.0.2" rimraf "^3.0.2"
"@tootallnate/once@2": "@tootallnate/once@1":
version "2.0.0" version "1.1.2"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
abbrev@^1.0.0: abbrev@1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
@@ -40,13 +40,13 @@ agent-base@6, agent-base@^6.0.2:
dependencies: dependencies:
debug "4" debug "4"
agentkeepalive@^4.2.1: agentkeepalive@^4.1.3:
version "4.3.0" version "4.1.4"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b"
integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg== integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==
dependencies: dependencies:
debug "^4.1.0" debug "^4.1.0"
depd "^2.0.0" depd "^1.1.2"
humanize-ms "^1.2.1" humanize-ms "^1.2.1"
aggregate-error@^3.0.0: aggregate-error@^3.0.0:
@@ -88,36 +88,29 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0" balanced-match "^1.0.0"
concat-map "0.0.1" concat-map "0.0.1"
brace-expansion@^2.0.1: cacache@^15.2.0:
version "2.0.1" version "15.3.0"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==
dependencies: dependencies:
balanced-match "^1.0.0" "@npmcli/fs" "^1.0.0"
"@npmcli/move-file" "^1.0.1"
cacache@^16.1.0:
version "16.1.3"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e"
integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==
dependencies:
"@npmcli/fs" "^2.1.0"
"@npmcli/move-file" "^2.0.0"
chownr "^2.0.0" chownr "^2.0.0"
fs-minipass "^2.1.0" fs-minipass "^2.0.0"
glob "^8.0.1" glob "^7.1.4"
infer-owner "^1.0.4" infer-owner "^1.0.4"
lru-cache "^7.7.1" lru-cache "^6.0.0"
minipass "^3.1.6" minipass "^3.1.1"
minipass-collect "^1.0.2" minipass-collect "^1.0.2"
minipass-flush "^1.0.5" minipass-flush "^1.0.5"
minipass-pipeline "^1.2.4" minipass-pipeline "^1.2.2"
mkdirp "^1.0.4" mkdirp "^1.0.3"
p-map "^4.0.0" p-map "^4.0.0"
promise-inflight "^1.0.1" promise-inflight "^1.0.1"
rimraf "^3.0.2" rimraf "^3.0.2"
ssri "^9.0.0" ssri "^8.0.1"
tar "^6.1.11" tar "^6.0.2"
unique-filename "^2.0.0" unique-filename "^1.1.1"
chownr@^2.0.0: chownr@^2.0.0:
version "2.0.0" version "2.0.0"
@@ -137,43 +130,36 @@ color-support@^1.1.2:
concat-map@0.0.1: concat-map@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
console-control-strings@^1.0.0, console-control-strings@^1.1.0: console-control-strings@^1.0.0, console-control-strings@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
debug@4, debug@^4.1.0: debug@4, debug@^4.1.0, debug@^4.3.1:
version "4.3.3" version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies: dependencies:
ms "2.1.2" ms "2.1.2"
debug@^4.3.3:
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"
delegates@^1.0.0: delegates@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
depd@^2.0.0: depd@^1.1.2:
version "2.0.0" version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
emoji-regex@^8.0.0: emoji-regex@^8.0.0:
version "8.0.0" version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
encoding@^0.1.13: encoding@^0.1.12:
version "0.1.13" version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
@@ -190,7 +176,7 @@ err-code@^2.0.2:
resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
fs-minipass@^2.0.0, fs-minipass@^2.1.0: fs-minipass@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
@@ -229,17 +215,6 @@ glob@^7.1.3, glob@^7.1.4:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^8.0.1:
version "8.1.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^5.0.1"
once "^1.3.0"
graceful-fs@^4.2.6: graceful-fs@^4.2.6:
version "4.2.8" version "4.2.8"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
@@ -251,16 +226,16 @@ has-unicode@^2.0.1:
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
http-cache-semantics@^4.1.0: http-cache-semantics@^4.1.0:
version "4.1.1" version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
http-proxy-agent@^5.0.0: http-proxy-agent@^4.0.1:
version "5.0.0" version "4.0.1"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==
dependencies: dependencies:
"@tootallnate/once" "2" "@tootallnate/once" "1"
agent-base "6" agent-base "6"
debug "4" debug "4"
@@ -314,10 +289,10 @@ inherits@2, inherits@^2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ip@^2.0.0: ip@^1.1.5:
version "2.0.0" version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
is-fullwidth-code-point@^3.0.0: is-fullwidth-code-point@^3.0.0:
version "3.0.0" version "3.0.0"
@@ -341,47 +316,35 @@ lru-cache@^6.0.0:
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
lru-cache@^7.7.1: make-fetch-happen@^9.1.0:
version "7.18.3" version "9.1.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968"
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==
make-fetch-happen@^10.0.3:
version "10.2.1"
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164"
integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==
dependencies: dependencies:
agentkeepalive "^4.2.1" agentkeepalive "^4.1.3"
cacache "^16.1.0" cacache "^15.2.0"
http-cache-semantics "^4.1.0" http-cache-semantics "^4.1.0"
http-proxy-agent "^5.0.0" http-proxy-agent "^4.0.1"
https-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0"
is-lambda "^1.0.1" is-lambda "^1.0.1"
lru-cache "^7.7.1" lru-cache "^6.0.0"
minipass "^3.1.6" minipass "^3.1.3"
minipass-collect "^1.0.2" minipass-collect "^1.0.2"
minipass-fetch "^2.0.3" minipass-fetch "^1.3.2"
minipass-flush "^1.0.5" minipass-flush "^1.0.5"
minipass-pipeline "^1.2.4" minipass-pipeline "^1.2.4"
negotiator "^0.6.3" negotiator "^0.6.2"
promise-retry "^2.0.1" promise-retry "^2.0.1"
socks-proxy-agent "^7.0.0" socks-proxy-agent "^6.0.0"
ssri "^9.0.0" ssri "^8.0.0"
minimatch@^3.0.4: minimatch@^3.0.4:
version "3.1.2" version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies: dependencies:
brace-expansion "^1.1.7" brace-expansion "^1.1.7"
minimatch@^5.0.1:
version "5.1.6"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
dependencies:
brace-expansion "^2.0.1"
minipass-collect@^1.0.2: minipass-collect@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
@@ -389,16 +352,16 @@ minipass-collect@^1.0.2:
dependencies: dependencies:
minipass "^3.0.0" minipass "^3.0.0"
minipass-fetch@^2.0.3: minipass-fetch@^1.3.2:
version "2.1.2" version "1.4.1"
resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6"
integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==
dependencies: dependencies:
minipass "^3.1.6" minipass "^3.1.0"
minipass-sized "^1.0.3" minipass-sized "^1.0.3"
minizlib "^2.1.2" minizlib "^2.0.0"
optionalDependencies: optionalDependencies:
encoding "^0.1.13" encoding "^0.1.12"
minipass-flush@^1.0.5: minipass-flush@^1.0.5:
version "1.0.5" version "1.0.5"
@@ -407,7 +370,7 @@ minipass-flush@^1.0.5:
dependencies: dependencies:
minipass "^3.0.0" minipass "^3.0.0"
minipass-pipeline@^1.2.4: minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4:
version "1.2.4" version "1.2.4"
resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c"
integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==
@@ -421,26 +384,14 @@ minipass-sized@^1.0.3:
dependencies: dependencies:
minipass "^3.0.0" minipass "^3.0.0"
minipass@^3.0.0, minipass@^3.1.1: minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3:
version "3.1.5" version "3.1.5"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.5.tgz#71f6251b0a33a49c01b3cf97ff77eda030dff732" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.5.tgz#71f6251b0a33a49c01b3cf97ff77eda030dff732"
integrity sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw== integrity sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
minipass@^3.1.6: minizlib@^2.0.0, minizlib@^2.1.1:
version "3.3.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
minipass@^4.0.0:
version "4.2.5"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb"
integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==
minizlib@^2.1.1, minizlib@^2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
@@ -463,33 +414,33 @@ ms@^2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
negotiator@^0.6.3: negotiator@^0.6.2:
version "0.6.3" version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
node-gyp@^9.3.1: node-gyp@^8.4.1:
version "9.3.1" version "8.4.1"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.3.1.tgz#1e19f5f290afcc9c46973d68700cbd21a96192e4" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937"
integrity sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg== integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==
dependencies: dependencies:
env-paths "^2.2.0" env-paths "^2.2.0"
glob "^7.1.4" glob "^7.1.4"
graceful-fs "^4.2.6" graceful-fs "^4.2.6"
make-fetch-happen "^10.0.3" make-fetch-happen "^9.1.0"
nopt "^6.0.0" nopt "^5.0.0"
npmlog "^6.0.0" npmlog "^6.0.0"
rimraf "^3.0.2" rimraf "^3.0.2"
semver "^7.3.5" semver "^7.3.5"
tar "^6.1.2" tar "^6.1.2"
which "^2.0.2" which "^2.0.2"
nopt@^6.0.0: nopt@^5.0.0:
version "6.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==
dependencies: dependencies:
abbrev "^1.0.0" abbrev "1"
npmlog@^6.0.0: npmlog@^6.0.0:
version "6.0.0" version "6.0.0"
@@ -581,32 +532,32 @@ signal-exit@^3.0.0:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af"
integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==
smart-buffer@^4.2.0: smart-buffer@^4.1.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
socks-proxy-agent@^7.0.0: socks-proxy-agent@^6.0.0:
version "7.0.0" version "6.1.1"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87"
integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==
dependencies: dependencies:
agent-base "^6.0.2" agent-base "^6.0.2"
debug "^4.3.3" debug "^4.3.1"
socks "^2.6.2" socks "^2.6.1"
socks@^2.6.2: socks@^2.6.1:
version "2.7.1" version "2.6.1"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e"
integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==
dependencies: dependencies:
ip "^2.0.0" ip "^1.1.5"
smart-buffer "^4.2.0" smart-buffer "^4.1.0"
ssri@^9.0.0: ssri@^8.0.0, ssri@^8.0.1:
version "9.0.1" version "8.0.1"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==
dependencies: dependencies:
minipass "^3.1.1" minipass "^3.1.1"
@@ -633,19 +584,7 @@ strip-ansi@^6.0.1:
dependencies: dependencies:
ansi-regex "^5.0.1" ansi-regex "^5.0.1"
tar@^6.1.11: tar@^6.0.2, tar@^6.1.2:
version "6.1.13"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b"
integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
minipass "^4.0.0"
minizlib "^2.1.1"
mkdirp "^1.0.3"
yallist "^4.0.0"
tar@^6.1.2:
version "6.1.11" version "6.1.11"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
@@ -657,17 +596,17 @@ tar@^6.1.2:
mkdirp "^1.0.3" mkdirp "^1.0.3"
yallist "^4.0.0" yallist "^4.0.0"
unique-filename@^2.0.0: unique-filename@^1.1.1:
version "2.0.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"
integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==
dependencies: dependencies:
unique-slug "^3.0.0" unique-slug "^2.0.0"
unique-slug@^3.0.0: unique-slug@^2.0.0:
version "3.0.0" version "2.0.2"
resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c"
integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==
dependencies: dependencies:
imurmurhash "^0.1.4" imurmurhash "^0.1.4"

View File

@@ -81,7 +81,6 @@
}, },
"dependencies": {}, "dependencies": {},
"resolutions": { "resolutions": {
"json-schema": "0.4.0", "json-schema": "0.4.0"
"jsonwebtoken": "9.0.0"
} }
} }

View File

@@ -1972,9 +1972,9 @@ htmlparser2@^8.0.1:
entities "^4.3.0" entities "^4.3.0"
http-cache-semantics@^4.0.0: http-cache-semantics@^4.0.0:
version "4.1.1" version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
http-proxy-agent@^5.0.0: http-proxy-agent@^5.0.0:
version "5.0.0" version "5.0.0"
@@ -2199,9 +2199,9 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json5@^2.2.1: json5@^2.2.1:
version "2.2.3" version "2.2.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
jsonc-parser@^2.3.0: jsonc-parser@^2.3.0:
version "2.3.1" version "2.3.1"
@@ -2229,15 +2229,21 @@ jsonparse@~1.2.0:
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.2.0.tgz#5c0c5685107160e72fe7489bddea0b44c2bc67bd" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.2.0.tgz#5c0c5685107160e72fe7489bddea0b44c2bc67bd"
integrity sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70= integrity sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70=
jsonwebtoken@9.0.0, jsonwebtoken@^8.5.1: jsonwebtoken@^8.5.1:
version "9.0.0" version "8.5.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw== integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
dependencies: dependencies:
jws "^3.2.2" jws "^3.2.2"
lodash "^4.17.21" lodash.includes "^4.3.0"
lodash.isboolean "^3.0.3"
lodash.isinteger "^4.0.4"
lodash.isnumber "^3.0.3"
lodash.isplainobject "^4.0.6"
lodash.isstring "^4.0.1"
lodash.once "^4.0.0"
ms "^2.1.1" ms "^2.1.1"
semver "^7.3.8" semver "^5.6.0"
jsprim@^1.2.2: jsprim@^1.2.2:
version "1.4.2" version "1.4.2"
@@ -2317,17 +2323,52 @@ linkify-it@^3.0.1:
dependencies: dependencies:
uc.micro "^1.0.1" uc.micro "^1.0.1"
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==
lodash.isboolean@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==
lodash.isinteger@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==
lodash.isnumber@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==
lodash.isplainobject@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
lodash.isstring@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==
lodash.mergewith@^4.6.1: lodash.mergewith@^4.6.1:
version "4.6.2" version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==
lodash.unescape@4.0.1: lodash.unescape@4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
lodash@^4.17.10, lodash@^4.17.21: lodash@^4.17.10:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -2759,16 +2800,16 @@ punycode@^2.1.0, punycode@^2.1.1:
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@^6.9.1: qs@^6.9.1:
version "6.11.0" version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
dependencies: dependencies:
side-channel "^1.0.4" side-channel "^1.0.4"
qs@~6.5.2: qs@~6.5.2:
version "6.5.3" version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
queue-microtask@^1.2.2: queue-microtask@^1.2.2:
version "1.2.3" version "1.2.3"
@@ -2991,7 +3032,7 @@ semver-compare@^1.0.0:
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
semver@^5.1.0, semver@^5.3.0: semver@^5.1.0, semver@^5.3.0, semver@^5.6.0:
version "5.7.1" version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -3015,13 +3056,6 @@ semver@^7.3.5, semver@^7.3.7:
dependencies: dependencies:
lru-cache "^6.0.0" lru-cache "^6.0.0"
semver@^7.3.8:
version "7.3.8"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
dependencies:
lru-cache "^6.0.0"
serialize-error@^7.0.1: serialize-error@^7.0.1:
version "7.0.1" version "7.0.1"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"

View File

@@ -6,7 +6,7 @@
"git": { "git": {
"name": "chromium", "name": "chromium",
"repositoryUrl": "https://chromium.googlesource.com/chromium/src", "repositoryUrl": "https://chromium.googlesource.com/chromium/src",
"commitHash": "16e28102fdf876ce6d136674ba66343ede07441f" "commitHash": "e2aa76f05f3a6ccadbf43e37f5dfc195cc090b6a"
} }
}, },
"licenseDetail": [ "licenseDetail": [
@@ -40,7 +40,7 @@
"SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
], ],
"isOnlyProductionDependency": true, "isOnlyProductionDependency": true,
"version": "100.0.4894.0" "version": "98.0.4758.141"
}, },
{ {
"component": { "component": {
@@ -48,11 +48,11 @@
"git": { "git": {
"name": "nodejs", "name": "nodejs",
"repositoryUrl": "https://github.com/nodejs/node", "repositoryUrl": "https://github.com/nodejs/node",
"commitHash": "acb71eab779fb56bf70e8a9e0cb2e82a089a87de" "commitHash": "40ecd5601193c316e62e9216e8a4259130686208"
} }
}, },
"isOnlyProductionDependency": true, "isOnlyProductionDependency": true,
"version": "16.13.2" "version": "16.13.0"
}, },
{ {
"component": { "component": {
@@ -60,12 +60,12 @@
"git": { "git": {
"name": "electron", "name": "electron",
"repositoryUrl": "https://github.com/electron/electron", "repositoryUrl": "https://github.com/electron/electron",
"commitHash": "f887fa45dfaeeddfe20c9835ae7ca3a0823b661b" "commitHash": "73c87bcfc6e18428c21676d68f829364e6a7b15d"
} }
}, },
"isOnlyProductionDependency": true, "isOnlyProductionDependency": true,
"license": "MIT", "license": "MIT",
"version": "19.1.8" "version": "17.4.11"
}, },
{ {
"component": { "component": {

View File

@@ -23,7 +23,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
## Privacy Statement ## Privacy Statement
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704). The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
## License ## License

View File

@@ -23,13 +23,9 @@ Both of these are available as menu items on the context menu for nodes in the O
## Code of Conduct ## 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. 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://aka.ms/ads-disable-telemetry) documentation.
## Privacy Statement ## Privacy Statement
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704). The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
## License ## License

View File

@@ -2,8 +2,8 @@
"downloadUrl": "https://sqlopsextensions.blob.core.windows.net/tools/ssmsmin/{#version#}/{#fileName#}", "downloadUrl": "https://sqlopsextensions.blob.core.windows.net/tools/ssmsmin/{#version#}/{#fileName#}",
"version": "16.0.19061.0", "version": "16.0.19061.0",
"downloadFileNames": { "downloadFileNames": {
"Windows_86": "SsmsMin-16.0.19061.0-win-x86.zip", "Windows_64": "SsmsMin-16.0.19061.0-win-x64.zip",
"Windows": "SsmsMin-16.0.19061.0-win-x64.zip" "Windows_86": "SsmsMin-16.0.19061.0-win-x86.zip"
}, },
"installDirectory": "ssmsmin/{#platform#}/{#version#}", "installDirectory": "ssmsmin/{#platform#}/{#version#}",
"executableFiles": [ "executableFiles": [

View File

@@ -2,7 +2,7 @@
"name": "admin-tool-ext-win", "name": "admin-tool-ext-win",
"displayName": "%adminToolExtWin.displayName%", "displayName": "%adminToolExtWin.displayName%",
"description": "%adminToolExtWin.description%", "description": "%adminToolExtWin.description%",
"version": "0.1.2", "version": "0.1.1",
"publisher": "Microsoft", "publisher": "Microsoft",
"preview": true, "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", "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": { "dependencies": {
"@microsoft/ads-extension-telemetry": "^3.0.1", "@microsoft/ads-extension-telemetry": "^1.3.1",
"@microsoft/ads-service-downloader": "^1.2.1", "@microsoft/ads-service-downloader": "1.0.4",
"vscode-nls": "^4.1.2" "vscode-nls": "^4.1.2"
}, },
"devDependencies": { "devDependencies": {
@@ -117,7 +117,7 @@
"mocha": "^7.1.1", "mocha": "^7.1.1",
"should": "^13.2.3", "should": "^13.2.3",
"@microsoft/vscodetestcover": "^1.2.1", "@microsoft/vscodetestcover": "^1.2.1",
"@microsoft/azdata-test": "^3.0.1" "@microsoft/azdata-test": "^2.0.3"
}, },
"__metadata": { "__metadata": {
"id": "41", "id": "41",

View File

@@ -24,7 +24,6 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
const ssmsMinVer = JSON.parse(rawConfig.toString()).version; const ssmsMinVer = JSON.parse(rawConfig.toString()).version;
exePath = path.join(context.extensionPath, 'ssmsmin', 'Windows', ssmsMinVer, 'ssmsmin.exe'); exePath = path.join(context.extensionPath, 'ssmsmin', 'Windows', ssmsMinVer, 'ssmsmin.exe');
registerCommands(context); registerCommands(context);
context.subscriptions.push(TelemetryReporter);
} }
} }
@@ -52,7 +51,7 @@ function registerCommands(context: vscode.ExtensionContext): void {
*/ */
async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> { async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> {
if (!connectionContext) { if (!connectionContext) {
TelemetryReporter.sendErrorEvent2(TelemetryViews.SsmsMinProperties, 'NoConnectionContext'); TelemetryReporter.sendErrorEvent(TelemetryViews.SsmsMinProperties, 'NoConnectionContext');
void vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForProp', "No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand")); void vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForProp', "No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand"));
return; return;
} }
@@ -64,7 +63,7 @@ async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: az
else if (connectionContext.nodeInfo) { else if (connectionContext.nodeInfo) {
nodeType = connectionContext.nodeInfo.nodeType; nodeType = connectionContext.nodeInfo.nodeType;
} else { } else {
TelemetryReporter.sendErrorEvent2(TelemetryViews.SsmsMinProperties, 'NoOENode'); TelemetryReporter.sendErrorEvent(TelemetryViews.SsmsMinProperties, 'NoOENode');
void vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', "Could not determine Object Explorer node from connectionContext : {0}", JSON.stringify(connectionContext))); void vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', "Could not determine Object Explorer node from connectionContext : {0}", JSON.stringify(connectionContext)));
return; return;
} }
@@ -81,7 +80,7 @@ async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: az
async function handleLaunchSsmsMinGswDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> { async function handleLaunchSsmsMinGswDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> {
const action = 'GenerateScripts'; const action = 'GenerateScripts';
if (!connectionContext) { if (!connectionContext) {
TelemetryReporter.sendErrorEvent2(TelemetryViews.SsmsMinGsw, 'NoConnectionContext'); TelemetryReporter.sendErrorEvent(TelemetryViews.SsmsMinGsw, 'NoConnectionContext');
void vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForGsw', "No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand")); void vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForGsw', "No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand"));
return; return;
} }
@@ -98,7 +97,7 @@ async function handleLaunchSsmsMinGswDialogCommand(connectionContext?: azdata.Ob
*/ */
async function launchSsmsDialog(action: string, connectionContext: azdata.ObjectExplorerContext): Promise<void> { async function launchSsmsDialog(action: string, connectionContext: azdata.ObjectExplorerContext): Promise<void> {
if (!connectionContext.connectionProfile) { if (!connectionContext.connectionProfile) {
TelemetryReporter.sendErrorEvent2(TelemetryViews.SsmsMinDialog, 'NoConnectionProfile'); TelemetryReporter.sendErrorEvent(TelemetryViews.SsmsMinDialog, 'NoConnectionProfile');
void vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionProfile', "No connectionProfile provided from connectionContext : {0}", JSON.stringify(connectionContext))); void vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionProfile', "No connectionProfile provided from connectionContext : {0}", JSON.stringify(connectionContext)));
return; return;
} }
@@ -112,7 +111,7 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object
oeNode = await azdata.objectexplorer.getNode(connectionContext.connectionProfile.id, connectionContext.nodeInfo.nodePath); oeNode = await azdata.objectexplorer.getNode(connectionContext.connectionProfile.id, connectionContext.nodeInfo.nodePath);
} }
else { else {
TelemetryReporter.sendErrorEvent2(TelemetryViews.SsmsMinDialog, 'NoOENode'); TelemetryReporter.sendErrorEvent(TelemetryViews.SsmsMinDialog, 'NoOENode');
void vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', "Could not determine Object Explorer node from connectionContext : {0}", JSON.stringify(connectionContext))); void vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', "Could not determine Object Explorer node from connectionContext : {0}", JSON.stringify(connectionContext)));
return; return;
} }
@@ -156,11 +155,9 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object
runningProcesses.delete(proc.pid); runningProcesses.delete(proc.pid);
const err = stderr.toString(); const err = stderr.toString();
if ((execException?.code !== 0) || err !== '') { if ((execException?.code !== 0) || err !== '') {
TelemetryReporter.sendErrorEvent2( TelemetryReporter.sendErrorEvent(
TelemetryViews.SsmsMinDialog, TelemetryViews.SsmsMinDialog,
'LaunchSsmsDialogError', 'LaunchSsmsDialogError',
execException,
false,
execException ? execException?.code?.toString() : '', execException ? execException?.code?.toString() : '',
getTelemetryErrorType(err)); getTelemetryErrorType(err));
} }

View File

@@ -16,7 +16,6 @@ export class ExtHostObjectExplorerNodeStub implements azdata.objectexplorer.Obje
// Base properties // Base properties
public connectionId: string; public connectionId: string;
public nodePath: string; public nodePath: string;
public parentNodePath: string;
public nodeType: string; public nodeType: string;
public nodeSubType: string; public nodeSubType: string;
public nodeStatus: string; public nodeStatus: string;

View File

@@ -182,39 +182,75 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
"@microsoft/ads-extension-telemetry@^3.0.1": "@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3":
version "3.0.1" version "3.2.3"
resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-3.0.1.tgz#82d4288f8ad96920512ab4edf87d009412e683bb" resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7"
integrity sha512-OsHLKvz4DEZg9THkI+Jk4aINgYv4X3qgbqnVAqY8f9zsWTiR+o4vt/F5w0w3kwpbU3ppRHa03yFTBEjYyGSg1g== integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g==
dependencies: dependencies:
"@vscode/extension-telemetry" "0.6.1" "@microsoft/applicationinsights-core-js" "2.8.4"
"@microsoft/applicationinsights-shims" "^2.0.1"
"@microsoft/dynamicproto-js" "^1.1.6"
"@microsoft/ads-service-downloader@^1.2.1": "@microsoft/1ds-post-js@^3.2.3":
version "1.2.1" version "3.2.3"
resolved "https://registry.yarnpkg.com/@microsoft/ads-service-downloader/-/ads-service-downloader-1.2.1.tgz#b0216cb0486db6697ccf9e166ec81a9764bdb3aa" resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216"
integrity sha512-xB3VUaEYauXtm3zFko5clHnhF7l7QbX0AnnULGDrd2JANu1zThBR6toUQ9+zAMO+0KCHs71XLSuoP2A24G3WCw== 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.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-1.3.1.tgz#fa757ee88eac91b21c3a68562da6441c2ad15c39"
integrity sha512-8Zd7RwwN7ZufMoWFmc1bwzmQc1RV7/jf/Ua33YL1+P0ZwHoWFOhf/b0lwvAVzi9TB/7oD5zA5yv7A/i2sSTn6Q==
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==
dependencies: dependencies:
async-retry "^1.2.3" async-retry "^1.2.3"
eventemitter2 "^5.0.1" eventemitter2 "^5.0.1"
http-proxy-agent "^5.0.0" http-proxy-agent "^2.1.0"
https-proxy-agent "^5.0.1" https-proxy-agent "^2.2.3"
mkdirp "1.0.4" mkdirp "1.0.4"
tar "^6.1.11" tar "^6.1.11"
tmp "^0.0.33" tmp "^0.0.33"
yauzl "^2.10.0" yauzl "^2.10.0"
"@microsoft/azdata-test@^3.0.1": "@microsoft/applicationinsights-core-js@2.8.4":
version "3.0.1" version "2.8.4"
resolved "https://registry.yarnpkg.com/@microsoft/azdata-test/-/azdata-test-3.0.1.tgz#a8b89a12de42f277d33aae71c277d0c8efcfbee0" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596"
integrity sha512-Zrctm/zKufwIRF9jfw8TOBzr5woLdKXAGNTlbAQl0IGLzVoIGULj9Gqdc1Ikhrov3rM0NkbAF/PY6j6BHiW8Tw== integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ==
dependencies: dependencies:
http-proxy-agent "^5.0.0" "@microsoft/applicationinsights-shims" "2.0.1"
https-proxy-agent "^5.0.0" "@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"
integrity sha512-BgB6gGjQVXxnZHq7o5TlajZR/mJd/6AqbclrGzoyATvCEt92jRXhPzaY6XA/jMahdUGFSQwXpm45qRhZetwDig==
dependencies:
http-proxy-agent "^2.1.0"
https-proxy-agent "^2.2.4"
mocha-junit-reporter "^1.17.0" mocha-junit-reporter "^1.17.0"
mocha-multi-reporters "^1.1.7" mocha-multi-reporters "^1.1.7"
rimraf "^2.6.3" rimraf "^2.6.3"
typemoq "^2.1.0" 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": "@microsoft/vscodetestcover@^1.2.1":
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/@microsoft/vscodetestcover/-/vscodetestcover-1.2.1.tgz#65f25132075a465a7a99688204486ee2b65ac07b" resolved "https://registry.yarnpkg.com/@microsoft/vscodetestcover/-/vscodetestcover-1.2.1.tgz#65f25132075a465a7a99688204486ee2b65ac07b"
@@ -230,11 +266,6 @@
istanbul-reports "^3.0.0" istanbul-reports "^3.0.0"
mocha "^7.1.1" 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": "@types/mocha@^7.0.2":
version "7.0.2" version "7.0.2"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce"
@@ -245,17 +276,20 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.7.tgz#01e4ea724d9e3bd50d90c11fd5980ba317d8fa11" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.7.tgz#01e4ea724d9e3bd50d90c11fd5980ba317d8fa11"
integrity sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w== integrity sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w==
"@vscode/extension-telemetry@0.6.1": "@vscode/extension-telemetry@^0.6.2":
version "0.6.1" version "0.6.2"
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.1.tgz#f8d1f7145baf932b75077c48107edff48501fc14" resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e"
integrity sha512-Y4Oc8yGURGVF4WhCZcu+EVy+MAIeQDLDVeDlLn59H0C1w+7xr8dL2ZtDBioy+Hog1Edrd6zOwr3Na7xe1iC/UA== integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w==
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: dependencies:
debug "4" "@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"
ansi-colors@3.2.3: ansi-colors@3.2.3:
version "3.2.3" version "3.2.3"
@@ -438,6 +472,13 @@ crypt@~0.0.1:
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
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.2.6, debug@^3.1.0: debug@3.2.6, debug@^3.1.0:
version "3.2.6" version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -445,13 +486,6 @@ debug@3.2.6, debug@^3.1.0:
dependencies: dependencies:
ms "^2.1.1" 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: debug@^2.2.0:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -541,6 +575,18 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1" is-date-object "^1.0.1"
is-symbol "^1.0.2" is-symbol "^1.0.2"
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"
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -752,22 +798,21 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
http-proxy-agent@^5.0.0: http-proxy-agent@^2.1.0:
version "5.0.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
dependencies: dependencies:
"@tootallnate/once" "2" agent-base "4"
agent-base "6" debug "3.1.0"
debug "4"
https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: https-proxy-agent@^2.2.3, https-proxy-agent@^2.2.4:
version "5.0.1" version "2.2.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
dependencies: dependencies:
agent-base "6" agent-base "^4.3.0"
debug "4" debug "^3.1.0"
inflight@^1.0.4: inflight@^1.0.4:
version "1.0.6" version "1.0.6"
@@ -987,9 +1032,11 @@ jsesc@^2.5.1:
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
json5@^2.1.2: json5@^2.1.2:
version "2.2.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
dependencies:
minimist "^1.2.5"
locate-path@^3.0.0: locate-path@^3.0.0:
version "3.0.0" version "3.0.0"
@@ -1133,7 +1180,7 @@ ms@2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
ms@2.1.2, ms@^2.1.1: ms@^2.1.1:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==

View File

@@ -17,7 +17,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
## Privacy Statement ## Privacy Statement
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704). The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
## License ## License

View File

@@ -94,7 +94,7 @@
"should": "^13.2.1", "should": "^13.2.1",
"typemoq": "^2.1.0", "typemoq": "^2.1.0",
"@microsoft/vscodetestcover": "^1.2.1", "@microsoft/vscodetestcover": "^1.2.1",
"@microsoft/azdata-test": "^3.0.1" "@microsoft/azdata-test": "^2.0.3"
}, },
"__metadata": { "__metadata": {
"id": "10", "id": "10",

View File

@@ -182,13 +182,13 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
"@microsoft/azdata-test@^3.0.1": "@microsoft/azdata-test@^2.0.3":
version "3.0.1" version "2.0.3"
resolved "https://registry.yarnpkg.com/@microsoft/azdata-test/-/azdata-test-3.0.1.tgz#a8b89a12de42f277d33aae71c277d0c8efcfbee0" resolved "https://registry.yarnpkg.com/@microsoft/azdata-test/-/azdata-test-2.0.3.tgz#652984efa2f5adc56cdae9029a4d5f33446b54d3"
integrity sha512-Zrctm/zKufwIRF9jfw8TOBzr5woLdKXAGNTlbAQl0IGLzVoIGULj9Gqdc1Ikhrov3rM0NkbAF/PY6j6BHiW8Tw== integrity sha512-BgB6gGjQVXxnZHq7o5TlajZR/mJd/6AqbclrGzoyATvCEt92jRXhPzaY6XA/jMahdUGFSQwXpm45qRhZetwDig==
dependencies: dependencies:
http-proxy-agent "^5.0.0" http-proxy-agent "^2.1.0"
https-proxy-agent "^5.0.0" https-proxy-agent "^2.2.4"
mocha-junit-reporter "^1.17.0" mocha-junit-reporter "^1.17.0"
mocha-multi-reporters "^1.1.7" mocha-multi-reporters "^1.1.7"
rimraf "^2.6.3" rimraf "^2.6.3"
@@ -209,11 +209,6 @@
istanbul-reports "^3.0.0" istanbul-reports "^3.0.0"
mocha "^7.1.1" 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": "@types/mocha@^7.0.2":
version "7.0.2" version "7.0.2"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce"
@@ -224,12 +219,12 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.7.tgz#01e4ea724d9e3bd50d90c11fd5980ba317d8fa11" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.7.tgz#01e4ea724d9e3bd50d90c11fd5980ba317d8fa11"
integrity sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w== integrity sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w==
agent-base@6: agent-base@4, agent-base@^4.3.0:
version "6.0.2" version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
dependencies: dependencies:
debug "4" es6-promisify "^5.0.0"
ansi-colors@3.2.3: ansi-colors@3.2.3:
version "3.2.3" version "3.2.3"
@@ -395,6 +390,13 @@ crypt@~0.0.1:
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
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.2.6, debug@^3.1.0: debug@3.2.6, debug@^3.1.0:
version "3.2.6" version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -402,13 +404,6 @@ debug@3.2.6, debug@^3.1.0:
dependencies: dependencies:
ms "^2.1.1" 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: debug@^2.2.0:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -498,6 +493,18 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1" is-date-object "^1.0.1"
is-symbol "^1.0.2" is-symbol "^1.0.2"
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 sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==
dependencies:
es6-promise "^4.0.3"
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -690,22 +697,21 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
http-proxy-agent@^5.0.0: http-proxy-agent@^2.1.0:
version "5.0.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
dependencies: dependencies:
"@tootallnate/once" "2" agent-base "4"
agent-base "6" debug "3.1.0"
debug "4"
https-proxy-agent@^5.0.0: https-proxy-agent@^2.2.4:
version "5.0.1" version "2.2.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
dependencies: dependencies:
agent-base "6" agent-base "^4.3.0"
debug "4" debug "^3.1.0"
inflight@^1.0.4: inflight@^1.0.4:
version "1.0.6" version "1.0.6"
@@ -925,9 +931,11 @@ jsesc@^2.5.1:
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
json5@^2.1.2: json5@^2.1.2:
version "2.2.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
dependencies:
minimist "^1.2.5"
locate-path@^3.0.0: locate-path@^3.0.0:
version "3.0.0" version "3.0.0"
@@ -1051,11 +1059,6 @@ ms@2.1.1, ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
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==
node-environment-flags@1.0.6: node-environment-flags@1.0.6:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088"

View File

@@ -42,7 +42,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
## Privacy Statement ## Privacy Statement
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704). The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
## License ## License

View File

@@ -2,7 +2,7 @@
"name": "arc", "name": "arc",
"displayName": "%arc.displayName%", "displayName": "%arc.displayName%",
"description": "%arc.description%", "description": "%arc.description%",
"version": "1.8.0", "version": "1.7.0",
"publisher": "Microsoft", "publisher": "Microsoft",
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt", "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
"icon": "images/extension.png", "icon": "images/extension.png",
@@ -54,7 +54,7 @@
{ {
"command": "arc.connectToController", "command": "arc.connectToController",
"title": "%command.connectToController.title%", "title": "%command.connectToController.title%",
"icon": "$(plug)" "icon": "$(disconnect)"
}, },
{ {
"command": "arc.removeController", "command": "arc.removeController",
@@ -1599,7 +1599,7 @@
"@types/sinon": "^9.0.4", "@types/sinon": "^9.0.4",
"@types/uuid": "^8.3.0", "@types/uuid": "^8.3.0",
"@types/yamljs": "^0.2.31", "@types/yamljs": "^0.2.31",
"@microsoft/azdata-test": "^3.0.1", "@microsoft/azdata-test": "^2.0.3",
"mocha": "^7.1.1", "mocha": "^7.1.1",
"should": "^13.2.3", "should": "^13.2.3",
"sinon": "^9.0.2", "sinon": "^9.0.2",

View File

@@ -190,7 +190,3 @@ export namespace cssStyles {
} }
export const iconSize = '20px'; 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';

View File

@@ -142,21 +142,12 @@ export const controllerPassword = localize('arc.controllerPassword', "Controller
export const username = localize('arc.username', "Username"); export const username = localize('arc.username', "Username");
export const password = localize('arc.password', "Password"); export const password = localize('arc.password', "Password");
export const rememberPassword = localize('arc.rememberPassword', "Remember 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 connect = localize('arc.connect', "Connect");
export const readMore = localize('arc.readMore', "Read more");
export const cancel = localize('arc.cancel', "Cancel"); export const cancel = localize('arc.cancel', "Cancel");
export const apply = localize('arc.apply', "Apply"); export const apply = localize('arc.apply', "Apply");
export const ok = localize('arc.ok', "Ok"); export const ok = localize('arc.ok', "Ok");
export const on = localize('arc.on', "On"); export const on = localize('arc.on', "On");
export const off = localize('arc.off', "Off"); 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"); export const notConfigured = localize('arc.notConfigured', "Not Configured");
// Database States - see https://docs.microsoft.com/sql/relational-databases/databases/database-states // Database States - see https://docs.microsoft.com/sql/relational-databases/databases/database-states

View File

@@ -219,10 +219,7 @@ export class MiaaModel extends ResourceModel {
saveProfile: true, saveProfile: true,
id: '', id: '',
groupId: undefined, groupId: undefined,
options: { options: {}
encrypt: this._miaaInfo.encrypt || true,
trustServerCertificate: this._miaaInfo.trustServerCertificate || false
}
}; };
} }
@@ -243,8 +240,6 @@ export class MiaaModel extends ResourceModel {
this._activeConnectionId = connectionProfile.id; this._activeConnectionId = connectionProfile.id;
this.info.connectionId = connectionProfile.id; this.info.connectionId = connectionProfile.id;
this._miaaInfo.userName = connectionProfile.userName; this._miaaInfo.userName = connectionProfile.userName;
this._miaaInfo.encrypt = connectionProfile.options.encrypt;
this._miaaInfo.trustServerCertificate = connectionProfile.options.trustServerCertificate;
await this._treeDataProvider.saveControllers(); await this._treeDataProvider.saveControllers();
} }
@@ -275,5 +270,6 @@ export class MiaaModel extends ResourceModel {
this._databaseTimeWindow.set(dbName, ['', '']); this._databaseTimeWindow.set(dbName, ['', '']);
} }
} }
} }
} }

View File

@@ -3,16 +3,16 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
// import { ControllerInfo } from 'arc'; import { ControllerInfo } from 'arc';
// import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
// import { ControllerModel } from '../../models/controllerModel'; import { ControllerModel } from '../../models/controllerModel';
// import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider'; import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider';
// export class FakeControllerModel extends ControllerModel { export class FakeControllerModel extends ControllerModel {
// constructor(treeDataProvider?: AzureArcTreeDataProvider, info?: Partial<ControllerInfo>) { constructor(treeDataProvider?: AzureArcTreeDataProvider, info?: Partial<ControllerInfo>) {
// const _info: ControllerInfo = Object.assign({ id: uuid(), endpoint: '', kubeConfigFilePath: '', kubeClusterContext: '', name: '', namespace: '', username: '', rememberPassword: false, resources: [], resourceGroup: '', connectionMode: '', location: '', customLocation: '' }, info); const _info: ControllerInfo = Object.assign({ id: uuid(), endpoint: '', kubeConfigFilePath: '', kubeClusterContext: '', name: '', namespace: '', username: '', rememberPassword: false, resources: [], resourceGroup: '', connectionMode: '', location: '', customLocation: '' }, info);
// super(treeDataProvider!, _info); super(treeDataProvider!, _info);
// } }
// } }

View File

@@ -10,10 +10,10 @@ import * as vscode from 'vscode';
import { Deferred } from '../../../common/promise'; import { Deferred } from '../../../common/promise';
import { FilePicker } from '../../../ui/components/filePicker'; import { FilePicker } from '../../../ui/components/filePicker';
import { createModelViewMock } from '@microsoft/azdata-test/out/mocks/azdata/modelView'; import { createModelViewMock } from '@microsoft/azdata-test/out/mocks/azdata/modelView';
import { StubButton } from '@microsoft/azdata-test/out/stubs/azdata/modelView'; import { StubButton } from '@microsoft/azdata-test/out/stubs/modelView/stubButton';
let filePicker: FilePicker; let filePicker: FilePicker;
const initialPath = path.join('path', 'to', '.kube', 'config'); const initialPath = path.join('path', 'to', '.kube','config');
const newFileUri = vscode.Uri.file(path.join('path', 'to', 'new', '.kube', 'config')); const newFileUri = vscode.Uri.file(path.join('path', 'to', 'new', '.kube', 'config'));
describe('filePicker', function (): void { describe('filePicker', function (): void {
beforeEach(async () => { beforeEach(async () => {

View File

@@ -7,7 +7,7 @@ import * as azdata from 'azdata';
import * as should from 'should'; import * as should from 'should';
import { RadioOptionsGroup, RadioOptionsInfo } from '../../../ui/components/radioOptionsGroup'; import { RadioOptionsGroup, RadioOptionsInfo } from '../../../ui/components/radioOptionsGroup';
import { createModelViewMock } from '@microsoft/azdata-test/out/mocks/azdata/modelView'; import { createModelViewMock } from '@microsoft/azdata-test/out/mocks/azdata/modelView';
import { StubRadioButton } from '@microsoft/azdata-test/out/stubs/azdata/modelView'; import { StubRadioButton } from '@microsoft/azdata-test/out/stubs/modelView/stubRadioButton';
import * as loc from '../../../localizedConstants'; import * as loc from '../../../localizedConstants';

View File

@@ -20,9 +20,7 @@ declare module 'arc' {
} }
export type MiaaResourceInfo = ResourceInfo & { export type MiaaResourceInfo = ResourceInfo & {
userName?: string, userName?: string
encrypt?: string,
trustServerCertificate?: boolean
}; };
export type PGResourceInfo = ResourceInfo & { export type PGResourceInfo = ResourceInfo & {

View File

@@ -0,0 +1,66 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as azdata from 'azdata';
import * as loc from '../../../localizedConstants';
import { IconPathHelper, cssStyles } from '../../../constants';
import { DashboardPage } from '../../components/dashboardPage';
import { PostgresModel } from '../../../models/postgresModel';
import { ControllerModel } from '../../../models/controllerModel';
export class PostgresDiagnoseAndSolveProblemsPage extends DashboardPage {
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _context: vscode.ExtensionContext, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
super(modelView, dashboard);
}
protected get title(): string {
return loc.diagnoseAndSolveProblems;
}
protected get id(): string {
return 'postgres-diagnose-and-solve-problems';
}
protected get icon(): { dark: string; light: string; } {
return IconPathHelper.wrench;
}
protected get container(): azdata.Component {
const root = this.modelView.modelBuilder.divContainer().component();
const content = this.modelView.modelBuilder.divContainer().component();
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
content.addItem(this.modelView.modelBuilder.text().withProps({
value: loc.diagnoseAndSolveProblems,
CSSStyles: { ...cssStyles.title, 'margin-bottom': '20px' }
}).component());
content.addItem(this.modelView.modelBuilder.text().withProps({
value: loc.clickTheTroubleshootButton('Postgres'),
CSSStyles: { ...cssStyles.text, 'margin-bottom': '20px' }
}).component());
const troubleshootButton = this.modelView.modelBuilder.button().withProps({
iconPath: IconPathHelper.wrench,
label: loc.troubleshoot,
width: '160px'
}).component();
this.disposables.push(
troubleshootButton.onDidClick(() => {
process.env['POSTGRES_SERVER_NAMESPACE'] = this._controllerModel.controllerConfig?.metadata.namespace ?? '';
process.env['POSTGRES_SERVER_NAME'] = this._postgresModel.info.name;
vscode.commands.executeCommand('bookTreeView.openBook', this._context.asAbsolutePath('notebooks/arcDataServices'), true, 'postgres/tsg100-troubleshoot-postgres');
}));
content.addItem(troubleshootButton);
return root;
}
protected get toolbarContainer(): azdata.ToolbarContainer {
return this.modelView.modelBuilder.toolbarContainer().component();
}
}

View File

@@ -0,0 +1,119 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as azdata from 'azdata';
import * as loc from '../../../localizedConstants';
import { IconPathHelper, cssStyles } from '../../../constants';
import { KeyValueContainer, KeyValue, InputKeyValue, TextKeyValue, LinkKeyValue } from '../../components/keyValueContainer';
import { DashboardPage } from '../../components/dashboardPage';
import { ControllerModel } from '../../../models/controllerModel';
import { PostgresModel } from '../../../models/postgresModel';
import { ControllerDashboard } from '../controller/controllerDashboard';
export class PostgresPropertiesPage extends DashboardPage {
private loading?: azdata.LoadingComponent;
private keyValueContainer?: KeyValueContainer;
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
super(modelView, dashboard);
this.disposables.push(this._postgresModel.onConfigUpdated(
() => this.eventuallyRunOnInitialized(() => this.handleServiceUpdated())));
this.disposables.push(this._controllerModel.onRegistrationsUpdated(
() => this.eventuallyRunOnInitialized(() => this.handleRegistrationsUpdated())));
}
protected get title(): string {
return loc.properties;
}
protected get id(): string {
return 'postgres-properties';
}
protected get icon(): { dark: string; light: string; } {
return IconPathHelper.properties;
}
protected get container(): azdata.Component {
const root = this.modelView.modelBuilder.divContainer().component();
const content = this.modelView.modelBuilder.divContainer().component();
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
content.addItem(this.modelView.modelBuilder.text().withProps({
value: loc.properties,
CSSStyles: { ...cssStyles.title, 'margin-bottom': '25px' }
}).component());
this.keyValueContainer = new KeyValueContainer(this.modelView.modelBuilder, this.getProperties());
this.keyValueContainer.container.updateCssStyles({ 'max-width': '750px' });
this.disposables.push(this.keyValueContainer);
this.loading = this.modelView.modelBuilder.loadingComponent()
.withItem(this.keyValueContainer.container)
.withProps({
loading: !this._postgresModel.configLastUpdated && !this._controllerModel.registrationsLastUpdated
}).component();
content.addItem(this.loading);
this.initialized = true;
return root;
}
protected get toolbarContainer(): azdata.ToolbarContainer {
const refreshButton = this.modelView.modelBuilder.button().withProps({
label: loc.refresh,
iconPath: IconPathHelper.refresh
}).component();
this.disposables.push(
refreshButton.onDidClick(async () => {
refreshButton.enabled = false;
try {
this.loading!.loading = true;
await Promise.all([
this._postgresModel.refresh(),
this._controllerModel.refresh(false, this._controllerModel.info.namespace)
]);
} catch (error) {
vscode.window.showErrorMessage(loc.refreshFailed(error));
}
finally {
refreshButton.enabled = true;
}
}));
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
{ component: refreshButton }
]).component();
}
private getProperties(): KeyValue[] {
const endpoint = this._postgresModel.endpoint;
const status = this._postgresModel.config?.status;
const controllerDashboard = new ControllerDashboard(this._controllerModel);
return [
new InputKeyValue(this.modelView.modelBuilder, loc.coordinatorEndpoint, endpoint ? `postgresql://postgres@${endpoint.ip}:${endpoint.port}` : ''),
new InputKeyValue(this.modelView.modelBuilder, loc.postgresAdminUsername, 'postgres'),
new InputKeyValue(this.modelView.modelBuilder, loc.subscriptionId, this._controllerModel.controllerConfig?.spec.settings.azure.subscription ?? ''),
new TextKeyValue(this.modelView.modelBuilder, loc.resourceGroup, this._controllerModel.controllerConfig?.spec.settings.azure.resourceGroup ?? ''),
new LinkKeyValue(this.modelView.modelBuilder, loc.dataController, this._controllerModel.controllerConfig?.metadata.name ?? '', () => controllerDashboard.showDashboard()),
new TextKeyValue(this.modelView.modelBuilder, loc.status, status ? `${status.state} (${status.readyPods} ${loc.podsReady})` : loc.unknown)
];
}
private handleRegistrationsUpdated() {
this.keyValueContainer?.refresh(this.getProperties());
this.loading!.loading = false;
}
private handleServiceUpdated() {
this.keyValueContainer?.refresh(this.getProperties());
this.loading!.loading = false;
}
}

View File

@@ -0,0 +1,348 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as azdata from 'azdata';
import * as loc from '../../../localizedConstants';
import { IconPathHelper, cssStyles, iconSize } from '../../../constants';
import { DashboardPage } from '../../components/dashboardPage';
import { PostgresModel } from '../../../models/postgresModel';
export type PodHealthModel = {
condition: string,
details: azdata.Component,
lastUpdate: string
};
export enum PodConditionType {
initialized = 'Initialized',
ready = 'Ready',
containersReady = 'ContainersReady',
podScheduled = 'PodScheduled'
}
export class PostgresResourceHealthPage extends DashboardPage {
private podSummaryContainer!: azdata.DivContainer;
private podConditionsContainer!: azdata.DivContainer;
private podConditionsLoading!: azdata.LoadingComponent;
private podConditionsTable!: azdata.DeclarativeTableComponent;
private podConditionsTableIndexes: Map<string, number[]> = new Map();
private podDropDown!: azdata.DropDownComponent;
private coordinatorPodName!: string;
private coordinatorData: PodHealthModel[] = [];
private podsData: PodHealthModel[] = [];
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
super(modelView, dashboard);
this.disposables.push(
this._postgresModel.onConfigUpdated(() => this.eventuallyRunOnInitialized(() => this.handleConfigUpdated())));
}
protected get title(): string {
return loc.resourceHealth;
}
protected get id(): string {
return 'postgres-resource-health';
}
protected get icon(): { dark: string; light: string; } {
return IconPathHelper.health;
}
protected get container(): azdata.Component {
const root = this.modelView.modelBuilder.divContainer().component();
const content = this.modelView.modelBuilder.divContainer().component();
root.addItem(content, { CSSStyles: { 'margin': '10px 20px 0px 20px' } });
content.addItem(this.modelView.modelBuilder.text().withProps({
value: loc.resourceHealth,
CSSStyles: { ...cssStyles.title }
}).component());
content.addItem(this.modelView.modelBuilder.text().withProps({
value: loc.resourceHealthDescription,
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
}).component());
this.podSummaryContainer = this.modelView.modelBuilder.divContainer().component();
this.refreshPodSummarySection();
content.addItem(this.podSummaryContainer);
// Pod Conditions
content.addItem(this.modelView.modelBuilder.text().withProps({
value: loc.podsPresent,
CSSStyles: { ...cssStyles.title }
}).component());
content.addItem(this.modelView.modelBuilder.text().withProps({
value: loc.podsUsedDescription,
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'margin-top': '10px' }
}).component());
this.podConditionsContainer = this.modelView.modelBuilder.divContainer().component();
this.podConditionsTable = this.modelView.modelBuilder.declarativeTable().withProps({
width: '100%',
ariaLabel: loc.podConditionsTable,
columns: [
{
displayName: loc.condition,
valueType: azdata.DeclarativeDataType.string,
isReadOnly: true,
width: '20%',
headerCssStyles: cssStyles.tableHeader,
rowCssStyles: cssStyles.tableRow
},
{
displayName: loc.details,
valueType: azdata.DeclarativeDataType.component,
isReadOnly: true,
width: '50%',
headerCssStyles: cssStyles.tableHeader,
rowCssStyles: {
...cssStyles.tableRow,
'min-width': '150px'
}
},
{
displayName: loc.lastTransition,
valueType: azdata.DeclarativeDataType.string,
isReadOnly: true,
width: '30%',
headerCssStyles: cssStyles.tableHeader,
rowCssStyles: cssStyles.tableRow
}
],
dataValues: this.createPodConditionsDataValues(this.coordinatorData)
}).component();
this.podDropDown = this.modelView.modelBuilder.dropDown().withProps({
width: '150px',
ariaLabel: loc.podsUsedDescriptionAria
}).component();
this.disposables.push(
this.podDropDown.onValueChanged(() => {
this.podConditionsTable.setFilter(this.podConditionsTableIndexes.get(String(this.podDropDown.value)));
})
);
this.podConditionsContainer.addItem(this.podDropDown, { CSSStyles: { 'margin': '10px 0px 10px 0px' } });
this.podConditionsContainer.addItem(this.podConditionsTable);
this.podConditionsLoading = this.modelView.modelBuilder.loadingComponent()
.withItem(this.podConditionsContainer)
.withProps({
loading: !this._postgresModel.configLastUpdated
}).component();
this.refreshPodConditions();
content.addItem(this.podConditionsLoading, { CSSStyles: cssStyles.text });
this.initialized = true;
return root;
}
protected get toolbarContainer(): azdata.ToolbarContainer {
// Refresh
const refreshButton = this.modelView.modelBuilder.button().withProps({
label: loc.refresh,
iconPath: IconPathHelper.refresh
}).component();
this.disposables.push(
refreshButton.onDidClick(async () => {
refreshButton.enabled = false;
try {
this.podConditionsLoading!.loading = true;
await this._postgresModel.refresh();
} catch (error) {
vscode.window.showErrorMessage(loc.refreshFailed(error));
}
finally {
refreshButton.enabled = true;
}
}));
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
{ component: refreshButton }
]).component();
}
private createPodList(): string[] {
const podStatus = this._postgresModel.config?.status.podsStatus;
let podNames: string[] = [];
podStatus?.forEach(p => {
let podHealthModels: PodHealthModel[] = [];
let indexes: number[] = [];
p.conditions.forEach(c => {
let message: string;
let imageComponent = this.modelView.modelBuilder.image().withProps({
width: iconSize,
height: iconSize,
iconHeight: '15px',
iconWidth: '15px'
}).component();
if (c.status === 'False') {
imageComponent.iconPath = IconPathHelper.fail;
message = c.message ?? c.reason ?? '';
} else {
imageComponent.iconPath = IconPathHelper.success;
if (c.type === PodConditionType.initialized) {
message = loc.podInitialized;
} else if (c.type === PodConditionType.ready) {
message = loc.podReady;
} else if (c.type === PodConditionType.containersReady) {
message = loc.containerReady;
} else if (c.type === PodConditionType.podScheduled) {
message = loc.podScheduled;
} else {
message = c.message ?? c.reason ?? '';
}
}
const conditionContainer = this.modelView.modelBuilder.flexContainer().withProps({
CSSStyles: { 'alignItems': 'center', 'height': '15px' }
}).component();
conditionContainer.addItem(imageComponent, { CSSStyles: { 'margin-right': '0px' } });
conditionContainer.addItem(this.modelView.modelBuilder.text().withProps({
value: message,
}).component());
indexes.push(this.podsData.length);
this.podsData.push({
condition: c.type,
details: conditionContainer,
lastUpdate: c.lastTransitionTime
});
});
if (p.role.toUpperCase() !== loc.coordinator.toUpperCase()) {
podNames.push(p.name);
} else {
this.coordinatorData = podHealthModels;
this.coordinatorPodName = p.name;
podNames.unshift(p.name);
}
this.podConditionsTableIndexes.set(p.name, indexes);
});
this.podConditionsTable.setDataValues(this.createPodConditionsDataValues(this.podsData));
return podNames;
}
private createPodConditionsDataValues(podInfo: PodHealthModel[]): azdata.DeclarativeTableCellValue[][] {
let podDataValues: (string | azdata.Component)[][] = podInfo.map(p => [p.condition, p.details, p.lastUpdate]);
return podDataValues.map(p => {
return p.map((value): azdata.DeclarativeTableCellValue => {
return { value: value };
});
});
}
private findPodIssues(): string[] {
const podStatus = this._postgresModel.config?.status.podsStatus;
let issueCount = 0;
let podIssuesDetected: string[] = [];
podStatus?.forEach(p => {
p.conditions.forEach(c => {
if (c.status === 'False') {
issueCount++;
}
});
if (issueCount > 0) {
podIssuesDetected.push(loc.numberOfIssuesDetected(p.name, issueCount));
issueCount = 0;
}
});
return podIssuesDetected;
}
private refreshPodSummarySection(): void {
let podSummaryTitle = this.modelView.modelBuilder.flexContainer().withProps({
CSSStyles: { 'alignItems': 'center', 'height': '15px', 'margin-top': '20px' }
}).component();
if (!this._postgresModel.config) {
podSummaryTitle.addItem(this.modelView.modelBuilder.loadingComponent().component(), { CSSStyles: { 'margin-right': '5px' } });
podSummaryTitle.addItem(this.modelView.modelBuilder.text().withProps({
value: loc.loading,
CSSStyles: { ...cssStyles.title }
}).component());
this.podSummaryContainer.addItem(podSummaryTitle);
} else {
let components: azdata.Component[] = [];
let imageComponent = this.modelView.modelBuilder.image().withProps({
iconPath: IconPathHelper.success,
width: iconSize,
height: iconSize,
iconHeight: '20px',
iconWidth: '20px'
}).component();
let podIssues = this.findPodIssues();
if (podIssues.length === 0) {
imageComponent.iconPath = IconPathHelper.success;
podSummaryTitle.addItem(imageComponent, { CSSStyles: { 'margin-right': '5px' } });
podSummaryTitle.addItem(this.modelView.modelBuilder.text().withProps({
value: loc.available,
CSSStyles: { ...cssStyles.title, 'margin-left': '0px' }
}).component());
components.push(podSummaryTitle);
components.push(this.modelView.modelBuilder.text().withProps({
value: loc.noPodIssuesDetected,
CSSStyles: { ...cssStyles.text, 'margin-top': '20px' }
}).component());
} else {
imageComponent.iconPath = IconPathHelper.fail;
podSummaryTitle.addItem(imageComponent, { CSSStyles: { 'margin-right': '5px' } });
podSummaryTitle.addItem(this.modelView.modelBuilder.text().withProps({
value: loc.issuesDetected,
CSSStyles: { ...cssStyles.title }
}).component());
components.push(podSummaryTitle);
components.push(this.modelView.modelBuilder.text().withProps({
value: loc.podIssuesDetected,
CSSStyles: { ...cssStyles.text, 'margin-top': '20px 0px 10px 0px' }
}).component());
components.push(...podIssues.map(i => {
return this.modelView.modelBuilder.text().withProps({
value: i,
CSSStyles: { ...cssStyles.text, 'margin': '0px' }
}).component();
}));
}
this.podSummaryContainer.addItems(components);
}
}
private refreshPodConditions(): void {
if (this._postgresModel.config) {
this.podConditionsTableIndexes = new Map();
this.podsData = [];
this.podDropDown.values = this.createPodList();
this.podConditionsTable.setFilter(this.podConditionsTableIndexes.get(this.coordinatorPodName!));
this.podConditionsLoading.loading = false;
}
}
private handleConfigUpdated() {
this.podSummaryContainer.clearItems();
this.refreshPodSummarySection();
this.refreshPodConditions();
}
}

View File

@@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { Deferred } from '../../common/promise';
import * as loc from '../../localizedConstants';
import { cssStyles } from '../../constants';
import { InitializingComponent } from '../components/initializingComponent';
import { PostgresModel } from '../../models/postgresModel';
export const validExtensions = ['citus', 'pgaudit', 'pgautofailover', 'pg_cron', 'pg_partman', 'plv8', 'postgis', 'postgis_raster', 'postgis_sfcgal', 'postgis_tiger_geocoder', 'tdigest'];
export class AddPGExtensionsDialog extends InitializingComponent {
protected modelBuilder!: azdata.ModelBuilder;
protected extensionsListInputBox!: azdata.InputBoxComponent;
protected _completionPromise = new Deferred<string | undefined>();
constructor(protected _model: PostgresModel) {
super();
}
public showDialog(dialogTitle: string): azdata.window.Dialog {
const dialog = azdata.window.createModelViewDialog(dialogTitle);
dialog.cancelButton.onClick(() => this.handleCancel());
dialog.registerContent(async view => {
this.modelBuilder = view.modelBuilder;
const info = this.modelBuilder.text().withProps({
value: loc.extensionsAddFunction(validExtensions.join(', ')),
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
}).component();
const link = this.modelBuilder.hyperlink().withProps({
label: loc.extensionsLearnMore,
url: 'https://docs.microsoft.com/azure/azure-arc/data/using-extensions-in-postgresql-hyperscale-server-group',
}).component();
const infoAndLink = this.modelBuilder.flexContainer().withLayout({ flexWrap: 'wrap' }).component();
infoAndLink.addItem(info, { CSSStyles: { 'margin-right': '5px' } });
infoAndLink.addItem(link);
this.extensionsListInputBox = this.modelBuilder.inputBox()
.withProps({
value: '',
ariaLabel: loc.extensionsAddList,
enabled: true,
validationErrorMessage: loc.extensionsAddErrorrMessage(validExtensions.join(','))
}).withValidation((component) => {
if (!component.value) {
return true;
}
let newExtensions = component.value.split(',');
return newExtensions.every(e => validExtensions.includes(e));
}).component();
let formModel = this.modelBuilder.formContainer()
.withFormItems([{
components: [
{
component: infoAndLink
},
{
component: this.extensionsListInputBox,
title: loc.extensionsAddList,
required: true
}
],
title: ''
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
this.extensionsListInputBox.focus();
this.initialized = true;
});
dialog.registerCloseValidator(async () => await this.validate());
dialog.okButton.label = loc.loadExtensions;
dialog.cancelButton.label = loc.cancel;
azdata.window.openDialog(dialog);
return dialog;
}
public async validate(): Promise<boolean> {
this._completionPromise.resolve(this.extensionsListInputBox.value);
return true;
}
private handleCancel(): void {
this._completionPromise.resolve(undefined);
}
public waitForClose(): Promise<string | undefined> {
return this._completionPromise.promise;
}
}

View File

@@ -8,15 +8,11 @@ import * as vscode from 'vscode';
import { Deferred } from '../../common/promise'; import { Deferred } from '../../common/promise';
import * as loc from '../../localizedConstants'; import * as loc from '../../localizedConstants';
import { createCredentialId } from '../../common/utils'; import { createCredentialId } from '../../common/utils';
import * as constants from '../../constants'; import { credentialNamespace } from '../../constants';
import { InitializingComponent } from '../components/initializingComponent'; import { InitializingComponent } from '../components/initializingComponent';
import { ResourceModel } from '../../models/resourceModel'; import { ResourceModel } from '../../models/resourceModel';
import { ControllerModel } from '../../models/controllerModel'; import { ControllerModel } from '../../models/controllerModel';
export interface IReconnectAction {
(profile: azdata.IConnectionProfile): Promise<boolean>;
}
export abstract class ConnectToSqlDialog extends InitializingComponent { export abstract class ConnectToSqlDialog extends InitializingComponent {
protected modelBuilder!: azdata.ModelBuilder; protected modelBuilder!: azdata.ModelBuilder;
@@ -24,9 +20,6 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
protected usernameInputBox!: azdata.InputBoxComponent; protected usernameInputBox!: azdata.InputBoxComponent;
protected passwordInputBox!: azdata.InputBoxComponent; protected passwordInputBox!: azdata.InputBoxComponent;
protected rememberPwCheckBox!: azdata.CheckBoxComponent; protected rememberPwCheckBox!: azdata.CheckBoxComponent;
protected encryptSelectBox!: azdata.DropDownComponent;
protected trustServerCertificateSelectBox!: azdata.DropDownComponent;
private options: { [name: string]: any } = {}; private options: { [name: string]: any } = {};
protected _completionPromise = new Deferred<azdata.IConnectionProfile | undefined>(); protected _completionPromise = new Deferred<azdata.IConnectionProfile | undefined>();
@@ -36,11 +29,7 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
} }
public showDialog(dialogTitle: string, connectionProfile?: azdata.IConnectionProfile): azdata.window.Dialog { public showDialog(dialogTitle: string, connectionProfile?: azdata.IConnectionProfile): azdata.window.Dialog {
const dialog = azdata.window.createModelViewDialog(dialogTitle, undefined, 'narrow'); const dialog = azdata.window.createModelViewDialog(dialogTitle);
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.cancelButton.onClick(() => this.handleCancel());
dialog.registerContent(async view => { dialog.registerContent(async view => {
this.modelBuilder = view.modelBuilder; this.modelBuilder = view.modelBuilder;
@@ -58,22 +47,13 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
.withProps({ .withProps({
inputType: 'password', inputType: 'password',
value: connectionProfile?.password value: connectionProfile?.password
}).component(); })
.component();
this.rememberPwCheckBox = this.modelBuilder.checkBox() this.rememberPwCheckBox = this.modelBuilder.checkBox()
.withProps({ .withProps({
label: loc.rememberPassword, label: loc.rememberPassword,
checked: connectionProfile?.savePassword checked: connectionProfile?.savePassword
}).component(); }).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() let formModel = this.modelBuilder.formContainer()
.withFormItems([{ .withFormItems([{
@@ -93,18 +73,6 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
}, { }, {
component: this.rememberPwCheckBox, component: this.rememberPwCheckBox,
title: '' title: ''
}, {
component: this.encryptSelectBox,
title: loc.encrypt,
layout: {
info: loc.encryptDescription,
}
}, {
component: this.trustServerCertificateSelectBox,
title: loc.trustServerCertificate,
layout: {
info: loc.trustServerCertDescription,
}
} }
], ],
title: '' title: ''
@@ -126,10 +94,6 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
if (!this.serverNameInputBox.value || !this.usernameInputBox.value || !this.passwordInputBox.value) { if (!this.serverNameInputBox.value || !this.usernameInputBox.value || !this.passwordInputBox.value) {
return false; return false;
} }
this.options.encrypt = this.encryptSelectBox.value;
this.options.trustServerCertificate = this.trustServerCertificateSelectBox.value;
const connectionProfile: azdata.IConnectionProfile = { const connectionProfile: azdata.IConnectionProfile = {
serverName: this.serverNameInputBox.value, serverName: this.serverNameInputBox.value,
databaseName: '', databaseName: '',
@@ -145,15 +109,10 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
groupId: undefined, groupId: undefined,
options: this.options 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); const result = await azdata.connection.connect(connectionProfile, false, false);
if (result.connected) { if (result.connected) {
connectionProfile.id = result.connectionId!; connectionProfile.id = result.connectionId!;
const credentialProvider = await azdata.credentials.getProvider(constants.credentialNamespace); const credentialProvider = await azdata.credentials.getProvider(credentialNamespace);
if (connectionProfile.savePassword) { if (connectionProfile.savePassword) {
await credentialProvider.saveCredential(createCredentialId(this._controllerModel.info.id, this._model.info.resourceType, this._model.info.name), connectionProfile.password); await credentialProvider.saveCredential(createCredentialId(this._controllerModel.info.id, this._model.info.resourceType, this._model.info.name), connectionProfile.password);
} else { } else {
@@ -164,40 +123,10 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
} }
else { else {
vscode.window.showErrorMessage(this.connectionFailedMessage(result.errorMessage)); vscode.window.showErrorMessage(this.connectionFailedMessage(result.errorMessage));
// Show error with instructions for MSSQL Provider Encryption error code -2146893019 thrown by SqlClient when certificate validation fails. return false;
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 get providerName(): string;
protected abstract connectionFailedMessage(error: any): string; protected abstract connectionFailedMessage(error: any): string;

View File

@@ -110,15 +110,10 @@ export class ControllerTreeNode extends TreeNode {
node = new PostgresTreeNode(postgresModel, this.model); node = new PostgresTreeNode(postgresModel, this.model);
break; break;
case ResourceType.sqlManagedInstances: case ResourceType.sqlManagedInstances:
// Fill in the username and connection properties too if we already have them // Fill in the username too if we already have it
let miaaResourceInfo = this.model.info.resources.find(info => (resourceInfo as MiaaResourceInfo).userName = (this.model.info.resources.find(info =>
info.name === resourceInfo.name && info.name === resourceInfo.name &&
info.resourceType === resourceInfo.resourceType) as MiaaResourceInfo; info.resourceType === resourceInfo.resourceType) as MiaaResourceInfo)?.userName;
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); const miaaModel = new MiaaModel(this.model, resourceInfo, registration, this._treeDataProvider);
node = new MiaaTreeNode(miaaModel, this.model); node = new MiaaTreeNode(miaaModel, this.model);
break; break;

View File

@@ -182,13 +182,13 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
"@microsoft/azdata-test@^3.0.1": "@microsoft/azdata-test@^2.0.3":
version "3.0.1" version "2.0.3"
resolved "https://registry.yarnpkg.com/@microsoft/azdata-test/-/azdata-test-3.0.1.tgz#a8b89a12de42f277d33aae71c277d0c8efcfbee0" resolved "https://registry.yarnpkg.com/@microsoft/azdata-test/-/azdata-test-2.0.3.tgz#652984efa2f5adc56cdae9029a4d5f33446b54d3"
integrity sha512-Zrctm/zKufwIRF9jfw8TOBzr5woLdKXAGNTlbAQl0IGLzVoIGULj9Gqdc1Ikhrov3rM0NkbAF/PY6j6BHiW8Tw== integrity sha512-BgB6gGjQVXxnZHq7o5TlajZR/mJd/6AqbclrGzoyATvCEt92jRXhPzaY6XA/jMahdUGFSQwXpm45qRhZetwDig==
dependencies: dependencies:
http-proxy-agent "^5.0.0" http-proxy-agent "^2.1.0"
https-proxy-agent "^5.0.0" https-proxy-agent "^2.2.4"
mocha-junit-reporter "^1.17.0" mocha-junit-reporter "^1.17.0"
mocha-multi-reporters "^1.1.7" mocha-multi-reporters "^1.1.7"
rimraf "^2.6.3" rimraf "^2.6.3"
@@ -245,11 +245,6 @@
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
"@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": "@types/mocha@^7.0.2":
version "7.0.2" version "7.0.2"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce"
@@ -282,12 +277,12 @@
resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.31.tgz#b1a620b115c96db7b3bfdf0cf54aee0c57139245" resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.31.tgz#b1a620b115c96db7b3bfdf0cf54aee0c57139245"
integrity sha512-QcJ5ZczaXAqbVD3o8mw/mEBhRvO5UAdTtbvgwL/OgoWubvNBh6/MxLBAigtcgIFaq3shon9m3POIxQaLQt4fxQ== integrity sha512-QcJ5ZczaXAqbVD3o8mw/mEBhRvO5UAdTtbvgwL/OgoWubvNBh6/MxLBAigtcgIFaq3shon9m3POIxQaLQt4fxQ==
agent-base@6: agent-base@4, agent-base@^4.3.0:
version "6.0.2" version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
dependencies: dependencies:
debug "4" es6-promisify "^5.0.0"
ansi-colors@3.2.3: ansi-colors@3.2.3:
version "3.2.3" version "3.2.3"
@@ -453,6 +448,13 @@ crypt@~0.0.1:
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
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.2.6, debug@^3.1.0: debug@3.2.6, debug@^3.1.0:
version "3.2.6" version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -460,13 +462,6 @@ debug@3.2.6, debug@^3.1.0:
dependencies: dependencies:
ms "^2.1.1" 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: debug@^2.2.0:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -561,6 +556,18 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1" is-date-object "^1.0.1"
is-symbol "^1.0.2" is-symbol "^1.0.2"
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"
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -741,22 +748,21 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
http-proxy-agent@^5.0.0: http-proxy-agent@^2.1.0:
version "5.0.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
dependencies: dependencies:
"@tootallnate/once" "2" agent-base "4"
agent-base "6" debug "3.1.0"
debug "4"
https-proxy-agent@^5.0.0: https-proxy-agent@^2.2.4:
version "5.0.1" version "2.2.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
dependencies: dependencies:
agent-base "6" agent-base "^4.3.0"
debug "4" debug "^3.1.0"
inflight@^1.0.4: inflight@^1.0.4:
version "1.0.6" version "1.0.6"
@@ -978,9 +984,11 @@ jsesc@^2.5.1:
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
json5@^2.1.2: json5@^2.1.2:
version "2.2.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
dependencies:
minimist "^1.2.5"
just-extend@^4.0.2: just-extend@^4.0.2:
version "4.1.0" version "4.1.0"
@@ -1114,7 +1122,7 @@ ms@2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
ms@2.1.2, ms@^2.1.1: ms@^2.1.1:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==

View File

@@ -8,7 +8,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
## Privacy Statement ## Privacy Statement
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704). The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
## License ## License

View File

@@ -14,7 +14,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
## Privacy Statement ## Privacy Statement
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704). The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
## License ## License

View File

@@ -2,7 +2,7 @@
"name": "azcli", "name": "azcli",
"displayName": "%azcli.arc.displayName%", "displayName": "%azcli.arc.displayName%",
"description": "%azcli.arc.description%", "description": "%azcli.arc.description%",
"version": "1.8.0", "version": "1.7.0",
"publisher": "Microsoft", "publisher": "Microsoft",
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt", "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
"icon": "images/extension.png", "icon": "images/extension.png",
@@ -107,7 +107,7 @@
"sinon": "^9.0.2", "sinon": "^9.0.2",
"typemoq": "^2.1.0", "typemoq": "^2.1.0",
"@microsoft/vscodetestcover": "^1.2.1", "@microsoft/vscodetestcover": "^1.2.1",
"@microsoft/azdata-test": "^3.0.1" "@microsoft/azdata-test": "^2.0.3"
}, },
"__metadata": { "__metadata": {
"id": "84", "id": "84",

View File

@@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
interface PlatformReleaseInfo {
version: string; // "20.0.1"
link?: string; // "https://aka.ms/az-msi"
}
export interface AzReleaseInfo {
win32: PlatformReleaseInfo,
darwin: PlatformReleaseInfo,
linux: PlatformReleaseInfo
}

View File

@@ -9,12 +9,12 @@
* @param promise The promise to verify was rejected * @param promise The promise to verify was rejected
* @param message The message to include in the error if the promise isn't rejected * @param message The message to include in the error if the promise isn't rejected
*/ */
// export async function assertRejected(promise: Promise<any>, message: string): Promise<void> { export async function assertRejected(promise: Promise<any>, message: string): Promise<void> {
// try { try {
// await promise; await promise;
// } catch { } catch {
// return; return;
// } }
// throw new Error(message); throw new Error(message);
// } }

View File

@@ -181,13 +181,13 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
"@microsoft/azdata-test@^3.0.1": "@microsoft/azdata-test@^2.0.3":
version "3.0.1" version "2.0.3"
resolved "https://registry.yarnpkg.com/@microsoft/azdata-test/-/azdata-test-3.0.1.tgz#a8b89a12de42f277d33aae71c277d0c8efcfbee0" resolved "https://registry.yarnpkg.com/@microsoft/azdata-test/-/azdata-test-2.0.3.tgz#652984efa2f5adc56cdae9029a4d5f33446b54d3"
integrity sha512-Zrctm/zKufwIRF9jfw8TOBzr5woLdKXAGNTlbAQl0IGLzVoIGULj9Gqdc1Ikhrov3rM0NkbAF/PY6j6BHiW8Tw== integrity sha512-BgB6gGjQVXxnZHq7o5TlajZR/mJd/6AqbclrGzoyATvCEt92jRXhPzaY6XA/jMahdUGFSQwXpm45qRhZetwDig==
dependencies: dependencies:
http-proxy-agent "^5.0.0" http-proxy-agent "^2.1.0"
https-proxy-agent "^5.0.0" https-proxy-agent "^2.2.4"
mocha-junit-reporter "^1.17.0" mocha-junit-reporter "^1.17.0"
mocha-multi-reporters "^1.1.7" mocha-multi-reporters "^1.1.7"
rimraf "^2.6.3" rimraf "^2.6.3"
@@ -244,11 +244,6 @@
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
"@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/caseless@*": "@types/caseless@*":
version "0.12.2" version "0.12.2"
resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8"
@@ -313,12 +308,12 @@
resolved "https://registry.yarnpkg.com/@types/which/-/which-1.3.2.tgz#9c246fc0c93ded311c8512df2891fb41f6227fdf" resolved "https://registry.yarnpkg.com/@types/which/-/which-1.3.2.tgz#9c246fc0c93ded311c8512df2891fb41f6227fdf"
integrity sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA== integrity sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA==
agent-base@6: agent-base@4, agent-base@^4.3.0:
version "6.0.2" version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
dependencies: dependencies:
debug "4" es6-promisify "^5.0.0"
ajv@^6.5.5: ajv@^6.5.5:
version "6.12.3" version "6.12.3"
@@ -552,6 +547,13 @@ dashdash@^1.12.0:
dependencies: dependencies:
assert-plus "^1.0.0" assert-plus "^1.0.0"
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.2.6, debug@^3.1.0: debug@3.2.6, debug@^3.1.0:
version "3.2.6" version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -559,13 +561,6 @@ debug@3.2.6, debug@^3.1.0:
dependencies: dependencies:
ms "^2.1.1" 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: debug@^2.2.0:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -673,6 +668,18 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1" is-date-object "^1.0.1"
is-symbol "^1.0.2" is-symbol "^1.0.2"
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 sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==
dependencies:
es6-promise "^4.0.3"
escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -921,14 +928,13 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
http-proxy-agent@^5.0.0: http-proxy-agent@^2.1.0:
version "5.0.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
dependencies: dependencies:
"@tootallnate/once" "2" agent-base "4"
agent-base "6" debug "3.1.0"
debug "4"
http-signature@~1.2.0: http-signature@~1.2.0:
version "1.2.0" version "1.2.0"
@@ -939,13 +945,13 @@ http-signature@~1.2.0:
jsprim "^1.2.2" jsprim "^1.2.2"
sshpk "^1.7.0" sshpk "^1.7.0"
https-proxy-agent@^5.0.0: https-proxy-agent@^2.2.4:
version "5.0.1" version "2.2.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
dependencies: dependencies:
agent-base "6" agent-base "^4.3.0"
debug "4" debug "^3.1.0"
inflight@^1.0.4: inflight@^1.0.4:
version "1.0.6" version "1.0.6"
@@ -1197,9 +1203,11 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json5@^2.1.2: json5@^2.1.2:
version "2.2.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
dependencies:
minimist "^1.2.5"
jsprim@^1.2.2: jsprim@^1.2.2:
version "1.4.2" version "1.4.2"
@@ -1360,7 +1368,7 @@ ms@2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
ms@2.1.2, ms@^2.1.1: ms@^2.1.1:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
@@ -1527,9 +1535,9 @@ punycode@^2.1.0, punycode@^2.1.1:
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@~6.5.2: qs@~6.5.2:
version "6.5.3" version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
readdirp@~3.2.0: readdirp@~3.2.0:
version "3.2.0" version "3.2.0"

View File

@@ -12,7 +12,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
## Privacy Statement ## Privacy Statement
To learn more about our Privacy Statement visit [this link](https://go.microsoft.com/fwlink/?LinkID=824704). The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
## License ## License

View File

@@ -19,10 +19,7 @@ const externals = {
'universalify': 'commonjs universalify', 'universalify': 'commonjs universalify',
'@azure/arm-subscriptions': 'commonjs @azure/arm-subscriptions', '@azure/arm-subscriptions': 'commonjs @azure/arm-subscriptions',
'@azure/arm-resourcegraph': 'commonjs @azure/arm-resourcegraph', '@azure/arm-resourcegraph': 'commonjs @azure/arm-resourcegraph',
'@azure/storage-blob': 'commonjs @azure/storage-blob', '@azure/storage-blob': 'commonjs @azure/storage-blob'
'@azure/msal-node': 'commonjs @azure/msal-node',
'@azure/msal-node-extensions': 'commonjs @azure/msal-node-extensions',
'msal': 'commonjs msal'
}; };
// conditionally add ws if we are going to be running in a node environment // conditionally add ws if we are going to be running in a node environment

View File

@@ -4,9 +4,19 @@
"default": false, "default": false,
"description": "%config.enableUsGovCloudDescription%" "description": "%config.enableUsGovCloudDescription%"
}, },
"accounts.azure.enableUsNatCloud": {
"type": "boolean",
"default": false,
"description": "%config.enableUsNatCloudDescription%"
},
"accounts.azure.enableChinaCloud": { "accounts.azure.enableChinaCloud": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "%config.enableChinaCloudDescription%" "description": "%config.enableChinaCloudDescription%"
},
"accounts.azure.enableGermanyCloud": {
"type": "boolean",
"default": false,
"description": "%config.enableGermanyCloudDescription%"
} }
} }

View File

@@ -18,12 +18,12 @@
"type": "git", "type": "git",
"url": "https://github.com/Microsoft/azuredatastudio.git" "url": "https://github.com/Microsoft/azuredatastudio.git"
}, },
"capabilities": { "capabilities": {
"virtualWorkspaces": false, "virtualWorkspaces": false,
"untrustedWorkspaces": { "untrustedWorkspaces": {
"supported": true "supported": true
} }
}, },
"contributes": { "contributes": {
"resourceViewResources": [ "resourceViewResources": [
{ {
@@ -63,6 +63,16 @@
"default": false, "default": false,
"description": "%config.enableUsGovCloudDescription%" "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": { "accounts.azure.cloud.enableChinaCloud": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
@@ -114,20 +124,6 @@
"Verbose", "Verbose",
"All" "All"
] ]
},
"azure.authenticationLibrary": {
"type": "string",
"description": "%config.authenticationLibrary%",
"default": "MSAL",
"enum": [
"ADAL",
"MSAL"
],
"enumDescriptions": [
"Azure Active Directory Authentication Library",
"Microsoft Authentication Library"
],
"deprecationMessage": "Warning: ADAL has been deprecated, and is scheduled to be removed in a future release. Please use MSAL (default option) instead."
} }
} }
} }
@@ -279,22 +275,22 @@
}, },
{ {
"command": "azure.resource.azureview.refresh", "command": "azure.resource.azureview.refresh",
"when": "viewItem =~ /^azure\\.resource\\.itemType\\.(?:account|subscription|databaseContainer|databaseServerContainer|synapseSqlPoolContainer|synapseWorkspaceContainer)$/", "when": "viewItem =~ /^azure\\.resource\\.itemType\\.(?:account|subscription|databaseContainer|databaseServerContainer)$/",
"group": "inline" "group": "inline"
}, },
{ {
"command": "azure.resource.azureview.refresh", "command": "azure.resource.azureview.refresh",
"when": "viewItem =~ /^azure\\.resource\\.itemType\\.(?:account|subscription|databaseContainer|databaseServerContainer|synapseSqlPoolContainer|synapseWorkspaceContainer)$/", "when": "viewItem =~ /^azure\\.resource\\.itemType\\.(?:account|subscription|databaseContainer|databaseServerContainer)$/",
"group": "azurecore" "group": "azurecore"
}, },
{ {
"command": "azure.resource.connectsqlserver", "command": "azure.resource.connectsqlserver",
"when": "viewItem =~ /^azure\\.resource\\.itemType\\.(?:database|databaseServer|synapseSqlPool|synapseWorkspace|sqlInstance)$/", "when": "viewItem == azure.resource.itemType.databaseServer || viewItem == azure.resource.itemType.database || viewItem == azure.resource.itemType.sqlInstance",
"group": "inline" "group": "inline"
}, },
{ {
"command": "azure.resource.connectsqlserver", "command": "azure.resource.connectsqlserver",
"when": "viewItem =~ /^azure\\.resource\\.itemType\\.(?:database|databaseServer|synapseSqlPool|synapseWorkspace|sqlInstance)$/", "when": "viewItem == azure.resource.itemType.databaseServer || viewItem == azure.resource.itemType.database || viewItem == azure.resource.itemType.sqlInstance",
"group": "azurecore" "group": "azurecore"
}, },
{ {
@@ -351,11 +347,8 @@
"dependencies": { "dependencies": {
"@azure/arm-resourcegraph": "^4.0.0", "@azure/arm-resourcegraph": "^4.0.0",
"@azure/arm-subscriptions": "^3.0.0", "@azure/arm-subscriptions": "^3.0.0",
"@azure/msal-common": "^11.0.0",
"@azure/msal-node": "^1.16.0",
"@azure/storage-blob": "^12.6.0", "@azure/storage-blob": "^12.6.0",
"axios": "^0.27.2", "axios": "^0.27.2",
"lockfile": "1.0.4",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"qs": "^6.9.1", "qs": "^6.9.1",
"universalify": "^0.1.2", "universalify": "^0.1.2",
@@ -363,12 +356,9 @@
"ws": "^7.4.6" "ws": "^7.4.6"
}, },
"devDependencies": { "devDependencies": {
"@microsoft/azdata-test": "^3.0.1",
"@microsoft/vscodetestcover": "^1.2.1",
"@types/keytar": "4.4.0", "@types/keytar": "4.4.0",
"@types/lockfile": "^1.0.2",
"@types/mocha": "^7.0.2", "@types/mocha": "^7.0.2",
"@types/node": "^12.20.55", "@types/node": "^12.11.7",
"@types/qs": "^6.9.1", "@types/qs": "^6.9.1",
"@types/request": "^2.48.1", "@types/request": "^2.48.1",
"@types/sinon": "^9.0.4", "@types/sinon": "^9.0.4",
@@ -376,6 +366,8 @@
"mocha": "^7.1.1", "mocha": "^7.1.1",
"should": "^13.2.1", "should": "^13.2.1",
"sinon": "^9.0.2", "sinon": "^9.0.2",
"typemoq": "^2.1.0" "typemoq": "^2.1.0",
"@microsoft/vscodetestcover": "^1.2.1",
"@microsoft/azdata-test": "^2.0.3"
} }
} }

View File

@@ -31,7 +31,6 @@
"config.azureDeviceCodeMethod": "Device Code Method", "config.azureDeviceCodeMethod": "Device Code Method",
"config.noSystemKeychain": "Disable system keychain integration. Credentials will be stored in a flat file in the user's home directory.", "config.noSystemKeychain": "Disable system keychain integration. Credentials will be stored in a flat file in the user's home directory.",
"config.piiLogging": "Should Personally Identifiable Information (PII) be logged in the Azure Accounts output channel and the output channel log file.", "config.piiLogging": "Should Personally Identifiable Information (PII) be logged in the Azure Accounts output channel and the output channel log file.",
"config.loggingLevel": "[Optional] The verbosity of logging for the Azure Accounts extension.", "config.loggingLevel": "[Optional] The verbosity of logging for the Azure Accounts extension."
"config.authenticationLibrary": "The library used for the AAD auth flow. Please restart ADS after changing this option."
} }

View File

@@ -5,6 +5,7 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as azdata from 'azdata'; import * as azdata from 'azdata';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
import { import {
@@ -14,58 +15,51 @@ import {
Resource, Resource,
Tenant Tenant
} from 'azurecore'; } from 'azurecore';
import { Deferred } from '../interfaces'; import { Deferred } from '../interfaces';
import * as url from 'url'; import * as url from 'url';
import * as Constants from '../../constants';
import { SimpleTokenCache } from '../utils/simpleTokenCache'; import { SimpleTokenCache } from '../simpleTokenCache';
import { MemoryDatabase } from '../utils/memoryDatabase'; import { MemoryDatabase } from '../utils/memoryDatabase';
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { Logger } from '../../utils/Logger'; import { Logger } from '../../utils/Logger';
import * as qs from 'qs'; import * as qs from 'qs';
import { AzureAuthError } from './azureAuthError'; import { AzureAuthError } from './azureAuthError';
import { AccountInfo, AuthenticationResult, InteractionRequiredAuthError, PublicClientApplication } from '@azure/msal-node';
import { HttpClient } from './httpClient';
import { getProxyEnabledHttpClient } from '../../utils';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
export abstract class AzureAuth implements vscode.Disposable { export abstract class AzureAuth implements vscode.Disposable {
public static ACCOUNT_VERSION = '2.0';
protected readonly memdb = new MemoryDatabase<string>(); protected readonly memdb = new MemoryDatabase<string>();
protected readonly WorkSchoolAccountType: string = 'work_school';
protected readonly MicrosoftAccountType: string = 'microsoft';
protected readonly loginEndpointUrl: string; protected readonly loginEndpointUrl: string;
public readonly commonTenant: Tenant; public readonly commonTenant: Tenant;
public readonly organizationTenant: Tenant;
protected readonly redirectUri: string; protected readonly redirectUri: string;
protected readonly scopes: string[]; protected readonly scopes: string[];
protected readonly scopesString: string; protected readonly scopesString: string;
protected readonly clientId: string; protected readonly clientId: string;
protected readonly resources: Resource[]; protected readonly resources: Resource[];
protected readonly httpClient: HttpClient;
private _authLibrary: string | undefined;
constructor( constructor(
protected readonly metadata: AzureAccountProviderMetadata, protected readonly metadata: AzureAccountProviderMetadata,
protected readonly tokenCache: SimpleTokenCache, protected readonly tokenCache: SimpleTokenCache,
protected readonly context: vscode.ExtensionContext, protected readonly context: vscode.ExtensionContext,
protected clientApplication: PublicClientApplication,
protected readonly uriEventEmitter: vscode.EventEmitter<vscode.Uri>, protected readonly uriEventEmitter: vscode.EventEmitter<vscode.Uri>,
protected readonly authType: AzureAuthType, protected readonly authType: AzureAuthType,
public readonly userFriendlyName: string, public readonly userFriendlyName: string
public readonly authLibrary: string
) { ) {
this._authLibrary = authLibrary;
this.loginEndpointUrl = this.metadata.settings.host; this.loginEndpointUrl = this.metadata.settings.host;
this.commonTenant = { this.commonTenant = {
id: 'common', id: 'common',
displayName: 'common', displayName: 'common',
}; };
this.organizationTenant = {
id: 'organizations',
displayName: 'organizations',
};
this.redirectUri = this.metadata.settings.redirectUri; this.redirectUri = this.metadata.settings.redirectUri;
this.clientId = this.metadata.settings.clientId; this.clientId = this.metadata.settings.clientId;
this.resources = [ this.resources = [
this.metadata.settings.armResource, this.metadata.settings.armResource,
this.metadata.settings.graphResource, this.metadata.settings.graphResource,
@@ -97,7 +91,6 @@ export abstract class AzureAuth implements vscode.Disposable {
this.scopes = [...this.metadata.settings.scopes]; this.scopes = [...this.metadata.settings.scopes];
this.scopesString = this.scopes.join(' '); this.scopesString = this.scopes.join(' ');
this.httpClient = getProxyEnabledHttpClient();
} }
public async startLogin(): Promise<AzureAccount | azdata.PromptFailedResult> { public async startLogin(): Promise<AzureAccount | azdata.PromptFailedResult> {
@@ -107,67 +100,42 @@ export abstract class AzureAuth implements vscode.Disposable {
if (!this.metadata.settings.microsoftResource) { if (!this.metadata.settings.microsoftResource) {
throw new Error(localize('noMicrosoftResource', "Provider '{0}' does not have a Microsoft resource endpoint defined.", this.metadata.displayName)); throw new Error(localize('noMicrosoftResource', "Provider '{0}' does not have a Microsoft resource endpoint defined.", this.metadata.displayName));
} }
if (this._authLibrary === Constants.AuthLibrary.MSAL) { const result = await this.login(this.commonTenant, this.metadata.settings.microsoftResource);
const result = await this.loginMsal(this.organizationTenant, this.metadata.settings.microsoftResource); loginComplete = result.authComplete;
loginComplete = result.authComplete; if (!result?.response) {
if (!result?.response || !result.response?.account) { Logger.error('Authentication failed');
Logger.error(`Authentication failed: ${loginComplete}`); return {
return { canceled: false
canceled: false
};
}
const token: Token = {
token: result.response.accessToken,
key: result.response.account.homeAccountId,
tokenType: result.response.tokenType
}; };
const tokenClaims = <TokenClaims>result.response.idTokenClaims;
const account = await this.hydrateAccount(token, tokenClaims);
loginComplete?.resolve();
return account;
} else {// fallback to ADAL as default
const result = await this.loginAdal(this.commonTenant, this.metadata.settings.microsoftResource);
loginComplete = result.authComplete;
if (!result?.response) {
Logger.error('Authentication failed - no response');
return {
canceled: false
};
}
const account = await this.hydrateAccount(result.response.accessToken, result.response.tokenClaims);
loginComplete?.resolve();
return account;
} }
const account = await this.hydrateAccount(result.response.accessToken, result.response.tokenClaims);
loginComplete?.resolve();
return account;
} catch (ex) { } catch (ex) {
Logger.error(`Login failed: ${ex}`); Logger.error('Login failed');
if (ex instanceof AzureAuthError) { if (ex instanceof AzureAuthError) {
if (loginComplete) { if (loginComplete) {
loginComplete.reject(ex); loginComplete.reject(ex);
Logger.error(ex);
} else {
void vscode.window.showErrorMessage(ex.message);
Logger.error(ex.originalMessageAndException);
} }
Logger.error(ex.originalMessageAndException);
} else { } 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); Logger.error(ex);
} }
return { return {
canceled: false, canceled: false
errorCode: ex.errorCode,
errorMessage: ex.errorMessage || ex.message
}; };
} finally {
loginComplete?.reject(new AzureAuthError(localize('azureAuth.unidentifiedError', "Unidentified error with azure authentication"), 'Unidentified error with azure auth', undefined));
} }
} }
public async refreshAccessAdal(account: AzureAccount): Promise<AzureAccount> { public async refreshAccess(account: AzureAccount): Promise<AzureAccount> {
// Deprecated account - delete it. // Deprecated account - delete it.
if (account.key.accountVersion !== Constants.AccountVersion) { if (account.key.accountVersion !== AzureAuth.ACCOUNT_VERSION) {
account.delete = true; account.delete = true;
return account; return account;
} }
@@ -176,7 +144,7 @@ export abstract class AzureAuth implements vscode.Disposable {
// We want to return the one that owns the Azure account. // We want to return the one that owns the Azure account.
// Not doing so can result in token being issued for the wrong tenant // Not doing so can result in token being issued for the wrong tenant
const tenant = account.properties.owningTenant; const tenant = account.properties.owningTenant;
const tokenResult = await this.getAccountSecurityTokenAdal(account, tenant.id, azdata.AzureResource.MicrosoftResourceManagement); const tokenResult = await this.getAccountSecurityToken(account, tenant.id, azdata.AzureResource.MicrosoftResourceManagement);
if (!tokenResult) { if (!tokenResult) {
account.isStale = true; account.isStale = true;
return account; return account;
@@ -186,37 +154,32 @@ export abstract class AzureAuth implements vscode.Disposable {
} catch (ex) { } catch (ex) {
if (ex instanceof AzureAuthError) { if (ex instanceof AzureAuthError) {
void vscode.window.showErrorMessage(ex.message); void vscode.window.showErrorMessage(ex.message);
Logger.error(`Error refreshing access for account ${account.displayInfo.displayName}`, ex.originalMessageAndException); Logger.error(ex.originalMessageAndException);
} else { } else {
Logger.error(ex); Logger.error(ex);
} }
Logger.error(ex);
account.isStale = true; account.isStale = true;
return account; return account;
} }
} }
public async hydrateAccount(token: Token | AccessToken, tokenClaims: TokenClaims): Promise<AzureAccount> { public async hydrateAccount(token: Token | AccessToken, tokenClaims: TokenClaims): Promise<AzureAccount> {
let account: azdata.Account; const tenants = await this.getTenants({ ...token });
if (this._authLibrary === Constants.AuthLibrary.MSAL) { const account = this.createAccount(tokenClaims, token.key, tenants);
const tenants = await this.getTenantsMsal(token.token);
account = this.createAccount(tokenClaims, token.key, tenants);
} else { // fallback to ADAL as default
const tenants = await this.getTenantsAdal({ ...token });
account = this.createAccount(tokenClaims, token.key, tenants);
}
return account; return account;
} }
public async getAccountSecurityTokenAdal(account: AzureAccount, tenantId: string, azureResource: azdata.AzureResource): Promise<Token | undefined> { public async getAccountSecurityToken(account: AzureAccount, tenantId: string, azureResource: azdata.AzureResource): Promise<Token | undefined> {
if (account.isStale === true) { if (account.isStale === true) {
Logger.error('Account was stale. No tokens being fetched.'); Logger.error('Account was stale. No tokens being fetched.');
return undefined; return undefined;
} }
const resource = this.resources.find(s => s.azureResourceId === azureResource); const resource = this.resources.find(s => s.azureResourceId === azureResource);
if (!resource) { if (!resource) {
Logger.error(`Unable to find Azure resource ${azureResource}`); Logger.error('Invalid resource, not fetching', azureResource);
return undefined; return undefined;
} }
@@ -233,7 +196,7 @@ export abstract class AzureAuth implements vscode.Disposable {
throw new AzureAuthError(localize('azure.tenantNotFound', "Specified tenant with ID '{0}' not found.", tenantId), `Tenant ${tenantId} not found.`, undefined); throw new AzureAuthError(localize('azure.tenantNotFound', "Specified tenant with ID '{0}' not found.", tenantId), `Tenant ${tenantId} not found.`, undefined);
} }
const cachedTokens = await this.getSavedTokenAdal(tenant, resource, account.key); const cachedTokens = await this.getSavedToken(tenant, resource, account.key);
// Let's check to see if we can just use the cached tokens to return to the user // Let's check to see if we can just use the cached tokens to return to the user
if (cachedTokens?.accessToken) { if (cachedTokens?.accessToken) {
@@ -250,7 +213,7 @@ export abstract class AzureAuth implements vscode.Disposable {
const maxTolerance = 2 * 60; // two minutes const maxTolerance = 2 * 60; // two minutes
if (remainingTime < maxTolerance) { if (remainingTime < maxTolerance) {
const result = await this.refreshTokenAdal(tenant, resource, cachedTokens.refreshToken); const result = await this.refreshToken(tenant, resource, cachedTokens.refreshToken);
if (result) { if (result) {
accessToken = result.accessToken; accessToken = result.accessToken;
expiresOn = Number(result.expiresOn); expiresOn = Number(result.expiresOn);
@@ -261,7 +224,7 @@ export abstract class AzureAuth implements vscode.Disposable {
return { return {
...accessToken, ...accessToken,
expiresOn: expiresOn, expiresOn: expiresOn,
tokenType: Constants.Bearer tokenType: 'Bearer'
}; };
} }
} }
@@ -271,7 +234,7 @@ export abstract class AzureAuth implements vscode.Disposable {
if (!this.metadata.settings.microsoftResource) { if (!this.metadata.settings.microsoftResource) {
throw new Error(localize('noMicrosoftResource', "Provider '{0}' does not have a Microsoft resource endpoint defined.", this.metadata.displayName)); throw new Error(localize('noMicrosoftResource', "Provider '{0}' does not have a Microsoft resource endpoint defined.", this.metadata.displayName));
} }
const baseTokens = await this.getSavedTokenAdal(this.commonTenant, this.metadata.settings.microsoftResource, account.key); const baseTokens = await this.getSavedToken(this.commonTenant, this.metadata.settings.microsoftResource, account.key);
if (!baseTokens) { if (!baseTokens) {
Logger.error('User had no base tokens for the basic resource registered. This should not happen and indicates something went wrong with the authentication cycle'); Logger.error('User had no base tokens for the basic resource registered. This should not happen and indicates something went wrong with the authentication cycle');
const msg = localize('azure.noBaseToken', 'Something failed with the authentication, or your tokens have been deleted from the system. Please try adding your account to Azure Data Studio again.'); const msg = localize('azure.noBaseToken', 'Something failed with the authentication, or your tokens have been deleted from the system. Please try adding your account to Azure Data Studio again.');
@@ -279,20 +242,20 @@ export abstract class AzureAuth implements vscode.Disposable {
throw new AzureAuthError(msg, 'No base token found', undefined); throw new AzureAuthError(msg, 'No base token found', undefined);
} }
// Let's try to convert the access token type, worst case we'll have to prompt the user to do an interactive authentication. // Let's try to convert the access token type, worst case we'll have to prompt the user to do an interactive authentication.
const result = await this.refreshTokenAdal(tenant, resource, baseTokens.refreshToken); const result = await this.refreshToken(tenant, resource, baseTokens.refreshToken);
if (result?.accessToken) { if (result?.accessToken) {
return { return {
...result.accessToken, ...result.accessToken,
expiresOn: Number(result.expiresOn), expiresOn: Number(result.expiresOn),
tokenType: Constants.Bearer tokenType: 'Bearer'
}; };
} }
return undefined; return undefined;
} }
protected abstract loginAdal(tenant: Tenant, resource: Resource): Promise<{ response: OAuthTokenResponse | undefined, authComplete: Deferred<void, Error> }>;
protected abstract loginMsal(tenant: Tenant, resource: Resource): Promise<{ response: AuthenticationResult | null, authComplete: Deferred<void, Error> }>;
protected abstract login(tenant: Tenant, resource: Resource): Promise<{ response: OAuthTokenResponse | undefined, authComplete: Deferred<void, Error> }>;
/** /**
* Refreshes a token, if a refreshToken is passed in then we use that. If it is not passed in then we will prompt the user for consent. * Refreshes a token, if a refreshToken is passed in then we use that. If it is not passed in then we will prompt the user for consent.
@@ -302,8 +265,8 @@ export abstract class AzureAuth implements vscode.Disposable {
* @returns The oauth token response or undefined. Undefined is returned when the user wants to ignore a tenant or chooses not to start the * @returns The oauth token response or undefined. Undefined is returned when the user wants to ignore a tenant or chooses not to start the
* re-authentication process for their tenant. * re-authentication process for their tenant.
*/ */
public async refreshTokenAdal(tenant: Tenant, resource: Resource, refreshToken: RefreshToken | undefined): Promise<OAuthTokenResponse | undefined> { public async refreshToken(tenant: Tenant, resource: Resource, refreshToken: RefreshToken | undefined): Promise<OAuthTokenResponse | undefined> {
Logger.piiSanitized('Refreshing token', [{ name: 'token', objOrArray: refreshToken }], []); Logger.pii('Refreshing token', [{ name: 'token', objOrArray: refreshToken }], []);
if (refreshToken) { if (refreshToken) {
const postData: RefreshTokenPostData = { const postData: RefreshTokenPostData = {
grant_type: 'refresh_token', grant_type: 'refresh_token',
@@ -312,114 +275,36 @@ export abstract class AzureAuth implements vscode.Disposable {
tenant: tenant.id, tenant: tenant.id,
resource: resource.endpoint resource: resource.endpoint
}; };
return this.getTokenAdal(tenant, resource, postData);
return this.getToken(tenant, resource, postData);
} }
return this.handleInteractionRequiredAdal(tenant, resource);
return this.handleInteractionRequired(tenant, resource);
} }
public async getToken(tenant: Tenant, resource: Resource, postData: AuthorizationCodePostData | TokenPostData | RefreshTokenPostData): Promise<OAuthTokenResponse | undefined> {
/** Logger.verbose('Fetching token');
* Gets the access token for the correct account and scope from the token cache, if the correct token doesn't exist in the token cache
* (i.e. expired token, wrong scope, etc.), sends a request for a new token using the refresh token
* @param accountId
* @param azureResource
* @returns The authentication result, including the access token
*/
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(`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 + '/';
let account: AccountInfo | null = await this.getAccountFromMsalCache(accountId);
if (!account) {
Logger.error('Error: Could not fetch account when acquiring token');
return null;
}
let newScope;
if (resource.azureResourceId === azdata.AzureResource.ResourceManagement) {
newScope = [`${endpoint}user_impersonation`];
} else {
newScope = [`${endpoint}.default`];
}
// construct request
// forceRefresh needs to be set true here in order to fetch the correct token for non-full tenants, due to this issue
// https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/3687
const tokenRequest = {
account: account,
authority: `${this.loginEndpointUrl}${tenantId}`,
scopes: newScope,
// Force Refresh when tenant is NOT full tenant or organizational id that this account belongs to.
forceRefresh: tenantId !== account.tenantId
};
try {
return await this.clientApplication.acquireTokenSilent(tokenRequest);
} catch (e) {
Logger.error('Failed to acquireTokenSilent', e);
if (e instanceof InteractionRequiredAuthError) {
// build refresh token request
const tenant: Tenant = {
id: tenantId,
displayName: ''
};
return this.handleInteractionRequiredMsal(tenant, resource);
} 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
}
}
}
}
public async getAccountFromMsalCache(accountId: string): Promise<AccountInfo | null> {
const cache = this.clientApplication.getTokenCache();
if (!cache) {
Logger.error('Error: Could not fetch token cache.');
return null;
}
let account: AccountInfo | null = null;
// if the accountId is a home ID, it will include a "." character
if (accountId.includes(".")) {
account = await cache.getAccountByHomeId(accountId);
} else {
account = await cache.getAccountByLocalId(accountId);
}
return account;
}
public async getTokenAdal(tenant: Tenant, resource: Resource, postData: AuthorizationCodePostData | TokenPostData | RefreshTokenPostData): Promise<OAuthTokenResponse | undefined> {
Logger.verbose('Fetching token for tenant {0}', tenant.id);
const tokenUrl = `${this.loginEndpointUrl}${tenant.id}/oauth2/token`; const tokenUrl = `${this.loginEndpointUrl}${tenant.id}/oauth2/token`;
const response = await this.makePostRequest(tokenUrl, postData); 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 }], []); [{ name: 'access token', value: response.data.access_token }, { name: 'refresh token', value: response.data.refresh_token }]);
if (response.data.error === 'interaction_required') { if (response.data.error === 'interaction_required') {
return this.handleInteractionRequiredAdal(tenant, resource); return this.handleInteractionRequired(tenant, resource);
} }
if (response.data.error) { if (response.data.error) {
Logger.error(`Response returned error : ${response.data}`); Logger.error('Response error!', response.data);
throw new AzureAuthError(localize('azure.responseError', "Token retrieval failed with an error. [Open developer tools]({0}) for more details.", 'command:workbench.action.toggleDevTools'), 'Token retrieval failed', undefined); throw new AzureAuthError(localize('azure.responseError', "Token retrieval failed with an error. [Open developer tools]({0}) for more details.", 'command:workbench.action.toggleDevTools'), 'Token retrieval failed', undefined);
} }
const accessTokenString = response.data.access_token; const accessTokenString = response.data.access_token;
const refreshTokenString = response.data.refresh_token; const refreshTokenString = response.data.refresh_token;
const expiresOnString = response.data.expires_on; const expiresOnString = response.data.expires_on;
return this.getTokenHelperAdal(tenant, resource, accessTokenString, refreshTokenString, expiresOnString);
return this.getTokenHelper(tenant, resource, accessTokenString, refreshTokenString, expiresOnString);
} }
public async getTokenHelperAdal(tenant: Tenant, resource: Resource, accessTokenString: string, refreshTokenString: string, expiresOnString: string): Promise<OAuthTokenResponse> { public async getTokenHelper(tenant: Tenant, resource: Resource, accessTokenString: string, refreshTokenString: string, expiresOnString: string): Promise<OAuthTokenResponse> {
if (!accessTokenString) { if (!accessTokenString) {
const msg = localize('azure.accessTokenEmpty', 'No access token returned from Microsoft OAuth'); const msg = localize('azure.accessTokenEmpty', 'No access token returned from Microsoft OAuth');
throw new AzureAuthError(msg, 'Access token was empty', undefined); throw new AzureAuthError(msg, 'Access token was empty', undefined);
@@ -464,69 +349,28 @@ export abstract class AzureAuth implements vscode.Disposable {
const accountKey: azdata.AccountKey = { const accountKey: azdata.AccountKey = {
providerId: this.metadata.id, providerId: this.metadata.id,
accountId: userKey, accountId: userKey
authLibrary: this._authLibrary
}; };
await this.saveTokenAdal(tenant, resource, accountKey, result); await this.saveToken(tenant, resource, accountKey, result);
return result; return result;
} }
public async getTenantsMsal(token: string): Promise<Tenant[]> {
const tenantUri = url.resolve(this.metadata.settings.armResource.endpoint, 'tenants?api-version=2019-11-01');
try {
Logger.verbose('Fetching tenants with uri {0}', tenantUri);
let tenantList: string[] = [];
const tenantResponse = await this.httpClient.sendGetRequestAsync<any>(tenantUri, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
}
});
const data = tenantResponse.body;
if (data.error) {
Logger.error(`Error fetching tenants :${data.error.code} - ${data.error.message}`);
throw new Error(`${data.error.code} - ${data.error.message}`);
}
const tenants: Tenant[] = data.value.map((tenantInfo: TenantResponse) => {
if (tenantInfo.displayName) {
tenantList.push(tenantInfo.displayName);
} else {
tenantList.push(tenantInfo.tenantId);
Logger.info('Tenant display name found empty: {0}', tenantInfo.tenantId);
}
return {
id: tenantInfo.tenantId,
displayName: tenantInfo.displayName ? tenantInfo.displayName : tenantInfo.tenantId,
userId: token,
tenantCategory: tenantInfo.tenantCategory
} as Tenant;
});
Logger.verbose(`Tenants: ${tenantList}`);
const homeTenantIndex = tenants.findIndex(tenant => tenant.tenantCategory === Constants.HomeCategory);
// remove home tenant from list of tenants
if (homeTenantIndex >= 0) {
const homeTenant = tenants.splice(homeTenantIndex, 1);
tenants.unshift(homeTenant[0]);
}
Logger.verbose(`Filtered Tenants: ${tenantList}`);
return tenants;
} catch (ex) {
Logger.error(`Error fetching tenants :${ex}`);
throw ex;
}
}
//#region tenant calls //#region tenant calls
public async getTenantsAdal(token: AccessToken): Promise<Tenant[]> { public async getTenants(token: AccessToken): Promise<Tenant[]> {
interface TenantResponse { // https://docs.microsoft.com/en-us/rest/api/resources/tenants/list
id: string
tenantId: string
displayName?: string
tenantCategory?: string
}
const tenantUri = url.resolve(this.metadata.settings.armResource.endpoint, 'tenants?api-version=2019-11-01'); const tenantUri = url.resolve(this.metadata.settings.armResource.endpoint, 'tenants?api-version=2019-11-01');
try { try {
Logger.verbose('Fetching tenants with URI: {0}', tenantUri); Logger.verbose('Fetching tenants', tenantUri);
let tenantList: string[] = [];
const tenantResponse = await this.makeGetRequest(tenantUri, token.token); const tenantResponse = await this.makeGetRequest(tenantUri, token.token);
if (tenantResponse.status !== 200) { if (tenantResponse.status !== 200) {
Logger.error(`Error with tenant response, status: ${tenantResponse.status} | status text: ${tenantResponse.statusText}`); Logger.error(`Error with tenant response, status: ${tenantResponse.status} | status text: ${tenantResponse.statusText}`);
@@ -534,22 +378,16 @@ export abstract class AzureAuth implements vscode.Disposable {
throw new Error('Error with tenant response'); throw new Error('Error with tenant response');
} }
const tenants: Tenant[] = tenantResponse.data.value.map((tenantInfo: TenantResponse) => { const tenants: Tenant[] = tenantResponse.data.value.map((tenantInfo: TenantResponse) => {
if (tenantInfo.displayName) { Logger.verbose(`Tenant: ${tenantInfo.displayName}`);
tenantList.push(tenantInfo.displayName);
} else {
tenantList.push(tenantInfo.tenantId);
Logger.info('Tenant display name found empty: {0}', tenantInfo.tenantId);
}
return { return {
id: tenantInfo.tenantId, id: tenantInfo.tenantId,
displayName: tenantInfo.displayName ? tenantInfo.displayName : tenantInfo.tenantId, displayName: tenantInfo.displayName ? tenantInfo.displayName : localize('azureWorkAccountDisplayName', "Work or school account"),
userId: token.key, userId: token.key,
tenantCategory: tenantInfo.tenantCategory tenantCategory: tenantInfo.tenantCategory
} as Tenant; } as Tenant;
}); });
Logger.verbose(`Tenants: ${tenantList}`);
const homeTenantIndex = tenants.findIndex(tenant => tenant.tenantCategory === Constants.HomeCategory); const homeTenantIndex = tenants.findIndex(tenant => tenant.tenantCategory === 'Home');
// remove home tenant from list of tenants
if (homeTenantIndex >= 0) { if (homeTenantIndex >= 0) {
const homeTenant = tenants.splice(homeTenantIndex, 1); const homeTenant = tenants.splice(homeTenantIndex, 1);
tenants.unshift(homeTenant[0]); tenants.unshift(homeTenant[0]);
@@ -565,16 +403,16 @@ export abstract class AzureAuth implements vscode.Disposable {
//#endregion //#endregion
//#region token management //#region token management
private async saveTokenAdal(tenant: Tenant, resource: Resource, accountKey: azdata.AccountKey, { accessToken, refreshToken, expiresOn }: OAuthTokenResponse) { private async saveToken(tenant: Tenant, resource: Resource, accountKey: azdata.AccountKey, { accessToken, refreshToken, expiresOn }: OAuthTokenResponse) {
const msg = localize('azure.cacheErrorAdd', "Error when adding your account to the cache."); const msg = localize('azure.cacheErrorAdd', "Error when adding your account to the cache.");
if (!tenant.id || !resource.id) { if (!tenant.id || !resource.id) {
Logger.piiSanitized('Tenant ID or resource ID was undefined', [], [], tenant, resource); Logger.pii('Tenant ID or resource ID was undefined', [], [], tenant, resource);
throw new AzureAuthError(msg, 'Adding account to cache failed', undefined); throw new AzureAuthError(msg, 'Adding account to cache failed', undefined);
} }
try { try {
Logger.piiSanitized(`Saving access token`, [{ name: 'access_token', objOrArray: accessToken }], []); Logger.pii(`Saving access token`, [{ name: 'access_token', objOrArray: accessToken }], []);
await this.tokenCache.saveCredential(`${accountKey.accountId}_access_${resource.id}_${tenant.id}`, JSON.stringify(accessToken)); await this.tokenCache.saveCredential(`${accountKey.accountId}_access_${resource.id}_${tenant.id}`, JSON.stringify(accessToken));
Logger.piiSanitized(`Saving refresh token`, [{ name: 'refresh_token', objOrArray: refreshToken }], []); Logger.pii(`Saving refresh token`, [{ name: 'refresh_token', objOrArray: refreshToken }], []);
await this.tokenCache.saveCredential(`${accountKey.accountId}_refresh_${resource.id}_${tenant.id}`, JSON.stringify(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); this.memdb.set(`${accountKey.accountId}_${tenant.id}_${resource.id}`, expiresOn);
} catch (ex) { } catch (ex) {
@@ -583,12 +421,12 @@ export abstract class AzureAuth implements vscode.Disposable {
} }
} }
public async getSavedTokenAdal(tenant: Tenant, resource: Resource, accountKey: azdata.AccountKey): Promise<{ accessToken: AccessToken, refreshToken: RefreshToken | undefined, expiresOn: string } | undefined> { public async getSavedToken(tenant: Tenant, resource: Resource, accountKey: azdata.AccountKey): Promise<{ accessToken: AccessToken, refreshToken: RefreshToken | undefined, expiresOn: string } | undefined> {
const getMsg = localize('azure.cacheErrorGet', "Error when getting your account from the cache"); const getMsg = localize('azure.cacheErrorGet', "Error when getting your account from the cache");
const parseMsg = localize('azure.cacheErrorParse', "Error when parsing your account from the cache"); const parseMsg = localize('azure.cacheErrorParse', "Error when parsing your account from the cache");
if (!tenant.id || !resource.id) { if (!tenant.id || !resource.id) {
Logger.piiSanitized('Tenant ID or resource ID was undefined', [], [], tenant, resource); Logger.pii('Tenant ID or resource ID was undefined', [], [], tenant, resource);
throw new AzureAuthError(getMsg, 'Getting account from cache failed', undefined); throw new AzureAuthError(getMsg, 'Getting account from cache failed', undefined);
} }
@@ -615,7 +453,7 @@ export abstract class AzureAuth implements vscode.Disposable {
if (refreshTokenString) { if (refreshTokenString) {
refreshToken = JSON.parse(refreshTokenString); refreshToken = JSON.parse(refreshTokenString);
} }
Logger.piiSanitized('GetSavedToken ', [{ name: 'access', objOrArray: accessToken }, { name: 'refresh', objOrArray: refreshToken }], [], `expiresOn=${expiresOn}`); Logger.pii('GetSavedToken ', [{ name: 'access', objOrArray: accessToken }, { name: 'refresh', objOrArray: refreshToken }], [], `expiresOn=${expiresOn}`);
return { return {
accessToken, refreshToken, expiresOn accessToken, refreshToken, expiresOn
}; };
@@ -626,22 +464,12 @@ export abstract class AzureAuth implements vscode.Disposable {
} }
//#endregion //#endregion
//#region interaction handling //#region interaction handling
public async handleInteractionRequiredMsal(tenant: Tenant, resource: Resource): Promise<AuthenticationResult | null> {
const shouldOpen = await this.askUserForInteraction(tenant, resource);
if (shouldOpen) {
const result = await this.loginMsal(tenant, resource);
result?.authComplete?.resolve();
return result?.response;
}
return null;
}
public async handleInteractionRequiredAdal(tenant: Tenant, resource: Resource): Promise<OAuthTokenResponse | undefined> { public async handleInteractionRequired(tenant: Tenant, resource: Resource): Promise<OAuthTokenResponse | undefined> {
const shouldOpen = await this.askUserForInteraction(tenant, resource); const shouldOpen = await this.askUserForInteraction(tenant, resource);
if (shouldOpen) { if (shouldOpen) {
const result = await this.loginAdal(tenant, resource); const result = await this.login(tenant, resource);
result?.authComplete?.resolve(); result?.authComplete?.resolve();
return result?.response; return result?.response;
} }
@@ -659,14 +487,13 @@ export abstract class AzureAuth implements vscode.Disposable {
} }
const getTenantConfigurationSet = (): Set<string> => { const getTenantConfigurationSet = (): Set<string> => {
const configuration = vscode.workspace.getConfiguration(Constants.AzureTenantConfigSection); const configuration = vscode.workspace.getConfiguration('azure.tenant.config');
let values: string[] = configuration.get('filter') ?? []; let values: string[] = configuration.get('filter') ?? [];
return new Set<string>(values); return new Set<string>(values);
}; };
// The user wants to ignore this tenant. // The user wants to ignore this tenant.
if (getTenantConfigurationSet().has(tenant.id)) { if (getTenantConfigurationSet().has(tenant.id)) {
Logger.info(`Tenant ${tenant.id} found in the ignore list, authentication will not be attempted.`);
return false; return false;
} }
@@ -701,7 +528,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.endpoint); 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 result = await vscode.window.showInformationMessage(messageBody, { modal: true }, openItem, closeItem, dontAskAgainItem); const result = await vscode.window.showInformationMessage(messageBody, { modal: true }, openItem, closeItem, dontAskAgainItem);
if (result?.action) { if (result?.action) {
@@ -715,33 +542,28 @@ export abstract class AzureAuth implements vscode.Disposable {
//#region data modeling //#region data modeling
public createAccount(tokenClaims: TokenClaims, key: string, tenants: Tenant[]): AzureAccount { public createAccount(tokenClaims: TokenClaims, key: string, tenants: Tenant[]): AzureAccount {
Logger.verbose(`Token Claims acccount: ${tokenClaims.name}, TID: ${tokenClaims.tid}`); Logger.verbose(`Token Claims: ${tokenClaims.name}`);
tenants.forEach((tenant) => { tenants.forEach((tenant) => {
Logger.verbose(`Tenant ID: ${tenant.id}, Tenant Name: ${tenant.displayName}`); Logger.verbose(
`Tenant ID: ${tenant.id}
Tenant Name: ${tenant.displayName}`);
}); });
// Determine if this is a microsoft account // Determine if this is a microsoft account
let accountIssuer = 'unknown'; let accountIssuer = 'unknown';
if (tokenClaims.iss === 'https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/' || if (tokenClaims.iss === 'https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/') {
tokenClaims.iss === `${this.loginEndpointUrl}72f988bf-86f1-41af-91ab-2d7cd011db47/v2.0`) { accountIssuer = 'corp';
accountIssuer = Constants.AccountIssuer.Corp;
} }
if (tokenClaims?.idp === 'live.com') { if (tokenClaims?.idp === 'live.com') {
accountIssuer = Constants.AccountIssuer.Msft; accountIssuer = 'msft';
} }
const name = tokenClaims.name ?? tokenClaims.preferred_username ?? tokenClaims.email ?? tokenClaims.unique_name; const name = tokenClaims.name ?? tokenClaims.email ?? tokenClaims.unique_name;
const email = tokenClaims.preferred_username ?? tokenClaims.email ?? tokenClaims.unique_name; const email = tokenClaims.email ?? tokenClaims.unique_name;
let owningTenant: Tenant = this.commonTenant; // default to common tenant
// Read more about tid > https://learn.microsoft.com/azure/active-directory/develop/id-tokens // Read more about tid > https://learn.microsoft.com/azure/active-directory/develop/id-tokens
if (tokenClaims.tid) { const owningTenant = tenants.find(t => t.id === tokenClaims.tid)
owningTenant = tenants.find(t => t.id === tokenClaims.tid) ?? { 'id': tokenClaims.tid, 'displayName': 'Microsoft Account' }; ?? { 'id': tokenClaims.tid, 'displayName': 'Microsoft Account' };
} else {
Logger.info('Could not find tenant information from tokenClaims, falling back to common Tenant.');
}
let displayName = name; let displayName = name;
if (email) { if (email) {
@@ -750,26 +572,25 @@ export abstract class AzureAuth implements vscode.Disposable {
let contextualDisplayName: string; let contextualDisplayName: string;
switch (accountIssuer) { switch (accountIssuer) {
case Constants.AccountIssuer.Corp: case 'corp':
contextualDisplayName = localize('azure.microsoftCorpAccount', "Microsoft Corp"); contextualDisplayName = localize('azure.microsoftCorpAccount', "Microsoft Corp");
break; break;
case Constants.AccountIssuer.Msft: case 'msft':
contextualDisplayName = localize('azure.microsoftAccountDisplayName', 'Microsoft Account'); contextualDisplayName = localize('azure.microsoftAccountDisplayName', 'Microsoft Account');
break; break;
default: default:
contextualDisplayName = displayName; contextualDisplayName = displayName;
} }
let accountType = accountIssuer === Constants.AccountIssuer.Msft let accountType = accountIssuer === 'msft'
? Constants.AccountType.Microsoft ? this.MicrosoftAccountType
: Constants.AccountType.WorkSchool; : this.WorkSchoolAccountType;
const account = { const account = {
key: { key: {
providerId: this.metadata.id, providerId: this.metadata.id,
accountId: key, accountId: key,
accountVersion: Constants.AccountVersion, accountVersion: AzureAuth.ACCOUNT_VERSION,
authLibrary: this._authLibrary
}, },
name: displayName, name: displayName,
displayInfo: { displayInfo: {
@@ -782,7 +603,7 @@ export abstract class AzureAuth implements vscode.Disposable {
}, },
properties: { properties: {
providerSettings: this.metadata, providerSettings: this.metadata,
isMsAccount: accountIssuer === Constants.AccountIssuer.Msft, isMsAccount: accountIssuer === 'msft',
owningTenant: owningTenant, owningTenant: owningTenant,
tenants, tenants,
azureAuthType: this.authType azureAuthType: this.authType
@@ -806,7 +627,7 @@ export abstract class AzureAuth implements vscode.Disposable {
// Intercept response and print out the response for future debugging // Intercept response and print out the response for future debugging
const response = await axios.post(url, qs.stringify(postData), config); const response = await axios.post(url, qs.stringify(postData), config);
Logger.piiSanitized('POST request ', [{ name: 'data', objOrArray: postData }, { name: 'response', objOrArray: response.data }], [], url); Logger.pii('POST request ', [{ name: 'data', objOrArray: postData }, { name: 'response', objOrArray: response.data }], [], url);
return response; return response;
} }
@@ -820,7 +641,7 @@ export abstract class AzureAuth implements vscode.Disposable {
}; };
const response = await axios.get(url, config); const response = await axios.get(url, config);
Logger.piiSanitized('GET request ', [{ name: 'response', objOrArray: response.data.value ?? response.data }], [], url,); Logger.pii('GET request ', [{ name: 'response', objOrArray: response.data.value ?? response.data }], [], url,);
return response; return response;
} }
@@ -839,10 +660,8 @@ export abstract class AzureAuth implements vscode.Disposable {
protected toBase64UrlEncoding(base64string: string): string { protected toBase64UrlEncoding(base64string: string): string {
return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding
} }
public async deleteAllCacheMsal(): Promise<void> {
this.clientApplication.clearCache(); public async deleteAllCache(): Promise<void> {
}
public async deleteAllCacheAdal(): Promise<void> {
const results = await this.tokenCache.findCredentials(''); const results = await this.tokenCache.findCredentials('');
for (let { account } of results) { for (let { account } of results) {
@@ -852,34 +671,17 @@ export abstract class AzureAuth implements vscode.Disposable {
public async clearCredentials(account: azdata.AccountKey): Promise<void> { public async clearCredentials(account: azdata.AccountKey): Promise<void> {
try { try {
// remove account based on authLibrary field, accounts added before this field was present will default to return this.deleteAccountCache(account);
// ADAL method of account removal
if (account.authLibrary === Constants.AuthLibrary.MSAL) {
return await this.deleteAccountCacheMsal(account);
} else { // fallback to ADAL by default
return await this.deleteAccountCacheAdal(account);
}
} catch (ex) { } catch (ex) {
// We need not prompt user for error if token could not be removed from cache. const msg = localize('azure.cacheErrrorRemove', "Error when removing your account from the cache.");
Logger.error('Error when removing token from cache: ', ex); void vscode.window.showErrorMessage(msg);
Logger.error('Error when removing tokens.', ex);
} }
} }
public async deleteAccountCacheMsal(account: azdata.AccountKey): Promise<void> { public async deleteAccountCache(account: azdata.AccountKey): Promise<void> {
const tokenCache = this.clientApplication.getTokenCache();
let msalAccount: AccountInfo | null = await this.getAccountFromMsalCache(account.accountId);
if (!msalAccount) {
Logger.error(`MSAL: Unable to find account ${account.accountId} for removal`);
throw Error(`Unable to find account ${account.accountId}`);
}
await tokenCache.removeAccount(msalAccount);
}
public async deleteAccountCacheAdal(account: azdata.AccountKey): Promise<void> {
const results = await this.tokenCache.findCredentials(account.accountId); const results = await this.tokenCache.findCredentials(account.accountId);
if (!results) {
Logger.error('ADAL: Unable to find account for removal');
}
for (let { account } of results) { for (let { account } of results) {
await this.tokenCache.clearCredential(account); await this.tokenCache.clearCredential(account);
} }
@@ -920,13 +722,6 @@ export interface RefreshToken extends AccountKey {
key: string key: string
} }
export interface TenantResponse { // https://docs.microsoft.com/en-us/rest/api/resources/tenants/list
id: string
tenantId: string
displayName?: string
tenantCategory?: string
}
export interface MultiTenantTokenResponse { export interface MultiTenantTokenResponse {
[tenantId: string]: Token | undefined; [tenantId: string]: Token | undefined;
} }

View File

@@ -8,17 +8,15 @@ import { AzureAccountProviderMetadata, AzureAuthType, Resource, Tenant } from 'a
import { Deferred } from '../interfaces'; import { Deferred } from '../interfaces';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as crypto from 'crypto'; import * as crypto from 'crypto';
import { SimpleTokenCache } from '../utils/simpleTokenCache'; import { SimpleTokenCache } from '../simpleTokenCache';
import { SimpleWebServer } from '../utils/simpleWebServer'; import { SimpleWebServer } from '../utils/simpleWebServer';
import { AzureAuthError } from './azureAuthError'; import { AzureAuthError } from './azureAuthError';
import { Logger } from '../../utils/Logger'; import { Logger } from '../../utils/Logger';
import * as Constants from '../../constants';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
import * as path from 'path'; import * as path from 'path';
import * as http from 'http'; import * as http from 'http';
import * as qs from 'qs'; import * as qs from 'qs';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
import { PublicClientApplication, CryptoProvider, AuthorizationUrlRequest, AuthorizationCodeRequest, AuthenticationResult } from '@azure/msal-node';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
@@ -30,43 +28,33 @@ interface AuthCodeResponse {
interface CryptoValues { interface CryptoValues {
nonce: string; nonce: string;
challengeMethod: string;
codeVerifier: string; codeVerifier: string;
codeChallenge: string; codeChallenge: string;
} }
export class AzureAuthCodeGrant extends AzureAuth { export class AzureAuthCodeGrant extends AzureAuth {
private static readonly USER_FRIENDLY_NAME: string = localize('azure.azureAuthCodeGrantName', 'Azure Auth Code Grant'); private static readonly USER_FRIENDLY_NAME: string = localize('azure.azureAuthCodeGrantName', 'Azure Auth Code Grant');
private cryptoProvider: CryptoProvider;
private pkceCodes: CryptoValues;
constructor( constructor(
metadata: AzureAccountProviderMetadata, metadata: AzureAccountProviderMetadata,
tokenCache: SimpleTokenCache, tokenCache: SimpleTokenCache,
context: vscode.ExtensionContext, context: vscode.ExtensionContext,
uriEventEmitter: vscode.EventEmitter<vscode.Uri>, uriEventEmitter: vscode.EventEmitter<vscode.Uri>,
clientApplication: PublicClientApplication,
authLibrary: string
) { ) {
super(metadata, tokenCache, context, clientApplication, uriEventEmitter, AzureAuthType.AuthCodeGrant, AzureAuthCodeGrant.USER_FRIENDLY_NAME, authLibrary); super(metadata, tokenCache, context, uriEventEmitter, AzureAuthType.AuthCodeGrant, AzureAuthCodeGrant.USER_FRIENDLY_NAME);
this.cryptoProvider = new CryptoProvider();
this.pkceCodes = {
nonce: '',
challengeMethod: Constants.S256_CODE_CHALLENGE_METHOD, // Use SHA256 as the challenge method
codeVerifier: '', // Generate a code verifier for the Auth Code Request first
codeChallenge: '', // Generate a code challenge from the previously generated code verifier
};
} }
protected async loginAdal(tenant: Tenant, resource: Resource): Promise<{ response: OAuthTokenResponse | undefined, authComplete: Deferred<void, Error> }> {
protected async login(tenant: Tenant, resource: Resource): Promise<{ response: OAuthTokenResponse | undefined, authComplete: Deferred<void, Error> }> {
let authCompleteDeferred: Deferred<void, Error>; let authCompleteDeferred: Deferred<void, Error>;
let authCompletePromise = new Promise<void>((resolve, reject) => authCompleteDeferred = { resolve, reject }); let authCompletePromise = new Promise<void>((resolve, reject) => authCompleteDeferred = { resolve, reject });
let authResponse: AuthCodeResponse; let authResponse: AuthCodeResponse;
if (vscode.env.uiKind === vscode.UIKind.Web) { if (vscode.env.uiKind === vscode.UIKind.Web) {
authResponse = await this.loginWebAdal(tenant, resource); authResponse = await this.loginWeb(tenant, resource);
} else { } else {
authResponse = await this.loginDesktopAdal(tenant, resource, authCompletePromise); authResponse = await this.loginDesktop(tenant, resource, authCompletePromise);
} }
return { return {
@@ -75,30 +63,6 @@ export class AzureAuthCodeGrant extends AzureAuth {
}; };
} }
protected async loginMsal(tenant: Tenant, resource: Resource): Promise<{ response: AuthenticationResult | null, authComplete: Deferred<void, Error> }> {
let authCompleteDeferred: Deferred<void, Error>;
let authCompletePromise = new Promise<void>((resolve, reject) => authCompleteDeferred = { resolve, reject });
let authCodeRequest: AuthorizationCodeRequest;
if (vscode.env.uiKind === vscode.UIKind.Web) {
authCodeRequest = await this.loginWebMsal(tenant, resource);
} else {
authCodeRequest = await this.loginDesktopMsal(tenant, resource, authCompletePromise);
}
let result = await this.clientApplication.acquireTokenByCode(authCodeRequest);
if (!result) {
Logger.error('Failed to acquireTokenByCode');
Logger.error(`Auth Code Request: ${JSON.stringify(authCodeRequest)}`)
throw Error('Failed to fetch token using auth code');
} else {
return {
response: result,
authComplete: authCompleteDeferred!
};
}
}
/** /**
* Requests an OAuthTokenResponse from Microsoft OAuth * Requests an OAuthTokenResponse from Microsoft OAuth
* *
@@ -115,47 +79,12 @@ export class AzureAuthCodeGrant extends AzureAuth {
resource: resource.endpoint resource: resource.endpoint
}; };
return this.getTokenAdal(tenant, resource, postData); return this.getToken(tenant, resource, postData);
} }
private async loginWebMsal(tenant: Tenant, resource: Resource): Promise<AuthorizationCodeRequest> { private async loginWeb(tenant: Tenant, resource: Resource): Promise<AuthCodeResponse> {
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://microsoft.azurecore`)); const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://microsoft.azurecore`));
await this.createCryptoValuesMsal(); const { nonce, codeVerifier, codeChallenge } = this.createCryptoValues();
const port = (callbackUri.authority.match(/:([0-9]*)$/) || [])[1] || (callbackUri.scheme === 'https' ? 443 : 80);
const state = `${port},${encodeURIComponent(this.pkceCodes.nonce)},${encodeURIComponent(callbackUri.query)}`;
try {
let authUrlRequest: AuthorizationUrlRequest;
authUrlRequest = {
scopes: this.scopes,
redirectUri: this.redirectUri,
codeChallenge: this.pkceCodes.codeChallenge,
codeChallengeMethod: this.pkceCodes.challengeMethod,
prompt: Constants.SELECT_ACCOUNT,
state: state
};
let authCodeRequest: AuthorizationCodeRequest;
authCodeRequest = {
scopes: this.scopes,
redirectUri: this.redirectUri,
codeVerifier: this.pkceCodes.codeVerifier,
code: ''
};
let authCodeUrl = await this.clientApplication.getAuthCodeUrl(authUrlRequest);
await vscode.env.openExternal(vscode.Uri.parse(authCodeUrl));
const authCode = await this.handleWebResponse(state);
authCodeRequest.code = authCode;
return authCodeRequest;
} catch (e) {
Logger.error('MSAL: Error requesting auth code', e);
throw new AzureAuthError('error', 'Error requesting auth code', e);
}
}
private async loginWebAdal(tenant: Tenant, resource: Resource): Promise<AuthCodeResponse> {
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://microsoft.azurecore`));
const { nonce, codeVerifier, codeChallenge } = this.createCryptoValuesAdal();
const port = (callbackUri.authority.match(/:([0-9]*)$/) || [])[1] || (callbackUri.scheme === 'https' ? 443 : 80); const port = (callbackUri.authority.match(/:([0-9]*)$/) || [])[1] || (callbackUri.scheme === 'https' ? 443 : 80);
const state = `${port},${encodeURIComponent(nonce)},${encodeURIComponent(callbackUri.query)}`; const state = `${port},${encodeURIComponent(nonce)},${encodeURIComponent(callbackUri.query)}`;
@@ -165,8 +94,8 @@ export class AzureAuthCodeGrant extends AzureAuth {
client_id: this.clientId, client_id: this.clientId,
redirect_uri: this.redirectUri, redirect_uri: this.redirectUri,
state, state,
prompt: Constants.SELECT_ACCOUNT, prompt: 'select_account',
code_challenge_method: Constants.S256_CODE_CHALLENGE_METHOD, code_challenge_method: 'S256',
code_challenge: codeChallenge, code_challenge: codeChallenge,
resource: resource.id resource: resource.id
}; };
@@ -212,7 +141,7 @@ export class AzureAuthCodeGrant extends AzureAuth {
}, {}); }, {});
} }
private async loginDesktopMsal(tenant: Tenant, resource: Resource, authCompletePromise: Promise<void>): Promise<AuthorizationCodeRequest> { private async loginDesktop(tenant: Tenant, resource: Resource, authCompletePromise: Promise<void>): Promise<AuthCodeResponse> {
const server = new SimpleWebServer(); const server = new SimpleWebServer();
let serverPort: string; let serverPort: string;
@@ -222,56 +151,7 @@ export class AzureAuthCodeGrant extends AzureAuth {
const msg = localize('azure.serverCouldNotStart', 'Server could not start. This could be a permissions error or an incompatibility on your system. You can try enabling device code authentication from settings.'); const msg = localize('azure.serverCouldNotStart', 'Server could not start. This could be a permissions error or an incompatibility on your system. You can try enabling device code authentication from settings.');
throw new AzureAuthError(msg, 'Server could not start', ex); throw new AzureAuthError(msg, 'Server could not start', ex);
} }
await this.createCryptoValuesMsal(); const { nonce, codeVerifier, codeChallenge } = this.createCryptoValues();
const state = `${serverPort},${this.pkceCodes.nonce}`;
try {
let authUrlRequest: AuthorizationUrlRequest;
authUrlRequest = {
scopes: this.scopes,
redirectUri: `${this.redirectUri}:${serverPort}/redirect`,
codeChallenge: this.pkceCodes.codeChallenge,
codeChallengeMethod: this.pkceCodes.challengeMethod,
prompt: Constants.SELECT_ACCOUNT,
authority: `${this.loginEndpointUrl}${tenant.id}`,
state: state
};
let authCodeRequest: AuthorizationCodeRequest;
authCodeRequest = {
scopes: this.scopes,
redirectUri: `${this.redirectUri}:${serverPort}/redirect`,
codeVerifier: this.pkceCodes.codeVerifier,
authority: `${this.loginEndpointUrl}${tenant.id}`,
code: ''
};
let authCodeUrl = await this.clientApplication.getAuthCodeUrl(authUrlRequest);
await vscode.env.openExternal(vscode.Uri.parse(`http://localhost:${serverPort}/signin?nonce=${encodeURIComponent(this.pkceCodes.nonce)}`));
const authCode = await this.addServerListeners(server, this.pkceCodes.nonce, authCodeUrl, authCompletePromise);
authCodeRequest.code = authCode;
return authCodeRequest;
}
catch (e) {
Logger.error('MSAL: Error requesting auth code', e);
throw new AzureAuthError('error', 'Error requesting auth code', e);
}
}
private async loginDesktopAdal(tenant: Tenant, resource: Resource, authCompletePromise: Promise<void>): Promise<AuthCodeResponse> {
const server = new SimpleWebServer();
let serverPort: string;
try {
serverPort = await server.startup();
} catch (ex) {
const msg = localize('azure.serverCouldNotStart', 'Server could not start. This could be a permissions error or an incompatibility on your system. You can try enabling device code authentication from settings.');
throw new AzureAuthError(msg, 'Server could not start', ex);
}
const { nonce, codeVerifier, codeChallenge } = this.createCryptoValuesAdal();
const state = `${serverPort},${encodeURIComponent(nonce)}`; const state = `${serverPort},${encodeURIComponent(nonce)}`;
const loginQuery = { const loginQuery = {
response_type: 'code', response_type: 'code',
@@ -279,8 +159,8 @@ export class AzureAuthCodeGrant extends AzureAuth {
client_id: this.clientId, client_id: this.clientId,
redirect_uri: `${this.redirectUri}:${serverPort}/redirect`, redirect_uri: `${this.redirectUri}:${serverPort}/redirect`,
state, state,
prompt: Constants.SELECT_ACCOUNT, prompt: 'select_account',
code_challenge_method: Constants.S256_CODE_CHALLENGE_METHOD, code_challenge_method: 'S256',
code_challenge: codeChallenge, code_challenge: codeChallenge,
resource: resource.endpoint resource: resource.endpoint
}; };
@@ -392,21 +272,13 @@ export class AzureAuthCodeGrant extends AzureAuth {
} }
private createCryptoValuesAdal(): CryptoValues { private createCryptoValues(): CryptoValues {
const nonce = crypto.randomBytes(16).toString('base64'); const nonce = crypto.randomBytes(16).toString('base64');
const codeVerifier = this.toBase64UrlEncoding(crypto.randomBytes(32).toString('base64')); const codeVerifier = this.toBase64UrlEncoding(crypto.randomBytes(32).toString('base64'));
const codeChallenge = this.toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64')); const codeChallenge = this.toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64'));
const challengeMethod = '';
return { return {
nonce, challengeMethod, codeVerifier, codeChallenge nonce, codeVerifier, codeChallenge
}; };
} }
private async createCryptoValuesMsal(): Promise<void> {
this.pkceCodes.nonce = this.cryptoProvider.createNewGuid();
const { verifier, challenge } = await this.cryptoProvider.generatePkceCodes();
this.pkceCodes.codeVerifier = verifier;
this.pkceCodes.codeChallenge = challenge;
}
} }

View File

@@ -13,6 +13,7 @@ import {
DeviceCodeCheckPostData, DeviceCodeCheckPostData,
} from './azureAuth'; } from './azureAuth';
import { import {
AzureAccountProviderMetadata, AzureAccountProviderMetadata,
AzureAuthType, AzureAuthType,
@@ -20,10 +21,8 @@ import {
Resource Resource
} from 'azurecore'; } from 'azurecore';
import { Deferred } from '../interfaces'; 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'; import { Logger } from '../../utils/Logger';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
interface DeviceCodeLogin { // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code interface DeviceCodeLogin { // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code
@@ -51,34 +50,12 @@ export class AzureDeviceCode extends AzureAuth {
tokenCache: SimpleTokenCache, tokenCache: SimpleTokenCache,
context: vscode.ExtensionContext, context: vscode.ExtensionContext,
uriEventEmitter: vscode.EventEmitter<vscode.Uri>, uriEventEmitter: vscode.EventEmitter<vscode.Uri>,
clientApplication: PublicClientApplication,
authLibrary: string
) { ) {
super(metadata, tokenCache, context, clientApplication, uriEventEmitter, AzureAuthType.DeviceCode, AzureDeviceCode.USER_FRIENDLY_NAME, authLibrary); super(metadata, tokenCache, context, uriEventEmitter, AzureAuthType.DeviceCode, AzureDeviceCode.USER_FRIENDLY_NAME);
this.pageTitle = localize('addAccount', "Add {0} account", this.metadata.displayName); this.pageTitle = localize('addAccount', "Add {0} account", this.metadata.displayName);
} }
protected async login(tenant: Tenant, resource: Resource): Promise<{ response: OAuthTokenResponse, authComplete: Deferred<void, Error> }> {
protected async loginMsal(tenant: Tenant, resource: Resource): Promise<{ response: AuthenticationResult | null, authComplete: Deferred<void, Error> }> {
let authCompleteDeferred: Deferred<void, Error>;
let authCompletePromise = new Promise<void>((resolve, reject) => authCompleteDeferred = { resolve, reject });
const deviceCodeRequest: DeviceCodeRequest = {
scopes: this.scopes,
authority: `${this.loginEndpointUrl}${tenant.id}`,
deviceCodeCallback: async (response) => {
await azdata.accounts.beginAutoOAuthDeviceCode(this.metadata.id, this.pageTitle, response.message, response.userCode, response.verificationUri);
}
};
const authResult = await this.clientApplication.acquireTokenByDeviceCode(deviceCodeRequest);
this.closeOnceComplete(authCompletePromise).catch(Logger.error);
return {
response: authResult,
authComplete: authCompleteDeferred!
};
}
protected async loginAdal(tenant: Tenant, resource: Resource): Promise<{ response: OAuthTokenResponse, authComplete: Deferred<void, Error> }> {
let authCompleteDeferred: Deferred<void, Error>; let authCompleteDeferred: Deferred<void, Error>;
let authCompletePromise = new Promise<void>((resolve, reject) => authCompleteDeferred = { resolve, reject }); let authCompletePromise = new Promise<void>((resolve, reject) => authCompleteDeferred = { resolve, reject });
@@ -102,7 +79,7 @@ export class AzureDeviceCode extends AzureAuth {
const currentTime = new Date().getTime() / 1000; const currentTime = new Date().getTime() / 1000;
const expiresOn = `${currentTime + finalDeviceLogin.expires_in}`; const expiresOn = `${currentTime + finalDeviceLogin.expires_in}`;
const result = await this.getTokenHelperAdal(tenant, resource, accessTokenString, refreshTokenString, expiresOn); const result = await this.getTokenHelper(tenant, resource, accessTokenString, refreshTokenString, expiresOn);
this.closeOnceComplete(authCompletePromise).catch(Logger.error); this.closeOnceComplete(authCompletePromise).catch(Logger.error);
return { return {
@@ -116,6 +93,7 @@ export class AzureDeviceCode extends AzureAuth {
azdata.accounts.endAutoOAuthDeviceCode(); azdata.accounts.endAutoOAuthDeviceCode();
} }
private setupPolling(info: DeviceCodeLogin): Promise<DeviceCodeLoginResult> { private setupPolling(info: DeviceCodeLogin): Promise<DeviceCodeLoginResult> {
const timeoutMessage = localize('azure.timeoutDeviceCode', 'Timed out when waiting for device code login.'); const timeoutMessage = localize('azure.timeoutDeviceCode', 'Timed out when waiting for device code login.');
const fiveMinutes = 5 * 60 * 1000; const fiveMinutes = 5 * 60 * 1000;
@@ -152,15 +130,18 @@ export class AzureDeviceCode extends AzureAuth {
}; };
const postResult = await this.makePostRequest(uri, postData); const postResult = await this.makePostRequest(uri, postData);
const result: DeviceCodeLoginResult = postResult.data; const result: DeviceCodeLoginResult = postResult.data;
return result; return result;
} catch (ex) { } catch (ex) {
Logger.error('Unexpected error making Azure auth request', 'azureCore.checkForResult', JSON.stringify(ex?.response?.data, undefined, 2)); console.log(ex);
console.log('Unexpected error making Azure auth request', 'azureCore.checkForResult', JSON.stringify(ex?.response?.data, undefined, 2));
throw new Error(msg); throw new Error(msg);
} }
} }
public override async autoOAuthCancelled(): Promise<void> { public override async autoOAuthCancelled(): Promise<void> {
return azdata.accounts.endAutoOAuthDeviceCode(); return azdata.accounts.endAutoOAuthDeviceCode();
} }

View File

@@ -1,370 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { INetworkModule, NetworkRequestOptions, NetworkResponse } from '@azure/msal-common';
import * as http from 'http';
import * as https from 'https';
import { NetworkUtils } from './networkUtils';
/**
* http methods
*/
export enum HttpMethod {
GET = 'get',
POST = 'post'
}
export enum HttpStatus {
SUCCESS_RANGE_START = 200,
SUCCESS_RANGE_END = 299,
REDIRECT = 302,
CLIENT_ERROR_RANGE_START = 400,
CLIENT_ERROR_RANGE_END = 499,
SERVER_ERROR_RANGE_START = 500,
SERVER_ERROR_RANGE_END = 599
}
export enum ProxyStatus {
SUCCESS_RANGE_START = 200,
SUCCESS_RANGE_END = 299,
SERVER_ERROR = 500
}
/**
* This class implements the API for network requests.
*/
export class HttpClient implements INetworkModule {
private proxyUrl: string;
private customAgentOptions: http.AgentOptions | https.AgentOptions;
static readonly AUTHORIZATION_PENDING: string = 'authorization_pending';
constructor(
proxyUrl?: string,
customAgentOptions?: http.AgentOptions | https.AgentOptions
) {
this.proxyUrl = proxyUrl || '';
this.customAgentOptions = customAgentOptions || {};
}
/**
* Http Get request
* @param url
* @param options
*/
async sendGetRequestAsync<T>(
url: string,
options?: NetworkRequestOptions
): Promise<NetworkResponse<T>> {
if (this.proxyUrl) {
return networkRequestViaProxy(url, this.proxyUrl, HttpMethod.GET, options, this.customAgentOptions as http.AgentOptions);
} else {
return networkRequestViaHttps(url, HttpMethod.GET, options, this.customAgentOptions as https.AgentOptions);
}
}
/**
* Http Post request
* @param url
* @param options
*/
async sendPostRequestAsync<T>(
url: string,
options?: NetworkRequestOptions,
cancellationToken?: number
): Promise<NetworkResponse<T>> {
if (this.proxyUrl) {
return networkRequestViaProxy(url, this.proxyUrl, HttpMethod.POST, options, this.customAgentOptions as http.AgentOptions, cancellationToken);
} else {
return networkRequestViaHttps(url, HttpMethod.POST, options, this.customAgentOptions as https.AgentOptions, cancellationToken);
}
}
}
const networkRequestViaProxy = <T>(
destinationUrlString: string,
proxyUrlString: string,
httpMethod: string,
options?: NetworkRequestOptions,
agentOptions?: http.AgentOptions,
timeout?: number
): Promise<NetworkResponse<T>> => {
const destinationUrl = new URL(destinationUrlString);
const proxyUrl = new URL(proxyUrlString);
// 'method: connect' must be used to establish a connection to the proxy
const headers = options?.headers || {} as Record<string, string>;
const tunnelRequestOptions: https.RequestOptions = {
host: proxyUrl.hostname,
port: proxyUrl.port,
method: 'CONNECT',
path: destinationUrl.hostname,
headers: headers
};
if (timeout) {
tunnelRequestOptions.timeout = timeout;
}
if (agentOptions && Object.keys(agentOptions).length) {
tunnelRequestOptions.agent = new http.Agent(agentOptions);
}
// compose a request string for the socket
let postRequestStringContent: string = '';
if (httpMethod === HttpMethod.POST) {
const body = options?.body || '';
postRequestStringContent =
'Content-Type: application/x-www-form-urlencoded\r\n' +
`Content-Length: ${body.length}\r\n` +
`\r\n${body}`;
}
const outgoingRequestString = `${httpMethod.toUpperCase()} ${destinationUrl.href} HTTP/1.1\r\n` +
`Host: ${destinationUrl.host}\r\n` +
'Connection: close\r\n' +
postRequestStringContent +
'\r\n';
return new Promise<NetworkResponse<T>>(((resolve, reject) => {
const request = http.request(tunnelRequestOptions);
if (tunnelRequestOptions.timeout) {
request.on('timeout', () => {
request.destroy();
reject(new Error('Request time out'));
});
}
request.end();
// establish connection to the proxy
request.on('connect', (response, socket) => {
const proxyStatusCode = response?.statusCode || ProxyStatus.SERVER_ERROR;
if ((proxyStatusCode < ProxyStatus.SUCCESS_RANGE_START) || (proxyStatusCode > ProxyStatus.SUCCESS_RANGE_END)) {
request.destroy();
socket.destroy();
reject(new Error(`Error connecting to proxy. Http status code: ${response.statusCode}. Http status message: ${response?.statusMessage || 'Unknown'}`));
}
if (tunnelRequestOptions.timeout) {
socket.setTimeout(tunnelRequestOptions.timeout);
socket.on('timeout', () => {
request.destroy();
socket.destroy();
reject(new Error('Request time out'));
});
}
// make a request over an HTTP tunnel
socket.write(outgoingRequestString);
const data: Buffer[] = [];
socket.on('data', (chunk) => {
data.push(chunk);
});
socket.on('end', () => {
// combine all received buffer streams into one buffer, and then into a string
const dataString = Buffer.concat([...data]).toString();
// separate each line into it's own entry in an arry
const dataStringArray = dataString.split('\r\n');
// the first entry will contain the statusCode and statusMessage
const httpStatusCode = parseInt(dataStringArray[0].split(' ')[1], undefined);
// remove 'HTTP/1.1' and the status code to get the status message
const statusMessage = dataStringArray[0].split(' ').slice(2).join(' ');
// the last entry will contain the body
const body = dataStringArray[dataStringArray.length - 1];
// everything in between the first and last entries are the headers
const headersArray = dataStringArray.slice(1, dataStringArray.length - 2);
// build an object out of all the headers
const entries = new Map();
headersArray.forEach((header) => {
/**
* the header might look like 'Content-Length: 1531', but that is just a string
* it needs to be converted to a key/value pair
* split the string at the first instance of ':'
* there may be more than one ':' if the value of the header is supposed to be a JSON object
*/
const headerKeyValue = header.split(new RegExp(/:\s(.*)/s));
const headerKey = headerKeyValue[0];
let headerValue = headerKeyValue[1];
// check if the value of the header is supposed to be a JSON object
try {
const object = JSON.parse(headerValue);
// if it is, then convert it from a string to a JSON object
if (object && (typeof object === 'object')) {
headerValue = object;
}
} catch (e) {
// otherwise, leave it as a string
}
entries.set(headerKey, headerValue);
});
const parsedHeaders = Object.fromEntries(entries) as Record<string, string>;
const networkResponse = NetworkUtils.getNetworkResponse(
parsedHeaders,
parseBody(httpStatusCode, statusMessage, parsedHeaders, body) as T,
httpStatusCode
);
if (((httpStatusCode < HttpStatus.SUCCESS_RANGE_START) || (httpStatusCode > HttpStatus.SUCCESS_RANGE_END)) &&
// do not destroy the request for the device code flow
// @ts-ignore
networkResponse.body['error'] !== HttpClient.AUTHORIZATION_PENDING) {
request.destroy();
}
resolve(networkResponse);
});
socket.on('error', (chunk) => {
request.destroy();
socket.destroy();
reject(new Error(chunk.toString()));
});
});
request.on('error', (chunk) => {
request.destroy();
reject(new Error(chunk.toString()));
});
}));
};
const networkRequestViaHttps = <T>(
urlString: string,
httpMethod: string,
options?: NetworkRequestOptions,
agentOptions?: https.AgentOptions,
timeout?: number
): Promise<NetworkResponse<T>> => {
const isPostRequest = httpMethod === HttpMethod.POST;
const body: string = options?.body || '';
const url = new URL(urlString);
const optionHeaders = options?.headers || {} as Record<string, string>;
let customOptions: https.RequestOptions = {
method: httpMethod,
headers: optionHeaders,
...NetworkUtils.urlToHttpOptions(url)
};
if (timeout) {
customOptions.timeout = timeout;
}
if (agentOptions && Object.keys(agentOptions).length) {
customOptions.agent = new https.Agent(agentOptions);
}
if (isPostRequest) {
// needed for post request to work
customOptions.headers = {
...customOptions.headers,
'Content-Length': body.length
};
}
return new Promise<NetworkResponse<T>>((resolve, reject) => {
const request = https.request(customOptions);
if (timeout) {
request.on('timeout', () => {
request.destroy();
reject(new Error('Request time out'));
});
}
if (isPostRequest) {
request.write(body);
}
request.end();
request.on('response', (response) => {
const headers = response.headers;
const statusCode = response.statusCode as number;
const statusMessage = response.statusMessage;
const data: Buffer[] = [];
response.on('data', (chunk) => {
data.push(chunk);
});
response.on('end', () => {
// combine all received buffer streams into one buffer, and then into a string
const dataBody = Buffer.concat([...data]).toString();
const parsedHeaders = headers as Record<string, string>;
const networkResponse = NetworkUtils.getNetworkResponse(
parsedHeaders,
parseBody(statusCode, statusMessage, parsedHeaders, dataBody) as T,
statusCode
);
if (((statusCode < HttpStatus.SUCCESS_RANGE_START) || (statusCode > HttpStatus.SUCCESS_RANGE_END)) &&
// do not destroy the request for the device code flow
// @ts-ignore
networkResponse.body['error'] !== HttpClient.AUTHORIZATION_PENDING) {
request.destroy();
}
resolve(networkResponse);
});
});
request.on('error', (chunk) => {
request.destroy();
reject(new Error(chunk.toString()));
});
});
};
/**
* Check if extra parsing is needed on the repsonse from the server
* @param statusCode {number} the status code of the response from the server
* @param statusMessage {string | undefined} the status message of the response from the server
* @param headers {Record<string, string>} the headers of the response from the server
* @param body {string} the body from the response of the server
* @returns JSON parsed body or error object
*/
const parseBody = (statusCode: number, statusMessage: string | undefined, headers: Record<string, string>, body: string) => {
/*
* Informational responses (100 - 199)
* Successful responses (200 - 299)
* Redirection messages (300 - 399)
* Client error responses (400 - 499)
* Server error responses (500 - 599)
*/
let parsedBody;
try {
parsedBody = JSON.parse(body);
} catch (error) {
let errorType;
let errorDescriptionHelper;
if ((statusCode >= HttpStatus.CLIENT_ERROR_RANGE_START) && (statusCode <= HttpStatus.CLIENT_ERROR_RANGE_END)) {
errorType = 'client_error';
errorDescriptionHelper = 'A client';
} else if ((statusCode >= HttpStatus.SERVER_ERROR_RANGE_START) && (statusCode <= HttpStatus.SERVER_ERROR_RANGE_END)) {
errorType = 'server_error';
errorDescriptionHelper = 'A server';
} else {
errorType = 'unknown_error';
errorDescriptionHelper = 'An unknown';
}
parsedBody = {
error: errorType,
error_description: `${errorDescriptionHelper} error occured.\nHttp status code: ${statusCode}\nHttp status message: ${statusMessage || 'Unknown'}\nHeaders: ${JSON.stringify(headers)}`
};
}
return parsedBody;
};

View File

@@ -1,43 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { NetworkResponse } from '@azure/msal-common';
import * as https from 'https';
export class NetworkUtils {
static getNetworkResponse<Body>(headers: Record<string, string>, body: Body, statusCode: number): NetworkResponse<Body> {
return {
headers: headers,
body: body,
status: statusCode
};
}
/*
* Utility function that converts a URL object into an ordinary options object as expected by the
* http.request and https.request APIs.
*/
static urlToHttpOptions(url: URL): https.RequestOptions {
const options: https.RequestOptions & Partial<Omit<URL, 'port'>> = {
protocol: url.protocol,
hostname: url.hostname && url.hostname.startsWith('[') ?
url.hostname.slice(1, -1) :
url.hostname,
hash: url.hash,
search: url.search,
pathname: url.pathname,
path: `${url.pathname || ''}${url.search || ''}`,
href: url.href
};
if (url.port !== '') {
options.port = Number(url.port);
}
if (url.username || url.password) {
options.auth = `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`;
}
return options;
}
}

View File

@@ -13,37 +13,31 @@ import {
AzureAccount AzureAccount
} from 'azurecore'; } from 'azurecore';
import { Deferred } from './interfaces'; import { Deferred } from './interfaces';
import { AuthenticationResult, PublicClientApplication } from '@azure/msal-node';
import { SimpleTokenCache } from './utils/simpleTokenCache'; import { SimpleTokenCache } from './simpleTokenCache';
import { Logger } from '../utils/Logger'; import { Logger } from '../utils/Logger';
import { MultiTenantTokenResponse, Token, AzureAuth } from './auths/azureAuth'; import { MultiTenantTokenResponse, Token, AzureAuth } from './auths/azureAuth';
import { AzureAuthCodeGrant } from './auths/azureAuthCodeGrant'; import { AzureAuthCodeGrant } from './auths/azureAuthCodeGrant';
import { AzureDeviceCode } from './auths/azureDeviceCode'; import { AzureDeviceCode } from './auths/azureDeviceCode';
import { filterAccounts } from '../azureResource/utils';
import * as Constants from '../constants';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disposable { export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disposable {
private static readonly CONFIGURATION_SECTION = 'accounts.azure.auth';
private readonly authMappings = new Map<AzureAuthType, AzureAuth>(); private readonly authMappings = new Map<AzureAuthType, AzureAuth>();
private initComplete!: Deferred<void, Error>; private initComplete!: Deferred<void, Error>;
private initCompletePromise: Promise<void> = new Promise<void>((resolve, reject) => this.initComplete = { resolve, reject }); private initCompletePromise: Promise<void> = new Promise<void>((resolve, reject) => this.initComplete = { resolve, reject });
public clientApplication: PublicClientApplication;
constructor( constructor(
metadata: AzureAccountProviderMetadata, metadata: AzureAccountProviderMetadata,
tokenCache: SimpleTokenCache, tokenCache: SimpleTokenCache,
context: vscode.ExtensionContext, context: vscode.ExtensionContext,
clientApplication: PublicClientApplication,
uriEventHandler: vscode.EventEmitter<vscode.Uri>, uriEventHandler: vscode.EventEmitter<vscode.Uri>,
private readonly authLibrary: string,
private readonly forceDeviceCode: boolean = false private readonly forceDeviceCode: boolean = false
) { ) {
this.clientApplication = clientApplication;
vscode.workspace.onDidChangeConfiguration((changeEvent) => { vscode.workspace.onDidChangeConfiguration((changeEvent) => {
const impactProvider = changeEvent.affectsConfiguration(Constants.AccountsAzureAuthSection); const impact = changeEvent.affectsConfiguration(AzureAccountProvider.CONFIGURATION_SECTION);
if (impactProvider === true) { if (impact === true) {
this.handleAuthMapping(metadata, tokenCache, context, uriEventHandler); this.handleAuthMapping(metadata, tokenCache, context, uriEventHandler);
} }
}); });
@@ -56,27 +50,22 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
} }
clearTokenCache(): Thenable<void> { clearTokenCache(): Thenable<void> {
return this.authLibrary === Constants.AuthLibrary.MSAL return this.getAuthMethod().deleteAllCache();
? this.getAuthMethod().deleteAllCacheMsal()
// fallback to ADAL as default
: this.getAuthMethod().deleteAllCacheAdal();
} }
private handleAuthMapping(metadata: AzureAccountProviderMetadata, tokenCache: SimpleTokenCache, context: vscode.ExtensionContext, uriEventHandler: vscode.EventEmitter<vscode.Uri>) { private handleAuthMapping(metadata: AzureAccountProviderMetadata, tokenCache: SimpleTokenCache, context: vscode.ExtensionContext, uriEventHandler: vscode.EventEmitter<vscode.Uri>) {
this.authMappings.forEach(m => m.dispose()); this.authMappings.forEach(m => m.dispose());
this.authMappings.clear(); this.authMappings.clear();
const configuration = vscode.workspace.getConfiguration(AzureAccountProvider.CONFIGURATION_SECTION);
const configuration = vscode.workspace.getConfiguration(Constants.AccountsAzureAuthSection); const codeGrantMethod: boolean = configuration.get<boolean>('codeGrant', false);
const codeGrantMethod: boolean = configuration.get<boolean>(Constants.AuthType.CodeGrant, false); const deviceCodeMethod: boolean = configuration.get<boolean>('deviceCode', false);
const deviceCodeMethod: boolean = configuration.get<boolean>(Constants.AuthType.DeviceCode, false);
if (codeGrantMethod === true && !this.forceDeviceCode) { if (codeGrantMethod === true && !this.forceDeviceCode) {
this.authMappings.set(AzureAuthType.AuthCodeGrant, new AzureAuthCodeGrant(metadata, tokenCache, context, uriEventHandler, this.clientApplication, this.authLibrary)); this.authMappings.set(AzureAuthType.AuthCodeGrant, new AzureAuthCodeGrant(metadata, tokenCache, context, uriEventHandler));
} } else if (deviceCodeMethod === true || this.forceDeviceCode) {
if (deviceCodeMethod === true || this.forceDeviceCode) { this.authMappings.set(AzureAuthType.DeviceCode, new AzureDeviceCode(metadata, tokenCache, context, uriEventHandler));
this.authMappings.set(AzureAuthType.DeviceCode, new AzureDeviceCode(metadata, tokenCache, context, uriEventHandler, this.clientApplication, this.authLibrary)); } else {
}
if (codeGrantMethod === false && deviceCodeMethod === false && !this.forceDeviceCode) {
console.error('No authentication methods selected'); console.error('No authentication methods selected');
} }
} }
@@ -105,26 +94,14 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
private async _initialize(storedAccounts: AzureAccount[]): Promise<AzureAccount[]> { private async _initialize(storedAccounts: AzureAccount[]): Promise<AzureAccount[]> {
const accounts: AzureAccount[] = []; const accounts: AzureAccount[] = [];
Logger.verbose(`Initializing stored accounts ${JSON.stringify(accounts)}`); console.log(`Initializing stored accounts ${JSON.stringify(accounts)}`);
const updatedAccounts = filterAccounts(storedAccounts, this.authLibrary); for (let account of storedAccounts) {
for (let account of updatedAccounts) {
const azureAuth = this.getAuthMethod(account); const azureAuth = this.getAuthMethod(account);
if (!azureAuth) { if (!azureAuth) {
account.isStale = true; account.isStale = true;
accounts.push(account); accounts.push(account);
} else { } else {
account.isStale = false; accounts.push(await azureAuth.refreshAccess(account));
if (this.authLibrary === Constants.AuthLibrary.MSAL) {
// Check MSAL Cache before adding account, to mark it as stale if it is not present in cache
const accountInCache = await azureAuth.getAccountFromMsalCache(account.key.accountId);
if (!accountInCache) {
account.isStale = true;
}
accounts.push(account);
} else { // fallback to ADAL as default
accounts.push(await azureAuth.refreshAccessAdal(account));
}
} }
} }
this.initComplete.resolve(); this.initComplete.resolve();
@@ -143,56 +120,10 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
private async _getAccountSecurityToken(account: AzureAccount, tenantId: string, resource: azdata.AzureResource): Promise<Token | undefined> { private async _getAccountSecurityToken(account: AzureAccount, tenantId: string, resource: azdata.AzureResource): Promise<Token | undefined> {
await this.initCompletePromise; await this.initCompletePromise;
const azureAuth = this.getAuthMethod(account); 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}`, [], []); return azureAuth?.getAccountSecurityToken(account, tenantId, resource);
if (this.authLibrary === Constants.AuthLibrary.MSAL) {
tenantId = tenantId || account.properties.owningTenant.id;
let authResult = await azureAuth.getTokenMsal(account.key.accountId, resource, tenantId);
if (this.isAuthenticationResult(authResult) && authResult.account && authResult.account.idTokenClaims) {
const token: Token = {
key: authResult.account.homeAccountId,
token: authResult.accessToken,
tokenType: authResult.tokenType,
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);
}
} else {
account.isStale = true;
Logger.error(`_getAccountSecurityToken: Authentication method not found for account ${account.displayInfo.displayName}`);
throw Error('Failed to get authentication method, please remove and re-add the account');
}
} }
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> { 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.")); 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; const azureAccount = account as AzureAccount;
@@ -245,6 +176,7 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
return pick.azureAuth.startLogin(); return pick.azureAuth.startLogin();
} }
refresh(account: AzureAccount): Thenable<AzureAccount | azdata.PromptFailedResult> { refresh(account: AzureAccount): Thenable<AzureAccount | azdata.PromptFailedResult> {
return this._refresh(account); return this._refresh(account);
} }

View File

@@ -3,23 +3,16 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * 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 azdata from 'azdata';
import * as events from 'events'; import * as events from 'events';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { promises as fsPromises } from 'fs'; import { SimpleTokenCache } from './simpleTokenCache';
import { SimpleTokenCache } from './utils/simpleTokenCache';
import providerSettings from './providerSettings'; import providerSettings from './providerSettings';
import { AzureAccountProvider as AzureAccountProvider } from './azureAccountProvider'; import { AzureAccountProvider as AzureAccountProvider } from './azureAccountProvider';
import { AzureAccountProviderMetadata, CacheEncryptionKeys } from 'azurecore'; import { AzureAccountProviderMetadata } from 'azurecore';
import { ProviderSettings } from './interfaces'; import { ProviderSettings } from './interfaces';
import { MsalCachePluginProvider } from './utils/msalCachePlugin';
import * as loc from '../localizedConstants'; import * as loc from '../localizedConstants';
import { Configuration, PublicClientApplication } from '@azure/msal-node';
import * as Constants from '../constants';
import { Logger } from '../utils/Logger';
import { ILoggerCallback, LogLevel as MsalLogLevel } from "@azure/msal-common";
let localize = nls.loadMessageBundle(); let localize = nls.loadMessageBundle();
@@ -30,23 +23,22 @@ class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.
} }
export class AzureAccountProviderService implements vscode.Disposable { export class AzureAccountProviderService implements vscode.Disposable {
// CONSTANTS ///////////////////////////////////////////////////////////////
private static CommandClearTokenCache = 'accounts.clearTokenCache';
private static ConfigurationSection = 'accounts.azure.cloud';
private static CredentialNamespace = 'azureAccountProviderCredentials';
// MEMBER VARIABLES //////////////////////////////////////////////////////// // MEMBER VARIABLES ////////////////////////////////////////////////////////
private _disposables: vscode.Disposable[] = []; private _disposables: vscode.Disposable[] = [];
private _accountDisposals: { [accountProviderId: string]: vscode.Disposable } = {}; private _accountDisposals: { [accountProviderId: string]: vscode.Disposable } = {};
private _accountProviders: { [accountProviderId: string]: azdata.AccountProvider } = {}; private _accountProviders: { [accountProviderId: string]: azdata.AccountProvider } = {};
private _credentialProvider: azdata.CredentialProvider | undefined = undefined; private _credentialProvider: azdata.CredentialProvider | undefined = undefined;
private _cachePluginProvider: MsalCachePluginProvider | undefined = undefined;
private _configChangePromiseChain: Thenable<void> = Promise.resolve(); private _configChangePromiseChain: Thenable<void> = Promise.resolve();
private _currentConfig: vscode.WorkspaceConfiguration | undefined = undefined; private _currentConfig: vscode.WorkspaceConfiguration | undefined = undefined;
private _event: events.EventEmitter = new events.EventEmitter(); private _event: events.EventEmitter = new events.EventEmitter();
private readonly _uriEventHandler: UriEventHandler = new UriEventHandler(); private readonly _uriEventHandler: UriEventHandler = new UriEventHandler();
public clientApplication!: PublicClientApplication;
private _onEncryptionKeysUpdated: vscode.EventEmitter<CacheEncryptionKeys>;
constructor(private _context: vscode.ExtensionContext, constructor(private _context: vscode.ExtensionContext, private _userStoragePath: string) {
private _userStoragePath: string,
private _authLibrary: string) {
this._onEncryptionKeysUpdated = new vscode.EventEmitter<CacheEncryptionKeys>();
this._disposables.push(vscode.window.registerUriHandler(this._uriEventHandler)); this._disposables.push(vscode.window.registerUriHandler(this._uriEventHandler));
} }
@@ -55,16 +47,17 @@ export class AzureAccountProviderService implements vscode.Disposable {
let self = this; let self = this;
// Register commands // Register commands
this._context.subscriptions.push(vscode.commands.registerCommand(Constants.AccountsClearTokenCacheCommand, this._context.subscriptions.push(vscode.commands.registerCommand(
() => { self._event.emit(Constants.AccountsClearTokenCacheCommand); } AzureAccountProviderService.CommandClearTokenCache,
() => { self._event.emit(AzureAccountProviderService.CommandClearTokenCache); }
)); ));
this._event.on(Constants.AccountsClearTokenCacheCommand, () => { void self.onClearTokenCache(); }); this._event.on(AzureAccountProviderService.CommandClearTokenCache, () => { void self.onClearTokenCache(); });
// 1) Get a credential provider // 1) Get a credential provider
// 2a) Store the credential provider for use later // 2a) Store the credential provider for use later
// 2b) Register the configuration change handler // 2b) Register the configuration change handler
// 2c) Perform an initial config change handling // 2c) Perform an initial config change handling
return azdata.credentials.getProvider(Constants.AzureAccountProviderCredentials) return azdata.credentials.getProvider(AzureAccountProviderService.CredentialNamespace)
.then(credProvider => { .then(credProvider => {
this._credentialProvider = credProvider; this._credentialProvider = credProvider;
@@ -77,17 +70,6 @@ export class AzureAccountProviderService implements vscode.Disposable {
}); });
} }
public getEncryptionKeysEmitter(): vscode.EventEmitter<CacheEncryptionKeys> {
return this._onEncryptionKeysUpdated;
}
public async getEncryptionKeys(): Promise<CacheEncryptionKeys> {
if (!this._cachePluginProvider) {
await this.onDidChangeConfiguration();
}
return this._cachePluginProvider!.getCacheEncryptionKeys();
}
public dispose() { public dispose() {
while (this._disposables.length) { while (this._disposables.length) {
const item = this._disposables.pop(); const item = this._disposables.pop();
@@ -121,7 +103,7 @@ export class AzureAccountProviderService implements vscode.Disposable {
// Add a new change processing onto the existing promise change // Add a new change processing onto the existing promise change
await this._configChangePromiseChain; await this._configChangePromiseChain;
// Grab the stored config and the latest config // Grab the stored config and the latest config
let newConfig = vscode.workspace.getConfiguration(Constants.AccountsAzureCloudSection); let newConfig = vscode.workspace.getConfiguration(AzureAccountProviderService.ConfigurationSection);
let oldConfig = this._currentConfig; let oldConfig = this._currentConfig;
this._currentConfig = newConfig; this._currentConfig = newConfig;
@@ -156,90 +138,22 @@ export class AzureAccountProviderService implements vscode.Disposable {
} }
private async registerAccountProvider(provider: ProviderSettings): Promise<void> { private async registerAccountProvider(provider: ProviderSettings): Promise<void> {
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 = Constants.MSALCacheName;
await this.clearOldCacheIfExists();
try { try {
const noSystemKeychain = vscode.workspace.getConfiguration('azure').get<boolean>('noSystemKeychain');
let tokenCacheKey = `azureTokenCache-${provider.metadata.id}`;
if (!this._credentialProvider) { if (!this._credentialProvider) {
throw new Error('Credential provider not registered'); throw new Error('Credential provider not registered');
} }
// ADAL Token Cache
let simpleTokenCache = new SimpleTokenCache(tokenCacheKey, this._userStoragePath, noSystemKeychain, this._credentialProvider); let simpleTokenCache = new SimpleTokenCache(tokenCacheKey, this._userStoragePath, noSystemKeychain, this._credentialProvider);
if (this._authLibrary === Constants.AuthLibrary.ADAL) { await simpleTokenCache.init();
await simpleTokenCache.init();
}
// MSAL Cache Plugin const isSaw: boolean = vscode.env.appName.toLowerCase().indexOf('saw') > 0;
this._cachePluginProvider = new MsalCachePluginProvider(tokenCacheKeyMsal, this._userStoragePath, this._credentialProvider, this._onEncryptionKeysUpdated); let accountProvider = new AzureAccountProvider(provider.metadata as AzureAccountProviderMetadata, simpleTokenCache, this._context, this._uriEventHandler, isSaw);
if (this._authLibrary === Constants.AuthLibrary.MSAL) {
// Initialize cache provider and encryption keys
await this._cachePluginProvider.init();
}
const msalConfiguration: Configuration = {
auth: {
clientId: provider.metadata.settings.clientId,
authority: 'https://login.windows.net/common'
},
system: {
loggerOptions: {
loggerCallback: this.getLoggerCallback(),
logLevel: MsalLogLevel.Trace,
piiLoggingEnabled: true,
},
},
cache: {
cachePlugin: this._cachePluginProvider?.getCachePlugin()
}
}
this.clientApplication = new PublicClientApplication(msalConfiguration);
let accountProvider = new AzureAccountProvider(provider.metadata as AzureAccountProviderMetadata,
simpleTokenCache, this._context, this.clientApplication, this._uriEventHandler, this._authLibrary, isSaw);
this._accountProviders[provider.metadata.id] = accountProvider; this._accountProviders[provider.metadata.id] = accountProvider;
this._accountDisposals[provider.metadata.id] = azdata.accounts.registerAccountProvider(provider.metadata, accountProvider); this._accountDisposals[provider.metadata.id] = azdata.accounts.registerAccountProvider(provider.metadata, accountProvider);
} catch (e) { } catch (e) {
console.error(`Failed to register account provider, isSaw: ${isSaw}: ${e}`); console.error(`Failed to register account provider: ${e}`);
}
}
/**
* 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) {
switch (level) {
case MsalLogLevel.Error:
Logger.error(message);
break;
case MsalLogLevel.Info:
Logger.info(message);
break;
case MsalLogLevel.Verbose:
default:
Logger.verbose(message);
break;
}
} else {
Logger.pii(message);
}
} }
} }

View File

@@ -39,7 +39,7 @@ const publicAzureSettings: ProviderSettings = {
}, },
graphResource: { graphResource: {
id: SettingIds.graph, id: SettingIds.graph,
endpoint: 'https://graph.windows.net/', endpoint: 'https://graph.windows.net',
azureResourceId: AzureResource.Graph azureResourceId: AzureResource.Graph
}, },
msGraphResource: { msGraphResource: {
@@ -49,7 +49,7 @@ const publicAzureSettings: ProviderSettings = {
}, },
armResource: { armResource: {
id: SettingIds.arm, id: SettingIds.arm,
endpoint: 'https://management.azure.com/', endpoint: 'https://management.azure.com',
azureResourceId: AzureResource.ResourceManagement azureResourceId: AzureResource.ResourceManagement
}, },
sqlResource: { sqlResource: {
@@ -59,38 +59,38 @@ const publicAzureSettings: ProviderSettings = {
}, },
ossRdbmsResource: { ossRdbmsResource: {
id: SettingIds.ossrdbms, id: SettingIds.ossrdbms,
endpoint: 'https://ossrdbms-aad.database.windows.net/', endpoint: 'https://ossrdbms-aad.database.windows.net',
azureResourceId: AzureResource.OssRdbms azureResourceId: AzureResource.OssRdbms
}, },
azureKeyVaultResource: { azureKeyVaultResource: {
id: SettingIds.vault, id: SettingIds.vault,
endpoint: 'https://vault.azure.net/', endpoint: 'https://vault.azure.net',
azureResourceId: AzureResource.AzureKeyVault azureResourceId: AzureResource.AzureKeyVault
}, },
azureDevOpsResource: { azureDevOpsResource: {
id: SettingIds.ado, id: SettingIds.ado,
endpoint: '499b84ac-1321-427f-aa17-267ca6975798/', endpoint: '499b84ac-1321-427f-aa17-267ca6975798',
azureResourceId: AzureResource.AzureDevOps, azureResourceId: AzureResource.AzureDevOps,
}, },
azureLogAnalyticsResource: { azureLogAnalyticsResource: {
id: SettingIds.ala, id: SettingIds.ala,
endpoint: 'https://api.loganalytics.io/', endpoint: 'https://api.loganalytics.io',
azureResourceId: AzureResource.AzureLogAnalytics, azureResourceId: AzureResource.AzureLogAnalytics,
}, },
azureStorageResource: { azureStorageResource: {
id: SettingIds.storage, id: SettingIds.storage,
endpoint: '', endpoint: '',
endpointSuffix: '.core.windows.net/', endpointSuffix: '.core.windows.net',
azureResourceId: AzureResource.AzureStorage azureResourceId: AzureResource.AzureStorage
}, },
azureKustoResource: { azureKustoResource: {
id: SettingIds.kusto, id: SettingIds.kusto,
endpoint: 'https://kusto.kusto.windows.net/', endpoint: 'https://kusto.kusto.windows.net',
azureResourceId: AzureResource.AzureKusto, azureResourceId: AzureResource.AzureKusto,
}, },
powerBiResource: { powerBiResource: {
id: SettingIds.powerbi, id: SettingIds.powerbi,
endpoint: 'https://analysis.windows.net/powerbi/api/', endpoint: 'https://analysis.windows.net/powerbi/api',
azureResourceId: AzureResource.PowerBi azureResourceId: AzureResource.PowerBi
}, },
redirectUri: 'http://localhost', redirectUri: 'http://localhost',
@@ -119,12 +119,12 @@ const usGovAzureSettings: ProviderSettings = {
}, },
graphResource: { graphResource: {
id: SettingIds.graph, id: SettingIds.graph,
endpoint: 'https://graph.windows.net/', endpoint: 'https://graph.windows.net',
azureResourceId: AzureResource.Graph azureResourceId: AzureResource.Graph
}, },
armResource: { armResource: {
id: SettingIds.arm, id: SettingIds.arm,
endpoint: 'https://management.usgovcloudapi.net/', endpoint: 'https://management.usgovcloudapi.net',
azureResourceId: AzureResource.ResourceManagement azureResourceId: AzureResource.ResourceManagement
}, },
sqlResource: { sqlResource: {
@@ -134,33 +134,28 @@ const usGovAzureSettings: ProviderSettings = {
}, },
ossRdbmsResource: { ossRdbmsResource: {
id: SettingIds.ossrdbms, id: SettingIds.ossrdbms,
endpoint: 'https://ossrdbms-aad.database.usgovcloudapi.net/', endpoint: 'https://ossrdbms-aad.database.usgovcloudapi.net',
azureResourceId: AzureResource.OssRdbms azureResourceId: AzureResource.OssRdbms
}, },
azureKeyVaultResource: { azureKeyVaultResource: {
id: SettingIds.vault, id: SettingIds.vault,
endpoint: 'https://vault.usgovcloudapi.net/', endpoint: 'https://vault.usgovcloudapi.net',
azureResourceId: AzureResource.AzureKeyVault azureResourceId: AzureResource.AzureKeyVault
}, },
azureLogAnalyticsResource: { azureLogAnalyticsResource: {
id: SettingIds.ala, id: SettingIds.ala,
endpoint: 'https://api.loganalytics.us/', endpoint: 'https://api.loganalytics.us',
azureResourceId: AzureResource.AzureLogAnalytics, azureResourceId: AzureResource.AzureLogAnalytics,
}, },
azureStorageResource: { azureStorageResource: {
id: SettingIds.storage, id: SettingIds.storage,
endpoint: '', endpoint: '',
endpointSuffix: '.core.usgovcloudapi.net/', endpointSuffix: '.core.usgovcloudapi.net',
azureResourceId: AzureResource.AzureStorage azureResourceId: AzureResource.AzureStorage
}, },
azureKustoResource: {
id: SettingIds.kusto,
endpoint: 'https://kusto.kusto.usgovcloudapi.net',
azureResourceId: AzureResource.AzureKusto,
},
powerBiResource: { powerBiResource: {
id: SettingIds.powerbi, id: SettingIds.powerbi,
endpoint: 'https://analysis.windows.net/powerbi/api/', endpoint: 'https://analysis.windows.net/powerbi/api',
azureResourceId: AzureResource.PowerBi azureResourceId: AzureResource.PowerBi
}, },
redirectUri: 'http://localhost', redirectUri: 'http://localhost',
@@ -173,6 +168,115 @@ 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 = { const chinaAzureSettings: ProviderSettings = {
configKey: 'enableChinaCloud', configKey: 'enableChinaCloud',
metadata: { metadata: {
@@ -222,11 +326,6 @@ const chinaAzureSettings: ProviderSettings = {
endpointSuffix: '.core.chinacloudapi.cn', endpointSuffix: '.core.chinacloudapi.cn',
azureResourceId: AzureResource.AzureStorage azureResourceId: AzureResource.AzureStorage
}, },
azureKustoResource: {
id: SettingIds.kusto,
endpoint: 'https://kusto.kusto.chinacloudapi.cn',
azureResourceId: AzureResource.AzureKusto,
},
powerBiResource: { powerBiResource: {
id: SettingIds.powerbi, id: SettingIds.powerbi,
endpoint: 'https://analysis.windows.net/powerbi/api', endpoint: 'https://analysis.windows.net/powerbi/api',
@@ -241,5 +340,5 @@ const chinaAzureSettings: ProviderSettings = {
} }
} }
}; };
const allSettings = [publicAzureSettings, usGovAzureSettings, chinaAzureSettings]; const allSettings = [publicAzureSettings, usGovAzureSettings, usNatAzureSettings, germanyAzureSettings, chinaAzureSettings];
export default allSettings; export default allSettings;

View File

@@ -4,10 +4,9 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as keytarType from 'keytar'; import * as keytarType from 'keytar';
import { join, parse } from 'path'; import { join, parse } from 'path';
import { FileDatabase } from './fileDatabase'; import { FileDatabase } from './utils/fileDatabase';
import * as azdata from 'azdata'; import * as azdata from 'azdata';
import { FileEncryptionHelper } from './fileEncryptionHelper'; import * as crypto from 'crypto';
import { AuthLibrary } from '../../constants';
function getSystemKeytar(): Keytar | undefined { function getSystemKeytar(): Keytar | undefined {
try { try {
@@ -26,8 +25,42 @@ const separator = '§';
async function getFileKeytar(filePath: string, credentialService: azdata.CredentialProvider): Promise<Keytar | undefined> { async function getFileKeytar(filePath: string, credentialService: azdata.CredentialProvider): Promise<Keytar | undefined> {
const fileName = parse(filePath).base; const fileName = parse(filePath).base;
const fileEncryptionHelper: FileEncryptionHelper = new FileEncryptionHelper(AuthLibrary.ADAL, credentialService, fileName); const iv = await credentialService.readCredential(`${fileName}-iv`);
const db = new FileDatabase(filePath, fileEncryptionHelper.fileOpener, fileEncryptionHelper.fileSaver); const key = await credentialService.readCredential(`${fileName}-key`);
let ivBuffer: Buffer;
let keyBuffer: Buffer;
if (!iv?.password || !key?.password) {
ivBuffer = crypto.randomBytes(16);
keyBuffer = crypto.randomBytes(32);
try {
await credentialService.saveCredential(`${fileName}-iv`, ivBuffer.toString('hex'));
await credentialService.saveCredential(`${fileName}-key`, keyBuffer.toString('hex'));
} catch (ex) {
console.log(ex);
}
} else {
ivBuffer = Buffer.from(iv.password, 'hex');
keyBuffer = Buffer.from(key.password, 'hex');
}
const fileSaver = async (content: string): Promise<string> => {
const cipherIv = crypto.createCipheriv('aes-256-gcm', keyBuffer, ivBuffer);
return `${cipherIv.update(content, 'utf8', 'hex')}${cipherIv.final('hex')}%${cipherIv.getAuthTag().toString('hex')}`;
};
const fileOpener = async (content: string): Promise<string> => {
const decipherIv = crypto.createDecipheriv('aes-256-gcm', keyBuffer, ivBuffer);
const split = content.split('%');
if (split.length !== 2) {
throw new Error('File didn\'t contain the auth tag.');
}
decipherIv.setAuthTag(Buffer.from(split[1], 'hex'));
return `${decipherIv.update(split[0], 'hex', 'utf8')}${decipherIv.final('utf8')}`;
};
const db = new FileDatabase(filePath, fileOpener, fileSaver);
await db.initialize(); await db.initialize();
const fileKeytar: Keytar = { const fileKeytar: Keytar = {
@@ -61,7 +94,6 @@ async function getFileKeytar(filePath: string, credentialService: azdata.Credent
return fileKeytar; return fileKeytar;
} }
export type Keytar = { export type Keytar = {
getPassword: typeof keytarType['getPassword']; getPassword: typeof keytarType['getPassword'];
setPassword: typeof keytarType['setPassword']; setPassword: typeof keytarType['setPassword'];
@@ -78,7 +110,9 @@ export class SimpleTokenCache {
private readonly userStoragePath: string, private readonly userStoragePath: string,
private readonly forceFileStorage: boolean = false, private readonly forceFileStorage: boolean = false,
private readonly credentialService: azdata.CredentialProvider, private readonly credentialService: azdata.CredentialProvider,
) { } ) {
}
async init(): Promise<void> { async init(): Promise<void> {
this.serviceName = this.serviceName.replace(/-/g, '_'); this.serviceName = this.serviceName.replace(/-/g, '_');

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { promises as fs, constants as fsConstants } from 'fs'; import { promises as fs, constants as fsConstants } from 'fs';
import { Logger } from '../../utils/Logger';
export type ReadWriteHook = (contents: string) => Promise<string>; export type ReadWriteHook = (contents: string) => Promise<string>;
const noOpHook: ReadWriteHook = async (contents): Promise<string> => { const noOpHook: ReadWriteHook = async (contents): Promise<string> => {
@@ -89,6 +88,7 @@ export class FileDatabase {
this.isDirty = true; this.isDirty = true;
} }
public async initialize(): Promise<void> { public async initialize(): Promise<void> {
this.isInitialized = true; this.isInitialized = true;
this.saveInterval = setInterval(() => this.save(), 20 * 1000); this.saveInterval = setInterval(() => this.save(), 20 * 1000);
@@ -98,7 +98,7 @@ export class FileDatabase {
fileContents = await fs.readFile(this.dbPath, { encoding: 'utf8' }); fileContents = await fs.readFile(this.dbPath, { encoding: 'utf8' });
fileContents = await this.readHook(fileContents); fileContents = await this.readHook(fileContents);
} catch (ex) { } catch (ex) {
Logger.error(`Error occurred when initializing File Database from file system cache, ADAL cache will be reset: ${ex}`); console.log(`file db does not exist ${ex}`);
await this.createFile(); await this.createFile();
this.db = {}; this.db = {};
this.isDirty = true; this.isDirty = true;
@@ -108,7 +108,7 @@ export class FileDatabase {
try { try {
this.db = JSON.parse(fileContents); this.db = JSON.parse(fileContents);
} catch (ex) { } catch (ex) {
Logger.error(`Error occurred when reading file database contents as JSON, ADAL cache will be reset: ${ex}`); console.log(`DB was corrupted, resetting it ${ex}`);
await this.createFile(); await this.createFile();
this.db = {}; this.db = {};
} }
@@ -140,7 +140,7 @@ export class FileDatabase {
this.isDirty = false; this.isDirty = false;
} catch (ex) { } catch (ex) {
Logger.error(`Error occurred while saving cache contents to file storage, this may cause issues with ADAL cache persistence: ${ex}`); console.log(`File saving is erroring! ${ex}`);
} finally { } finally {
this.isSaving = false; this.isSaving = false;
} }

View File

@@ -1,139 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* 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';
import { CacheEncryptionKeys } from 'azurecore';
export class FileEncryptionHelper {
constructor(
private readonly _authLibrary: AuthLibrary,
private readonly _credentialService: azdata.CredentialProvider,
protected readonly _fileName: string,
private readonly _onEncryptionKeysUpdated?: vscode.EventEmitter<CacheEncryptionKeys>
) {
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;
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);
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, this._bufferEncoding);
this._keyBuffer = Buffer.from(key, this._bufferEncoding);
}
// Emit event with cache encryption keys to send notification to provider services.
if (this._authLibrary === AuthLibrary.MSAL && this._onEncryptionKeysUpdated) {
this._onEncryptionKeysUpdated.fire(this.getEncryptionKeys());
Logger.verbose('FileEncryptionHelper: Fired encryption keys updated event.');
}
}
/**
* Provides encryption keys in use for instant access.
*/
public getEncryptionKeys(): CacheEncryptionKeys {
return {
iv: this._ivBuffer!.toString(this._bufferEncoding),
key: this._keyBuffer!.toString(this._bufferEncoding)
}
}
fileSaver = async (content: string): Promise<string> => {
if (!this._keyBuffer || !this._ivBuffer) {
await this.init();
}
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();
}
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];
}
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);
});
}
}
}

View File

@@ -1,136 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ICachePlugin, TokenCacheContext } from '@azure/msal-node';
import { promises as fsPromises } from 'fs';
import * as lockFile from 'lockfile';
import * as path from 'path';
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { AccountsClearTokenCacheCommand, AuthLibrary } from '../../constants';
import { Logger } from '../../utils/Logger';
import { FileEncryptionHelper } from './fileEncryptionHelper';
import { CacheEncryptionKeys } from 'azurecore';
export class MsalCachePluginProvider {
constructor(
private readonly _serviceName: string,
private readonly _msalFilePath: string,
private readonly _credentialService: azdata.CredentialProvider,
private readonly _onEncryptionKeysUpdated: vscode.EventEmitter<CacheEncryptionKeys>
) {
this._msalFilePath = path.join(this._msalFilePath, this._serviceName);
this._fileEncryptionHelper = new FileEncryptionHelper(AuthLibrary.MSAL, this._credentialService, this._serviceName, this._onEncryptionKeysUpdated);
}
private _lockTaken: boolean = false;
private _fileEncryptionHelper: FileEncryptionHelper;
private getLockfilePath(): string {
return this._msalFilePath + '.lockfile';
}
public async init(): Promise<void> {
await this._fileEncryptionHelper.init();
}
public getCacheEncryptionKeys(): CacheEncryptionKeys {
return this._fileEncryptionHelper.getEncryptionKeys();
}
public getCachePlugin(): ICachePlugin {
const lockFilePath = this.getLockfilePath();
const beforeCacheAccess = async (cacheContext: TokenCacheContext): Promise<void> => {
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(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.
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' });
}
Logger.verbose(`MsalCachePlugin: Token read from cache successfully.`);
} catch (e) {
if (e.code === 'ENOENT') {
// File doesn't exist, log and continue
Logger.verbose(`MsalCachePlugin: Cache file not found on disk: ${e.code}`);
}
else {
Logger.error(`MsalCachePlugin: Failed to read from cache file: ${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);
this._lockTaken = false;
}
}
const afterCacheAccess = async (cacheContext: TokenCacheContext): Promise<void> => {
if (cacheContext.cacheHasChanged) {
await this.waitAndLock(lockFilePath);
try {
const data = cacheContext.tokenCache.serialize();
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}`);
throw e;
} finally {
lockFile.unlockSync(lockFilePath);
this._lockTaken = false;
}
}
};
// This is an implementation of ICachePlugin that uses the beforeCacheAccess and afterCacheAccess callbacks to read and write to a file
// Ref https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-node-migration#enable-token-caching
// In future we should use msal-node-extensions to provide a secure storage of tokens, instead of implementing our own
// However - as of now this library does not come with pre-compiled native libraries that causes runtime issues
// Ref https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/3332
return {
beforeCacheAccess,
afterCacheAccess,
};
}
private async waitAndLock(lockFilePath: string): Promise<void> {
// Make 500 retry attempts with 100ms wait time between each attempt to allow enough time for the lock to be released.
const retries = 500;
const retryWait = 100;
// We cannot rely on lockfile.lockSync() to clear stale lockfile,
// so we check if the lockfile exists and if it does, calling unlockSync() will clear it.
if (lockFile.checkSync(lockFilePath) && !this._lockTaken) {
lockFile.unlockSync(lockFilePath);
Logger.verbose(`MsalCachePlugin: Stale lockfile found and has been removed.`);
}
let retryAttempt = 0;
while (retryAttempt <= retries) {
try {
// Use lockfile.lockSync() to ensure only one process is accessing the cache at a time.
// lockfile.lock() does not wait for async callback promise to resolve.
lockFile.lockSync(lockFilePath);
this._lockTaken = true;
break;
} catch (e) {
if (retryAttempt === retries) {
Logger.error(`MsalCachePlugin: Failed to acquire lock on cache file after ${retries} attempts.`);
throw new Error(`Failed to acquire lock on cache file after ${retries} attempts. Please attempt command: '${AccountsClearTokenCacheCommand}' to clear access token cache.`);
}
retryAttempt++;
Logger.verbose(`MsalCachePlugin: Failed to acquire lock on cache file. Retrying in ${retryWait} ms.`);
await new Promise(resolve => setTimeout(resolve, retryWait));
}
}
}
}

View File

@@ -5,7 +5,6 @@
import * as http from 'http'; import * as http from 'http';
import * as url from 'url'; import * as url from 'url';
import { AddressInfo } from 'net'; import { AddressInfo } from 'net';
import { Logger } from '../../utils/Logger';
export type WebHandler = (req: http.IncomingMessage, reqUrl: url.UrlWithParsedQuery, res: http.ServerResponse) => void; export type WebHandler = (req: http.IncomingMessage, reqUrl: url.UrlWithParsedQuery, res: http.ServerResponse) => void;
@@ -25,7 +24,7 @@ export class SimpleWebServer {
const time = new Date().getTime(); const time = new Date().getTime();
if (time - this.lastUsed > this.autoShutoffTimer) { if (time - this.lastUsed > this.autoShutoffTimer) {
Logger.verbose('Shutting off webserver...'); console.log('Shutting off webserver...');
this.shutdown().catch(console.error); this.shutdown().catch(console.error);
} }
}, 1000); }, 1000);

View File

@@ -12,13 +12,10 @@ import * as azureResourceUtils from './azureResource/utils';
import * as constants from './constants'; import * as constants from './constants';
import * as loc from './localizedConstants'; import * as loc from './localizedConstants';
import * as utils from './utils'; import * as utils from './utils';
import { Logger } from './utils/Logger';
const typesClause = [ const typesClause = [
azureResource.AzureResourceType.sqlDatabase, azureResource.AzureResourceType.sqlDatabase,
azureResource.AzureResourceType.sqlServer, azureResource.AzureResourceType.sqlServer,
azureResource.AzureResourceType.sqlSynapseWorkspace,
azureResource.AzureResourceType.sqlSynapseSqlPool,
azureResource.AzureResourceType.sqlManagedInstance, azureResource.AzureResourceType.sqlManagedInstance,
azureResource.AzureResourceType.postgresServer, azureResource.AzureResourceType.postgresServer,
azureResource.AzureResourceType.azureArcService, azureResource.AzureResourceType.azureArcService,
@@ -27,15 +24,13 @@ const typesClause = [
].map(type => `type == "${type}"`).join(' or '); ].map(type => `type == "${type}"`).join(' or ');
export class AzureDataGridProvider implements azdata.DataGridProvider { export class AzureDataGridProvider implements azdata.DataGridProvider {
constructor(private _appContext: AppContext, constructor(private _appContext: AppContext) { }
private readonly authLibrary: string) { }
public providerId = constants.dataGridProviderId; public providerId = constants.dataGridProviderId;
public title = loc.azureResourcesGridTitle; public title = loc.azureResourcesGridTitle;
public async getDataGridItems() { public async getDataGridItems() {
let accounts: azdata.Account[]; const accounts = await azdata.accounts.getAllAccounts();
accounts = azureResourceUtils.filterAccounts(await azdata.accounts.getAllAccounts(), this.authLibrary);
const items: any[] = []; const items: any[] = [];
await Promise.all(accounts.map(async (account) => { await Promise.all(accounts.map(async (account) => {
await Promise.all(account.properties.tenants.map(async (tenant: { id: string; }) => { await Promise.all(account.properties.tenants.map(async (tenant: { id: string; }) => {
@@ -61,10 +56,10 @@ export class AzureDataGridProvider implements azdata.DataGridProvider {
}); });
items.push(...newItems); items.push(...newItems);
} catch (err) { } catch (err) {
Logger.error(err); console.log(err);
} }
} catch (err) { } catch (err) {
Logger.error(err); console.log(err);
} }
})); }));
})); }));

View File

@@ -17,9 +17,9 @@ import { AzureResourceServiceNames } from './constants';
import { AzureAccount, Tenant, azureResource } from 'azurecore'; import { AzureAccount, Tenant, azureResource } from 'azurecore';
import { FlatAccountTreeNode } from './tree/flatAccountTreeNode'; import { FlatAccountTreeNode } from './tree/flatAccountTreeNode';
import { ConnectionDialogTreeProvider } from './tree/connectionDialogTreeProvider'; import { ConnectionDialogTreeProvider } from './tree/connectionDialogTreeProvider';
import { AzureResourceErrorMessageUtil, filterAccounts } from './utils'; import { AzureResourceErrorMessageUtil } from './utils';
export function registerAzureResourceCommands(appContext: AppContext, azureViewTree: AzureResourceTreeProvider, connectionDialogTree: ConnectionDialogTreeProvider, authLibrary: string): void { export function registerAzureResourceCommands(appContext: AppContext, azureViewTree: AzureResourceTreeProvider, connectionDialogTree: ConnectionDialogTreeProvider): void {
const trees = [azureViewTree, connectionDialogTree]; const trees = [azureViewTree, connectionDialogTree];
vscode.commands.registerCommand('azure.resource.startterminal', async (node?: TreeNode) => { vscode.commands.registerCommand('azure.resource.startterminal', async (node?: TreeNode) => {
try { try {
@@ -33,7 +33,7 @@ export function registerAzureResourceCommands(appContext: AppContext, azureViewT
if (node instanceof AzureResourceAccountTreeNode) { if (node instanceof AzureResourceAccountTreeNode) {
azureAccount = node.account; azureAccount = node.account;
} else { } else {
let accounts = filterAccounts(await azdata.accounts.getAllAccounts(), authLibrary); let accounts = await azdata.accounts.getAllAccounts();
accounts = accounts.filter(a => a.key.providerId.startsWith('azure')); accounts = accounts.filter(a => a.key.providerId.startsWith('azure'));
if (accounts.length === 0) { if (accounts.length === 0) {
const signin = localize('azure.signIn', "Sign in"); const signin = localize('azure.signIn', "Sign in");

View File

@@ -10,10 +10,6 @@ export enum AzureResourceItemType {
database = 'azure.resource.itemType.database', database = 'azure.resource.itemType.database',
databaseServerContainer = 'azure.resource.itemType.databaseServerContainer', databaseServerContainer = 'azure.resource.itemType.databaseServerContainer',
databaseServer = 'azure.resource.itemType.databaseServer', databaseServer = 'azure.resource.itemType.databaseServer',
synapseSqlPoolContainer = 'azure.resource.itemType.synapseSqlPoolContainer',
synapseSqlPool = 'azure.resource.itemType.synapseSqlPool',
synapseWorkspaceContainer = 'azure.resource.itemType.synapseWorkspaceContainer',
synapseWorkspace = 'azure.resource.itemType.synapseWorkspace',
azureDataExplorerContainer = 'azure.resource.itemType.azureDataExplorerContainer', azureDataExplorerContainer = 'azure.resource.itemType.azureDataExplorerContainer',
azureDataExplorer = 'azure.resource.itemType.azureDataExplorer', azureDataExplorer = 'azure.resource.itemType.azureDataExplorer',
sqlInstance = 'azure.resource.itemType.sqlInstance', sqlInstance = 'azure.resource.itemType.sqlInstance',
@@ -33,5 +29,3 @@ export enum AzureResourceServiceNames {
tenantService = 'AzureResourceTenantService', tenantService = 'AzureResourceTenantService',
terminalService = 'AzureTerminalService', terminalService = 'AzureTerminalService',
} }
export const mssqlProvider = 'MSSQL';

View File

@@ -41,7 +41,6 @@ export class AzureResourceMessageTreeNode extends TreeNode {
errorMessage: undefined, errorMessage: undefined,
metadata: undefined, metadata: undefined,
nodePath: this.generateNodePath(), nodePath: this.generateNodePath(),
parentNodePath: this.parent?.generateNodePath() ?? '',
nodeStatus: undefined, nodeStatus: undefined,
nodeType: AzureResourceItemType.message, nodeType: AzureResourceItemType.message,
nodeSubType: undefined, nodeSubType: undefined,

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { azureResource } from 'azurecore'; import { azureResource } from 'azurecore';
import { logAnalyticsQuery } from '../queryStringConstants';
import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase'; import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase';
export interface AzureMonitorGraphData extends GraphData { export interface AzureMonitorGraphData extends GraphData {
@@ -16,10 +15,12 @@ export interface AzureMonitorGraphData extends GraphData {
}; };
} }
const instanceQuery = `where type == "${azureResource.AzureResourceType.logAnalytics}"`;
export class AzureMonitorResourceService extends ResourceServiceBase<AzureMonitorGraphData, azureResource.AzureResourceDatabaseServer> { export class AzureMonitorResourceService extends ResourceServiceBase<AzureMonitorGraphData, azureResource.AzureResourceDatabaseServer> {
protected get query(): string { protected get query(): string {
return logAnalyticsQuery; return instanceQuery;
} }
protected convertResource(resource: AzureMonitorGraphData): azureResource.AzureResourceDatabaseServer { protected convertResource(resource: AzureMonitorGraphData): azureResource.AzureResourceDatabaseServer {

View File

@@ -6,7 +6,6 @@
import { ResourceServiceBase, GraphData } from '../../resourceTreeDataProviderBase'; import { ResourceServiceBase, GraphData } from '../../resourceTreeDataProviderBase';
import { azureResource } from 'azurecore'; import { azureResource } from 'azurecore';
import { cosmosMongoDbQuery } from '../../queryStringConstants';
interface DbServerGraphData extends GraphData { interface DbServerGraphData extends GraphData {
@@ -16,10 +15,12 @@ interface DbServerGraphData extends GraphData {
}; };
} }
const serversQuery = `where type == "${azureResource.AzureResourceType.cosmosDbAccount}" and kind == "MongoDB"`;
export class CosmosDbMongoService extends ResourceServiceBase<DbServerGraphData, azureResource.AzureResourceDatabaseServer> { export class CosmosDbMongoService extends ResourceServiceBase<DbServerGraphData, azureResource.AzureResourceDatabaseServer> {
protected get query(): string { protected get query(): string {
return cosmosMongoDbQuery; return serversQuery;
} }
protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer { protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer {

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ExtensionContext } from 'vscode'; import { ExtensionContext } from 'vscode';
import { azureResource } from 'azurecore'; import { azureResource } from 'azurecore';

View File

@@ -5,8 +5,8 @@
import { ServiceClientCredentials } from '@azure/ms-rest-js'; import { ServiceClientCredentials } from '@azure/ms-rest-js';
import { IAzureResourceService } from '../../interfaces'; import { IAzureResourceService } from '../../interfaces';
import { DbServerGraphData } from '../databaseServer/databaseServerService'; import { DbServerGraphData, SynapseWorkspaceGraphData } from '../databaseServer/databaseServerService';
import { sqlServerQuery, sqlDatabaseQuery } from '../queryStringConstants'; import { synapseWorkspacesQuery, sqlServersQuery } from '../databaseServer/serverQueryStrings';
import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase'; import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; import { AzureAccount, azureResource } from 'azurecore';
@@ -19,43 +19,69 @@ export class AzureResourceDatabaseService implements IAzureResourceService<azure
const databases: azureResource.AzureResourceDatabase[] = []; const databases: azureResource.AzureResourceDatabase[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint });
// Query servers and databases in parallel (start all promises before waiting on the 1st) // Query servers, synapse workspaces, and databases in parallel (start all promises before waiting on the 1st)
let serverQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, sqlServerQuery); let servers: DbServerGraphData[];
let dbQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, sqlDatabaseQuery); let synapseWorkspaces: SynapseWorkspaceGraphData[];
let servers: DbServerGraphData[] = await serverQueryPromise as DbServerGraphData[]; let combined: (DbServerGraphData | SynapseWorkspaceGraphData)[] = [];
/**
* We need to get the list of servers minus the Synapse Workspaces,
* then we need to make another query to get them.
*
* This is done because the first query provides invalid endpoints for Synapse Workspaces
* While the second one provides them as one of its properties.
*
* They have to be processed in different ways as their structure differs
* in terms of properties. (See databaseServer/databaseServerService.ts for more info)
*
* Queries must be made separately due to union not being recognized by resourceGraph resource calls
*/
let synapseQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, synapseWorkspacesQuery);
let serverQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, sqlServersQuery);
let dbQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, `where type == "${azureResource.AzureResourceType.sqlDatabase}"`);
servers = await serverQueryPromise as DbServerGraphData[];
synapseWorkspaces = await synapseQueryPromise as SynapseWorkspaceGraphData[];
let dbByGraph: DatabaseGraphData[] = await dbQueryPromise as DatabaseGraphData[]; let dbByGraph: DatabaseGraphData[] = await dbQueryPromise as DatabaseGraphData[];
combined = combined.concat(servers).concat(synapseWorkspaces);
// Group servers by resource group, then merge DB results with servers so we // Group servers by resource group, then merge DB results with servers so we
// can get the login name and server fully qualified name to use for connections // can get the login name and server fully qualified name to use for connections
let rgMap = new Map<string, (DbServerGraphData)[]>(); let rgMap = new Map<string, (DbServerGraphData | SynapseWorkspaceGraphData)[]>();
servers.forEach(s => { combined.forEach(s => {
let serversForRg = rgMap.get(s.resourceGroup) || []; if ((s as SynapseWorkspaceGraphData).properties.connectivityEndpoints) {
serversForRg.push(s); // If the resource is a Synapse Workspace, we need to use the managedResourceGroupName
rgMap.set(s.resourceGroup, serversForRg); // (any SQL pools inside will use this instead of the regular resource group associated with the workspace itself).
let serversForRg = rgMap.get((s as SynapseWorkspaceGraphData).properties.managedResourceGroupName) || [];
serversForRg.push(s as SynapseWorkspaceGraphData);
rgMap.set((s as SynapseWorkspaceGraphData).properties.managedResourceGroupName, serversForRg);
} else {
let serversForRg = rgMap.get(s.resourceGroup) || [];
serversForRg.push(s);
rgMap.set(s.resourceGroup, serversForRg);
}
}); });
// Match database ID. When calling exec [0] is full match, [1] is resource group name, [2] is server name // Match database ID. When calling exec [0] is full match, [1] is resource group name, [2] is server name
const svrIdRegExp = new RegExp(`\/subscriptions\/.+\/resourceGroups\/(.+)\/providers\/Microsoft\.Sql\/servers\/(.+)\/databases\/.+`); const svrIdRegExp = new RegExp(`\/subscriptions\/.+\/resourceGroups\/(.+)\/providers\/Microsoft\.Sql\/servers\/(.+)\/databases\/.+`);
const synapseDBRegExp = new RegExp(`\/subscriptions\/.+\/resourceGroups\/(.+)\/providers\/Microsoft\.Synapse\/workspaces\/(.+)\/databases\/.+`);
dbByGraph.forEach(db => { dbByGraph.forEach(db => {
// Filter master DBs, and for all others find their server to get login info // Filter master DBs, and for all others find their server to get login info
let serversForRg = rgMap.get(db.resourceGroup); let serversForRg = rgMap.get(db.resourceGroup);
if (serversForRg && !db.kind.endsWith('system') && (svrIdRegExp.test(db.id) || synapseDBRegExp.test(db.id))) { if (serversForRg && !db.kind.endsWith('system') && svrIdRegExp.test(db.id)) {
const founds = svrIdRegExp.exec(db.id) ?? synapseDBRegExp.exec(db.id); const founds = svrIdRegExp.exec(db.id);
if (!founds) { if (!founds) {
console.warn(`Could not parse server name from ID ${db.id}`); console.warn(`Could not parse server name from ID ${db.id}`);
return; return;
} }
const serverName = founds[2]; const serverName = founds[2];
let server = servers.find(s => s.name === serverName); let server = combined.find(s => s.name === serverName);
if (server) { if (server) {
databases.push({ databases.push({
name: db.name, name: db.name,
id: db.id, id: db.id,
serverName: server.name, serverName: server.name,
serverFullName: server.properties.fullyQualifiedDomainName, // Determine if server object is for Synapse Workspace or not and get the needed property from the correct place.
loginName: server.properties.administratorLogin, serverFullName: (server as SynapseWorkspaceGraphData).properties.connectivityEndpoints?.sql ?? (server as DbServerGraphData).properties.fullyQualifiedDomainName,
loginName: (server as SynapseWorkspaceGraphData).properties.sqlAdministratorLogin ?? (server as DbServerGraphData).properties.administratorLogin,
subscription: { subscription: {
id: db.subscriptionId, id: db.subscriptionId,
name: (subscriptions.find(sub => sub.id === db.subscriptionId))?.name || '' name: (subscriptions.find(sub => sub.id === db.subscriptionId))?.name || ''

View File

@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType, mssqlProvider } from '../../../azureResource/constants'; import { AzureResourceItemType } from '../../../azureResource/constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { IAzureResourceService } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
@@ -25,7 +25,6 @@ export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProvi
) { ) {
super(databaseService); super(databaseService);
} }
protected getTreeItemForResource(database: azureResource.AzureResourceDatabase, account: AzureAccount): TreeItem { protected getTreeItemForResource(database: azureResource.AzureResourceDatabase, account: AzureAccount): TreeItem {
return { return {
id: `databaseServer_${database.serverFullName}.database_${database.name}`, id: `databaseServer_${database.serverFullName}.database_${database.name}`,
@@ -47,7 +46,7 @@ export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProvi
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
providerName: mssqlProvider, providerName: 'MSSQL',
saveProfile: false, saveProfile: false,
options: {}, options: {},
azureAccount: account.key.accountId, azureAccount: account.key.accountId,
@@ -55,7 +54,7 @@ export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProvi
azureTenantId: database.tenant, azureTenantId: database.tenant,
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
}, },
childProvider: mssqlProvider, childProvider: 'MSSQL',
type: ExtensionNodeType.Database type: ExtensionNodeType.Database
}; };
} }

View File

@@ -6,8 +6,8 @@
import { ServiceClientCredentials } from '@azure/ms-rest-js'; import { ServiceClientCredentials } from '@azure/ms-rest-js';
import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { GraphData, queryGraphResources } from '../resourceTreeDataProviderBase'; import { GraphData, queryGraphResources } from '../resourceTreeDataProviderBase';
import { sqlServerQuery } from '../queryStringConstants';
import { azureResource, AzureAccount } from 'azurecore'; import { azureResource, AzureAccount } from 'azurecore';
import { sqlServersQuery, synapseWorkspacesQuery } from './serverQueryStrings';
import { IAzureResourceService } from '../../interfaces'; import { IAzureResourceService } from '../../interfaces';
export interface DbServerGraphData extends GraphData { export interface DbServerGraphData extends GraphData {
@@ -17,14 +17,54 @@ export interface DbServerGraphData extends GraphData {
}; };
} }
/**
* Properties returned by the Synapse query are different from the server ones and have to be treated differently.
*/
export interface SynapseWorkspaceGraphData extends GraphData {
properties: {
/**
* SQL connectivity endpoint and other endpoints are found here, instead of fullyQualifiedDomainName.
*/
connectivityEndpoints: { sql: string };
/**
* managedResourceGroupName is the resource group used by any SQL pools inside the workspace
* which is different from the resource group of the workspace itself.
*/
managedResourceGroupName: string;
/**
* administratorLogin is called sqlAdministratorLogin here.
*/
sqlAdministratorLogin: string;
};
}
export class AzureResourceDatabaseServerService implements IAzureResourceService<azureResource.AzureResourceDatabaseServer> { export class AzureResourceDatabaseServerService implements IAzureResourceService<azureResource.AzureResourceDatabaseServer> {
protected get query(): string {
return sqlServersQuery;
}
public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabaseServer[]> { public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabaseServer[]> {
const convertedResources: azureResource.AzureResourceDatabaseServer[] = []; const convertedResources: azureResource.AzureResourceDatabaseServer[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint });
let serverGraphResources: DbServerGraphData[] = await queryGraphResources<DbServerGraphData>(resourceClient, subscriptions, sqlServerQuery); /**
* We need to get the list of servers minus the Synapse Workspaces,
* then we need to make another query to get them.
*
* This is done because the first query provides invalid endpoints for Synapse Workspaces
* While the second one provides them as one of its properties.
*
* They have to be processed in different ways by convertResource as their structure differs
* in terms of properties. (See above)
*
* Queries must be made separately due to union not being recognized by resourceGraph resource calls.
*/
let combinedGraphResources: (DbServerGraphData | SynapseWorkspaceGraphData)[] = [];
let serverGraphResources: DbServerGraphData[] = await queryGraphResources<DbServerGraphData>(resourceClient, subscriptions, this.query);
let synapseGraphResources: SynapseWorkspaceGraphData[] = await queryGraphResources<SynapseWorkspaceGraphData>(resourceClient, subscriptions, synapseWorkspacesQuery);
combinedGraphResources = combinedGraphResources.concat(serverGraphResources).concat(synapseGraphResources);
const ids = new Set<string>(); const ids = new Set<string>();
serverGraphResources.forEach((res) => { combinedGraphResources.forEach((res) => {
if (!ids.has(res.id)) { if (!ids.has(res.id)) {
ids.add(res.id); ids.add(res.id);
res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name; res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name;
@@ -36,14 +76,14 @@ export class AzureResourceDatabaseServerService implements IAzureResourceService
return convertedResources; return convertedResources;
} }
protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer { protected convertResource(resource: DbServerGraphData | SynapseWorkspaceGraphData): azureResource.AzureResourceDatabaseServer {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
// Determine if resource object is for Synapse Workspace or not and get the needed property from the correct place. // Determine if resource object is for Synapse Workspace or not and get the needed property from the correct place.
fullName: resource.properties.fullyQualifiedDomainName, fullName: (resource as SynapseWorkspaceGraphData).properties.connectivityEndpoints?.sql ?? (resource as DbServerGraphData).properties.fullyQualifiedDomainName,
loginName: resource.properties.administratorLogin, loginName: (resource as SynapseWorkspaceGraphData).properties.sqlAdministratorLogin ?? (resource as DbServerGraphData).properties.administratorLogin,
defaultDatabaseName: 'master', defaultDatabaseName: 'master',
subscription: { subscription: {
id: resource.subscriptionId, id: resource.subscriptionId,

View File

@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType, mssqlProvider } from '../../../azureResource/constants'; import { AzureResourceItemType } from '../../../azureResource/constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { IAzureResourceService } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
@@ -25,6 +25,7 @@ export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDat
super(databaseServerService); super(databaseServerService);
} }
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
return { return {
id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`, id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`,
@@ -46,7 +47,7 @@ export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDat
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
providerName: mssqlProvider, providerName: 'MSSQL',
saveProfile: false, saveProfile: false,
options: {}, options: {},
azureAccount: account.key.accountId, azureAccount: account.key.accountId,
@@ -54,7 +55,7 @@ export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDat
azureResourceId: databaseServer.id, azureResourceId: databaseServer.id,
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
}, },
childProvider: mssqlProvider, childProvider: 'MSSQL',
type: ExtensionNodeType.Server type: ExtensionNodeType.Server
}; };
} }

View File

@@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { azureResource } from 'azurecore';
/**
* Get list of Synapse Workspaces with information such as SQL connection endpoints.
*/
export const synapseWorkspacesQuery = `where type == "microsoft.synapse/workspaces"`;
/**
* Lists all Sql Servers except for Synapse Pool Servers
* (they have different properties and need to be handled separately,
* see databaseServerService.ts for more details)
*/
export const sqlServersQuery = `where type == "${azureResource.AzureResourceType.sqlServer}" and kind != "v12.0,analytics"`;

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { azureResource } from 'azurecore'; import { azureResource } from 'azurecore';
import { kustoClusterQuery } from '../queryStringConstants';
import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase'; import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase';
export interface KustoGraphData extends GraphData { export interface KustoGraphData extends GraphData {
@@ -15,10 +14,12 @@ export interface KustoGraphData extends GraphData {
}; };
} }
const instanceQuery = `where type == "${azureResource.AzureResourceType.kustoClusters}"`;
export class KustoResourceService extends ResourceServiceBase<KustoGraphData, azureResource.AzureResourceDatabaseServer> { export class KustoResourceService extends ResourceServiceBase<KustoGraphData, azureResource.AzureResourceDatabaseServer> {
protected get query(): string { protected get query(): string {
return kustoClusterQuery; return instanceQuery;
} }
protected convertResource(resource: KustoGraphData): azureResource.AzureResourceDatabaseServer { protected convertResource(resource: KustoGraphData): azureResource.AzureResourceDatabaseServer {

Some files were not shown because too many files have changed in this diff Show More