Compare commits
161 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdb81bc9b1 | ||
|
|
023e10b496 | ||
|
|
27cb693bd5 | ||
|
|
80a9d94648 | ||
|
|
6fad30bf3c | ||
|
|
a0c2dc199e | ||
|
|
95980130c8 | ||
|
|
8cc0fdfdc5 | ||
|
|
f83605cbf4 | ||
|
|
90d6defa05 | ||
|
|
ca904ed380 | ||
|
|
784a9270c8 | ||
|
|
d585e75706 | ||
|
|
01509de495 | ||
|
|
fa227c88c9 | ||
|
|
cb607e34d7 | ||
|
|
de9a4e02b1 | ||
|
|
820838fae8 | ||
|
|
85f1f3e0dc | ||
|
|
2d1ffeb47c | ||
|
|
a86301312c | ||
|
|
206188f3ab | ||
|
|
d8a8107dc6 | ||
|
|
26e6cd28c2 | ||
|
|
02093f8497 | ||
|
|
62a0bcc69f | ||
|
|
7181d4c79e | ||
|
|
4551329db0 | ||
|
|
5c14fe4f4c | ||
|
|
d1d6b229ce | ||
|
|
94b4437a6a | ||
|
|
ebdfea25ad | ||
|
|
5f2d8b2bd5 | ||
|
|
85d6b2ef46 | ||
|
|
f424bea0de | ||
|
|
364684c79e | ||
|
|
4d7467a5d5 | ||
|
|
b299f7ed3f | ||
|
|
0c54c12772 | ||
|
|
65b4847874 | ||
|
|
33d2735970 | ||
|
|
822199c9be | ||
|
|
e50bded5d1 | ||
|
|
27763c860c | ||
|
|
634b9f8d08 | ||
|
|
3844638156 | ||
|
|
eb77d86136 | ||
|
|
025e0831e7 | ||
|
|
5bb35d4eed | ||
|
|
f013a88163 | ||
|
|
4f9d45335e | ||
|
|
fee8e78011 | ||
|
|
1be5c926d3 | ||
|
|
72632bc376 | ||
|
|
9890cc79c0 | ||
|
|
b3107dc4b1 | ||
|
|
eccb77aca3 | ||
|
|
5d0f0afdc6 | ||
|
|
33259764f7 | ||
|
|
c9aa3e9f4b | ||
|
|
8e9957adbe | ||
|
|
442c16bc3c | ||
|
|
1c83aa61d7 | ||
|
|
9e3d678536 | ||
|
|
9a944b614d | ||
|
|
bc78f6a573 | ||
|
|
da076c152c | ||
|
|
a2c39369c9 | ||
|
|
be6b23eff0 | ||
|
|
0ab4752643 | ||
|
|
10ff8aa116 | ||
|
|
4f61be2495 | ||
|
|
d5fa5fca3a | ||
|
|
8b543d1d12 | ||
|
|
87f1e26e39 | ||
|
|
9990f7c8e2 | ||
|
|
d6a08a60d9 | ||
|
|
628c8da716 | ||
|
|
79cda5cbe5 | ||
|
|
c34de52a03 | ||
|
|
eb928e0b8f | ||
|
|
84001139cc | ||
|
|
00f98457e8 | ||
|
|
ebd4c6844b | ||
|
|
3b4800de51 | ||
|
|
7b5117b185 | ||
|
|
d36b755d6d | ||
|
|
96729a4817 | ||
|
|
d8fd5b36f7 | ||
|
|
dfa6799fcc | ||
|
|
c64655ae71 | ||
|
|
5e93f2ca9b | ||
|
|
7db7c2927c | ||
|
|
ffdefd3b52 | ||
|
|
02341088eb | ||
|
|
8032f59d41 | ||
|
|
0193f8a6dc | ||
|
|
23cbb98162 | ||
|
|
9b3d65b1d4 | ||
|
|
e2b4dd5a79 | ||
|
|
19c7971630 | ||
|
|
5731366a1c | ||
|
|
ef100b8e82 | ||
|
|
7b92c9eb65 | ||
|
|
5547c2baba | ||
|
|
40ee82ee3e | ||
|
|
3c84575755 | ||
|
|
696db01dc8 | ||
|
|
e0cb88599d | ||
|
|
eff847f35a | ||
|
|
2da0428454 | ||
|
|
1b63fec4e5 | ||
|
|
f137174719 | ||
|
|
75d9246f5e | ||
|
|
cbaffd0b0d | ||
|
|
b33afe3efb | ||
|
|
19f63ea88f | ||
|
|
a1a1793221 | ||
|
|
8da49ff14e | ||
|
|
f0ebc6ed60 | ||
|
|
aaf174e85c | ||
|
|
13dd74efcc | ||
|
|
bafbca492b | ||
|
|
3472e229f1 | ||
|
|
3a04235001 | ||
|
|
0f385b386f | ||
|
|
28ab7372d2 | ||
|
|
e66192cbfd | ||
|
|
63cff7a5ab | ||
|
|
f1b23c96a9 | ||
|
|
ede0937c2b | ||
|
|
83698a14b0 | ||
|
|
2be1394748 | ||
|
|
8135867e06 | ||
|
|
87d5da00bf | ||
|
|
678f2e3878 | ||
|
|
81e8eb9a82 | ||
|
|
fe360afec7 | ||
|
|
80c8b06ec8 | ||
|
|
7ff42eeb08 | ||
|
|
3dbd5ac2c1 | ||
|
|
3c321ee8b7 | ||
|
|
f6128d262b | ||
|
|
d24a289af5 | ||
|
|
89cc59a2fd | ||
|
|
25f563229f | ||
|
|
33ea2d8ee6 | ||
|
|
9e8b01fd1e | ||
|
|
bb468a5b98 | ||
|
|
9a28d47c5b | ||
|
|
45763e12b4 | ||
|
|
316c6a536f | ||
|
|
f5392e3c8f | ||
|
|
4dd59c6b8d | ||
|
|
9e574ae602 | ||
|
|
1dd7e93063 | ||
|
|
13276cf52e | ||
|
|
baa6e4b380 | ||
|
|
3bc5306258 | ||
|
|
c45cfaf01b | ||
|
|
0612a8ac35 |
@@ -212,6 +212,7 @@
|
||||
"restrictions": [
|
||||
"vs/nls",
|
||||
"azdata",
|
||||
"azurecore",
|
||||
"**/{vs,sql}/base/common/**",
|
||||
"**/{vs,sql}/base/parts/*/common/**",
|
||||
"**/{vs,sql}/platform/*/common/**"
|
||||
@@ -472,6 +473,7 @@
|
||||
"restrictions": [
|
||||
"vscode",
|
||||
"azdata",
|
||||
"azurecore",
|
||||
"vs/nls",
|
||||
"**/{vs,sql}/base/common/**",
|
||||
"**/{vs,sql}/platform/*/common/**",
|
||||
@@ -577,6 +579,7 @@
|
||||
"vs/nls",
|
||||
"vs/css!./**/*",
|
||||
"azdata",
|
||||
"azurecore",
|
||||
"vscode",
|
||||
"**/{vs,sql}/base/**/{common,browser,worker}/**",
|
||||
"**/{vs,sql}/platform/**/{common,browser}/**",
|
||||
|
||||
16
.github/label-actions.yml
vendored
@@ -27,6 +27,22 @@ There are two types of logs to collect:
|
||||
|
||||
- This will open the log folder locally. Please zip up this folder and attach it to the issue."
|
||||
|
||||
# actions for Needs Logs - Azure label
|
||||
Needs Logs - Azure:
|
||||
comment: "We need more info to debug your Azure Active Directory issue. If you could attach your logs to the issue (ensure no private data is in them), it would help us fix the issue much faster.
|
||||
|
||||
- In the settings menu, find the setting titled `Azure: Logging Level` and select the `Verbose` option
|
||||
|
||||
- Run the process that produces your error
|
||||
|
||||
- Open command palette (Click **View** -> **Command Palette**)
|
||||
|
||||
- Run the command: **`Developer: Open Logs Folder`**
|
||||
|
||||
- Follow this path to find the Azure Accounts log file: `[default log folder]/exthost1/output_logging_[earliest timestamp]/#-Azure Acounts.log`
|
||||
|
||||
- Please attach the Azure-Accounts.log file to the issue."
|
||||
|
||||
# actions for Out of Scope label
|
||||
Out of Scope:
|
||||
comment: "Thank you for opening this suggestion! This enhancement is not planned in our
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
jobs:
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2019
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
CHILD_CONCURRENCY: "1"
|
||||
|
||||
58
CHANGELOG.md
@@ -1,5 +1,43 @@
|
||||
# Change Log
|
||||
|
||||
## Version 1.35.1
|
||||
* Release date: March 17, 2022
|
||||
* Release status: General Availability
|
||||
## Hotfix release
|
||||
- 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)
|
||||
|
||||
| Platform |
|
||||
| --------------------------------------- |
|
||||
| [Windows User Installer][win-user] |
|
||||
| [Windows System Installer][win-system] |
|
||||
| [Windows ZIP][win-zip] |
|
||||
| [macOS ZIP][osx-zip] |
|
||||
| [Linux TAR.GZ][linux-zip] |
|
||||
| [Linux RPM][linux-rpm] |
|
||||
| [Linux DEB][linux-deb] |
|
||||
|
||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2187459
|
||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2187520
|
||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2187460
|
||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2187461
|
||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2187462
|
||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2187521
|
||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2187522
|
||||
|
||||
## Version 1.35.0
|
||||
* Release date: February 24, 2022
|
||||
* Release status: General Availability
|
||||
## What's new in this version
|
||||
* New Features:
|
||||
* 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
|
||||
* Azure Arc Extension - Updated the Data Controller deployment wizard and the SQL Managed Instance - Azure Arc deployment wizard to reflect the deployment experience in Azure Portal
|
||||
|
||||
* Bug Fixes:
|
||||
* Azure Arc Extension - SQL Managed Instance-Azure Arc is now fixed for both indirect connectivity mode and direct connectivity mode
|
||||
* Notebooks - Support for keyboard navigation between cells to minimize mouse clicking
|
||||
|
||||
## Version 1.34.0
|
||||
* Release date: December 15, 2021
|
||||
* Release status: General Availability
|
||||
@@ -31,26 +69,6 @@
|
||||
- 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)
|
||||
|
||||
|
||||
|
||||
| Platform |
|
||||
| --------------------------------------- |
|
||||
| [Windows User Installer][win-user] |
|
||||
| [Windows System Installer][win-system] |
|
||||
| [Windows ZIP][win-zip] |
|
||||
| [macOS ZIP][osx-zip] |
|
||||
| [Linux TAR.GZ][linux-zip] |
|
||||
| [Linux RPM][linux-rpm] |
|
||||
| [Linux DEB][linux-deb] |
|
||||
|
||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2176805
|
||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2175910
|
||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2176806
|
||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2176807
|
||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2176505
|
||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2176005
|
||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2176006
|
||||
|
||||
## Version 1.33.0
|
||||
* Release date: October 27, 2021
|
||||
* Release status: General Availability
|
||||
|
||||
14
README.md
@@ -131,10 +131,10 @@ Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Licensed under the [Source EULA](LICENSE.txt).
|
||||
|
||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2183280
|
||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2183423
|
||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2183190
|
||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2183189
|
||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2183277
|
||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2183342
|
||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2183341
|
||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2187459
|
||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2187520
|
||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2187460
|
||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2187461
|
||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2187522
|
||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2187521
|
||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2187462
|
||||
|
||||
@@ -128,14 +128,24 @@ steps:
|
||||
yarn gulp compile-extensions
|
||||
displayName: Compile Extensions
|
||||
|
||||
# Per https://developercommunity.visualstudio.com/t/variablesexpressions-dont-work-with-continueonerro/1187733 we can't use variables
|
||||
# in continueOnError directly so instead make two copies of the task and only run one or the other based on the SMOKE_FAIL_ON_ERROR value
|
||||
- script: |
|
||||
set -e
|
||||
APP_ROOT=$(agent.builddirectory)/azuredatastudio-darwin-x64
|
||||
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||
yarn smoketest --build "$APP_ROOT/$APP_NAME" --screenshots "$(build.artifactstagingdirectory)/smokeshots" --log "$(build.artifactstagingdirectory)/logs/darwin/smoke.log" --extensionsDir "$(build.sourcesdirectory)/extensions"
|
||||
displayName: Run smoke tests (Electron)
|
||||
displayName: Run smoke tests (Electron) (Continue on Error)
|
||||
continueOnError: true
|
||||
condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'))
|
||||
condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), ne(variables['SMOKE_FAIL_ON_ERROR'], 'true')))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
APP_ROOT=$(agent.builddirectory)/azuredatastudio-darwin-x64
|
||||
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||
yarn smoketest --build "$APP_ROOT/$APP_NAME" --screenshots "$(build.artifactstagingdirectory)/smokeshots" --log "$(build.artifactstagingdirectory)/logs/darwin/smoke.log" --extensionsDir "$(build.sourcesdirectory)/extensions"
|
||||
displayName: Run smoke tests (Electron) (Fail on Error)
|
||||
condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), eq(variables['SMOKE_FAIL_ON_ERROR'], 'true')))
|
||||
|
||||
# - script: |
|
||||
# set -e
|
||||
|
||||
@@ -6,6 +6,9 @@ trigger:
|
||||
|
||||
pr: none
|
||||
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
steps:
|
||||
- template: linux/sql-product-build-linux.yml
|
||||
parameters:
|
||||
extensionsToUnitTest: ["admin-tool-ext-win", "agent", "azcli", "azurecore", "cms", "dacpac", "data-workspace", "import", "machine-learning", "notebook", "resource-deployment", "schema-compare", "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
|
||||
|
||||
- job: Windows
|
||||
@@ -60,16 +60,17 @@ jobs:
|
||||
- template: win32/sql-product-build-win32.yml
|
||||
timeoutInMinutes: 90
|
||||
|
||||
- job: Windows_Test
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true'))
|
||||
pool:
|
||||
name: mssqltools
|
||||
dependsOn:
|
||||
- Linux
|
||||
- Windows
|
||||
steps:
|
||||
- template: win32/sql-product-test-win32.yml
|
||||
timeoutInMinutes: 90
|
||||
# disable due to invalid machine pool (karlb 3/9/2022)
|
||||
# - job: Windows_Test
|
||||
# condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true'))
|
||||
# pool:
|
||||
# name: mssqltools
|
||||
# dependsOn:
|
||||
# - Linux
|
||||
# - Windows
|
||||
# steps:
|
||||
# - template: win32/sql-product-test-win32.yml
|
||||
# timeoutInMinutes: 90
|
||||
|
||||
- job: Release
|
||||
condition: and(succeeded(), or(eq(variables['VSCODE_RELEASE'], 'true'), and(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['Build.Reason'], 'Schedule'))))
|
||||
@@ -79,7 +80,8 @@ jobs:
|
||||
- macOS
|
||||
- Linux
|
||||
- Windows
|
||||
- Windows_Test
|
||||
# disable due to invalid machine pool (karlb 3/9/2022)
|
||||
# - Windows_Test
|
||||
- macOS_Signing
|
||||
steps:
|
||||
- template: sql-release.yml
|
||||
|
||||
@@ -93,6 +93,7 @@ steps:
|
||||
AZURE_STORAGE_ACCESS_KEY="$(sourcemap-storage-key)" \
|
||||
node build/azure-pipelines/upload-sourcemaps
|
||||
displayName: Upload sourcemaps
|
||||
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
@@ -104,14 +104,26 @@ steps:
|
||||
yarn gulp compile-extensions
|
||||
displayName: Compile Extensions
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
node ./node_modules/playwright/install.js
|
||||
APP_ROOT=$(Agent.BuildDirectory)/vscode-reh-web-linux-x64
|
||||
xvfb-run yarn smoketest --build "$(Agent.BuildDirectory)/vscode-reh-web-linux-x64" --web --headless --screenshots "$(Build.ArtifactStagingDirectory)/smokeshots" --log "$(Build.ArtifactStagingDirectory)/logs/web/smoke.log"
|
||||
displayName: Run smoke tests (Browser)
|
||||
continueOnError: true
|
||||
condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'))
|
||||
# disable smoke tests (karlb 3/2/2022)
|
||||
# # Per https://developercommunity.visualstudio.com/t/variablesexpressions-dont-work-with-continueonerro/1187733 we can't use variables
|
||||
# # in continueOnError directly so instead make two copies of the task and only run one or the other based on the SMOKE_FAIL_ON_ERROR value
|
||||
# - script: |
|
||||
# set -e
|
||||
# node ./node_modules/playwright/install.js
|
||||
# APP_ROOT=$(Agent.BuildDirectory)/vscode-reh-web-linux-x64
|
||||
# xvfb-run yarn smoketest --build "$(Agent.BuildDirectory)/vscode-reh-web-linux-x64" --web --headless --screenshots "$(Build.ArtifactStagingDirectory)/smokeshots" --log "$(Build.ArtifactStagingDirectory)/logs/web/smoke.log"
|
||||
# displayName: Run smoke tests (Browser) (Continue on Error)
|
||||
# continueOnError: true
|
||||
# condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), ne(variables['SMOKE_FAIL_ON_ERROR'], 'true')))
|
||||
|
||||
# disable smoke tests (karlb 3/2/2022)
|
||||
# - script: |
|
||||
# set -e
|
||||
# node ./node_modules/playwright/install.js
|
||||
# APP_ROOT=$(Agent.BuildDirectory)/vscode-reh-web-linux-x64
|
||||
# xvfb-run yarn smoketest --build "$(Agent.BuildDirectory)/vscode-reh-web-linux-x64" --web --headless --screenshots "$(Build.ArtifactStagingDirectory)/smokeshots" --log "$(Build.ArtifactStagingDirectory)/logs/web/smoke.log"
|
||||
# displayName: Run smoke tests (Browser) (Fail on Error)
|
||||
# condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), eq(variables['SMOKE_FAIL_ON_ERROR'], 'true')))
|
||||
|
||||
# - script: |
|
||||
# set -e
|
||||
|
||||
@@ -129,16 +129,19 @@ gulp.task('package-external-extensions', task.series(
|
||||
const packageManifestPath = path.join(packageDir, 'package.json');
|
||||
const json = require('gulp-json-editor');
|
||||
const packageJsonStream = gulp.src(packageManifestPath) // Create stream for the original package.json
|
||||
.pipe(json(data => { // And now use gulp-json-editor to modify the contents
|
||||
.pipe(json(data => {
|
||||
// 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
|
||||
Object.keys(updateData).forEach(key => {
|
||||
data[key] = updateData[key];
|
||||
});
|
||||
// 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.
|
||||
['objectExplorer/item/context', 'dataExplorer/context', 'dashboard/toolbar'].forEach(menu => {
|
||||
delete data.contributes.menus[menu];
|
||||
});
|
||||
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
|
||||
// More can be added to the list as needed.
|
||||
['objectExplorer/item/context', 'dataExplorer/context', 'dashboard/toolbar'].forEach(menu => {
|
||||
delete data.contributes.menus[menu];
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}, { beautify: false }))
|
||||
.pipe(gulp.dest(packageDir));
|
||||
|
||||
@@ -135,6 +135,7 @@ const extensionsFilter = filter([
|
||||
'**/schema-compare.xlf',
|
||||
'**/server-report.xlf',
|
||||
'**/sql-assessment.xlf',
|
||||
'**/sql-bindings.xlf',
|
||||
'**/sql-database-projects.xlf',
|
||||
'**/sql-migration.xlf',
|
||||
'**/xml-language-features.xlf'
|
||||
|
||||
@@ -231,6 +231,7 @@ const externalExtensions = [
|
||||
'schema-compare',
|
||||
'server-report',
|
||||
'sql-assessment',
|
||||
'sql-bindings',
|
||||
'sql-database-projects',
|
||||
'sql-migration'
|
||||
];
|
||||
|
||||
@@ -267,6 +267,7 @@ const externalExtensions = [
|
||||
'schema-compare',
|
||||
'server-report',
|
||||
'sql-assessment',
|
||||
'sql-bindings',
|
||||
'sql-database-projects',
|
||||
'sql-migration'
|
||||
];
|
||||
|
||||
@@ -18,6 +18,13 @@ const File = require("vinyl");
|
||||
const rimraf = require("rimraf");
|
||||
const gulp = require("gulp");
|
||||
const vfs = require("vinyl-fs");
|
||||
/**
|
||||
* If you need to compile this file for any changes, please run: yarn tsc -p ./build/tsconfig.json
|
||||
*/
|
||||
//List of extensions that we changed from vscode, so we can exclude them from having "Microsoft." appended in front.
|
||||
const alteredVSCodeExtensions = [
|
||||
'git'
|
||||
];
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
// Modified packageLocalExtensionsStream from extensions.ts, but for langpacks.
|
||||
function packageLangpacksStream() {
|
||||
@@ -134,10 +141,13 @@ function modifyI18nPackFiles(existingTranslationFolder, resultingTranslationPath
|
||||
for (let extension in extensionsPacks) {
|
||||
const translatedExtFile = i18n.createI18nFile(`extensions/${extension}`, extensionsPacks[extension]);
|
||||
this.queue(translatedExtFile);
|
||||
//handle edge case for 'Microsoft.sqlservernotebook' where extension name is the same as extension ID.
|
||||
//(Other extensions need to have publisher appended in front as their ID.)
|
||||
const adsExtensionId = (extension === 'Microsoft.sqlservernotebook') ? extension : 'Microsoft.' + extension;
|
||||
resultingTranslationPaths.push({ id: adsExtensionId, resourceName: `extensions/${extension}.i18n.json` });
|
||||
// exclude altered vscode extensions from having a new path even if we provide a new I18n file.
|
||||
if (alteredVSCodeExtensions.indexOf(extension) === -1) {
|
||||
//handle edge case for 'Microsoft.sqlservernotebook' where extension name is the same as extension ID.
|
||||
//(Other extensions need to have publisher appended in front as their ID.)
|
||||
let adsExtensionId = (extension === 'Microsoft.sqlservernotebook') ? extension : 'Microsoft.' + extension;
|
||||
resultingTranslationPaths.push({ id: adsExtensionId, resourceName: `extensions/${extension}.i18n.json` });
|
||||
}
|
||||
}
|
||||
this.queue(null);
|
||||
})
|
||||
@@ -162,7 +172,6 @@ const VSCODEExtensions = [
|
||||
"bat",
|
||||
"configuration-editing",
|
||||
"docker",
|
||||
"extension-editing",
|
||||
"git-ui",
|
||||
"git",
|
||||
"github-authentication",
|
||||
|
||||
@@ -17,6 +17,15 @@ import * as rimraf from 'rimraf';
|
||||
import * as gulp from 'gulp';
|
||||
import * as vfs from 'vinyl-fs';
|
||||
|
||||
/**
|
||||
* If you need to compile this file for any changes, please run: yarn tsc -p ./build/tsconfig.json
|
||||
*/
|
||||
|
||||
//List of extensions that we changed from vscode, so we can exclude them from having "Microsoft." appended in front.
|
||||
const alteredVSCodeExtensions = [
|
||||
'git'
|
||||
]
|
||||
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
|
||||
// Modified packageLocalExtensionsStream from extensions.ts, but for langpacks.
|
||||
@@ -68,7 +77,7 @@ function updateMainI18nFile(existingTranslationFilePath: string, originalFilePat
|
||||
|
||||
// Delete any SQL strings that are no longer part of ADS in current langpack.
|
||||
for (let contentKey of Object.keys(objectContents)) {
|
||||
if(contentKey.startsWith('sql') && messages.contents[contentKey] === undefined){
|
||||
if (contentKey.startsWith('sql') && messages.contents[contentKey] === undefined) {
|
||||
delete objectContents[`${contentKey}`]
|
||||
}
|
||||
}
|
||||
@@ -148,10 +157,14 @@ export function modifyI18nPackFiles(existingTranslationFolder: string, resulting
|
||||
const translatedExtFile = i18n.createI18nFile(`extensions/${extension}`, extensionsPacks[extension]);
|
||||
this.queue(translatedExtFile);
|
||||
|
||||
//handle edge case for 'Microsoft.sqlservernotebook' where extension name is the same as extension ID.
|
||||
//(Other extensions need to have publisher appended in front as their ID.)
|
||||
const adsExtensionId = (extension === 'Microsoft.sqlservernotebook') ? extension : 'Microsoft.' + extension;
|
||||
resultingTranslationPaths.push({ id: adsExtensionId, resourceName: `extensions/${extension}.i18n.json` });
|
||||
// exclude altered vscode extensions from having a new path even if we provide a new I18n file.
|
||||
if (alteredVSCodeExtensions.indexOf(extension) === -1) {
|
||||
//handle edge case for 'Microsoft.sqlservernotebook' where extension name is the same as extension ID.
|
||||
//(Other extensions need to have publisher appended in front as their ID.)
|
||||
let adsExtensionId = (extension === 'Microsoft.sqlservernotebook') ? extension : 'Microsoft.' + extension;
|
||||
|
||||
resultingTranslationPaths.push({ id: adsExtensionId, resourceName: `extensions/${extension}.i18n.json` });
|
||||
}
|
||||
}
|
||||
this.queue(null);
|
||||
})
|
||||
@@ -399,7 +412,7 @@ export function renameVscodeLangpacks(): Promise<void> {
|
||||
|
||||
//Copy files to vscode langpack, then remove the ADS langpack, and finally rename the vscode langpack to match the ADS one.
|
||||
globMDArray.forEach(element => {
|
||||
fs.copyFileSync(element, path.join(locVSCODEFolder,path.parse(element).base));
|
||||
fs.copyFileSync(element, path.join(locVSCODEFolder, path.parse(element).base));
|
||||
});
|
||||
rimraf.sync(locADSFolder);
|
||||
fs.renameSync(locVSCODEFolder, locADSFolder);
|
||||
|
||||
@@ -48,6 +48,7 @@ exports.dirs = [
|
||||
'extensions/server-report',
|
||||
'extensions/simple-browser',
|
||||
'extensions/sql-assessment',
|
||||
'extensions/sql-bindings',
|
||||
'extensions/sql-database-projects',
|
||||
'extensions/sql-migration',
|
||||
'extensions/vscode-test-resolver',
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
"mime": "^1.4.1",
|
||||
"mkdirp": "^1.0.4",
|
||||
"p-limit": "^3.1.0",
|
||||
"plist": "^3.0.1",
|
||||
"plist": "^3.0.4",
|
||||
"rollup": "^1.20.3",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
|
||||
@@ -783,7 +783,7 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-js@^1.2.3:
|
||||
base64-js@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
@@ -2038,14 +2038,13 @@ pify@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
|
||||
|
||||
plist@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c"
|
||||
integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==
|
||||
plist@^3.0.1, plist@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe"
|
||||
integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==
|
||||
dependencies:
|
||||
base64-js "^1.2.3"
|
||||
base64-js "^1.5.1"
|
||||
xmlbuilder "^9.0.7"
|
||||
xmldom "0.1.x"
|
||||
|
||||
"postcss@5 - 7":
|
||||
version "7.0.36"
|
||||
@@ -2676,11 +2675,6 @@ xmlbuilder@~11.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
|
||||
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
|
||||
|
||||
xmldom@0.1.x:
|
||||
version "0.1.31"
|
||||
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff"
|
||||
integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==
|
||||
|
||||
yallist@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||
|
||||
@@ -52,9 +52,6 @@ describe('radioOptionsGroup', function (): void {
|
||||
const label = radioOptionsGroup.items[0] as azdata.TextComponent;
|
||||
should(label.value).not.be.undefined();
|
||||
label.value!.should.deepEqual(loc.loadingClusterContextsError(loadingError));
|
||||
should(label.CSSStyles).not.be.undefined();
|
||||
should(label.CSSStyles!.color).not.be.undefined();
|
||||
label.CSSStyles!.color.should.equal('Red');
|
||||
});
|
||||
|
||||
describe('getters and setters', async () => {
|
||||
|
||||
@@ -67,7 +67,7 @@ export class RadioOptionsGroup {
|
||||
this.component().loadingCompletedText = this._loadingCompleteMessage;
|
||||
}
|
||||
catch (e) {
|
||||
const errorLabel = this._modelBuilder.text().withProps({ value: loc.loadingClusterContextsError(e), CSSStyles: { 'color': 'Red' } }).component();
|
||||
const errorLabel = this._modelBuilder.text().withProps({ value: loc.loadingClusterContextsError(e), textType: azdata.TextType.Error }).component();
|
||||
this._divContainer.addItem(errorLabel);
|
||||
this.component().loadingCompletedText = this._loadingCompleteErrorMessage(e);
|
||||
}
|
||||
|
||||
6
extensions/azurecore/src/azurecore.d.ts
vendored
@@ -263,6 +263,12 @@ declare module 'azurecore' {
|
||||
}
|
||||
|
||||
export interface IExtension {
|
||||
/**
|
||||
* Gets the list of subscriptions for the specified AzureAccount
|
||||
* @param account The account to get the subscriptions for
|
||||
* @param ignoreErrors If true any errors are not thrown and instead collected and returned as part of the result
|
||||
* @param selectedOnly Whether to only list subscriptions the user has selected to filter to for this account
|
||||
*/
|
||||
getSubscriptions(account?: AzureAccount, ignoreErrors?: boolean, selectedOnly?: boolean): Promise<GetSubscriptionsResult>;
|
||||
getResourceGroups(account?: AzureAccount, subscription?: azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<GetResourceGroupsResult>;
|
||||
getLocations(account?: AzureAccount, subscription?: azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<GetLocationsResult>;
|
||||
|
||||
@@ -655,9 +655,9 @@ events@^3.0.0:
|
||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
||||
|
||||
follow-redirects@^1.14.0:
|
||||
version "1.14.7"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
|
||||
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
|
||||
version "1.14.8"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
||||
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
|
||||
|
||||
form-data@^2.5.0:
|
||||
version "2.5.1"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "data-workspace",
|
||||
"displayName": "Data Workspace",
|
||||
"description": "Additional common functionality for database projects",
|
||||
"version": "0.1.1",
|
||||
"version": "0.2.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||
|
||||
@@ -49,6 +49,13 @@ export const SelectProjectType = localize('dataworkspace.selectProjectType', "Se
|
||||
export const SelectProjectLocation = localize('dataworkspace.selectProjectLocation', "Select Project Location");
|
||||
export const NameCannotBeEmpty = localize('dataworkspace.nameCannotBeEmpty', "Name cannot be empty");
|
||||
export const TargetPlatform = localize('dataworkspace.targetPlatform', "Target Platform");
|
||||
export const SdkStyleProject = localize('dataworkspace.sdkStyleProject', "SDK-style project (Preview)");
|
||||
export const LearnMore = localize('dataworkspace.learnMore', "Learn More");
|
||||
export const YesRecommended = localize('dataworkspace.yesRecommended', "Yes (Recommended)");
|
||||
export const No = localize('dataworkspace.no', "No");
|
||||
export const SdkLearnMorePlaceholder = localize('dataworkspace.sdkLearnMorePlaceholder', "Click \"Learn More\" button for more information about SDK-style projects");
|
||||
export const Default = localize('dataworkspace.default', "Default");
|
||||
export const SelectTargetPlatform = localize('dataworkspace.selectTargetPlatform', "Select Target Platform");
|
||||
|
||||
//Open Existing Dialog
|
||||
export const OpenExistingDialogTitle = localize('dataworkspace.openExistingDialogTitle', "Open Existing Project");
|
||||
|
||||
@@ -74,8 +74,9 @@ export interface IWorkspaceService {
|
||||
* @param location The location of the project
|
||||
* @param projectTypeId The project type id
|
||||
* @param projectTargetPlatform The target platform of the project
|
||||
* @param sdkStyleProject Whether or not the project is SDK-style
|
||||
*/
|
||||
createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetPlatform?: string): Promise<vscode.Uri>;
|
||||
createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetPlatform?: string, sdkStyleProject?: boolean): Promise<vscode.Uri>;
|
||||
|
||||
/**
|
||||
* Clones git repository and adds projects to workspace
|
||||
|
||||
11
extensions/data-workspace/src/dataworkspace.d.ts
vendored
@@ -67,8 +67,9 @@ declare module 'dataworkspace' {
|
||||
* @param location the parent directory of the project
|
||||
* @param projectTypeId the identifier of the selected project type
|
||||
* @param projectTargetPlatform the target platform of the project
|
||||
* @param sdkStyleProject whether or not a project is SDK-style
|
||||
*/
|
||||
createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetPlatform?: string): Promise<vscode.Uri>;
|
||||
createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetPlatform?: string, sdkStyleProject?: boolean): Promise<vscode.Uri>;
|
||||
|
||||
/**
|
||||
* Gets the project data corresponding to the project file, to be placed in the dashboard container
|
||||
@@ -131,14 +132,14 @@ declare module 'dataworkspace' {
|
||||
readonly defaultTargetPlatform?: string;
|
||||
|
||||
/**
|
||||
* Link display value for a link at the end of the project description. linkLocation also needs to be set to use this
|
||||
* Whether or not sdk style project is an option
|
||||
*/
|
||||
readonly linkDisplayValue?: string;
|
||||
readonly sdkStyleOption?: boolean;
|
||||
|
||||
/**
|
||||
* Location where clicking on the linkDisplayValue will go to
|
||||
* Location where clicking on the Learn More next to SDK style checkbox will go. sdkStyleOption needs to be set to true to use this
|
||||
*/
|
||||
readonly linkLocation?: string
|
||||
readonly sdkStyleLearnMoreUrl?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ class NewProjectDialogModel {
|
||||
name: string = '';
|
||||
location: string = '';
|
||||
targetPlatform?: string;
|
||||
sdkStyleProject?: boolean;
|
||||
}
|
||||
|
||||
export async function openSpecificProjectNewProjectDialog(projectType: IProjectType, workspaceService: WorkspaceService): Promise<vscode.Uri | undefined> {
|
||||
@@ -35,6 +36,7 @@ export class NewProjectDialog extends DialogBase {
|
||||
public model: NewProjectDialogModel = new NewProjectDialogModel();
|
||||
public formBuilder: azdataType.FormBuilder | undefined;
|
||||
public targetPlatformDropdownFormComponent: azdataType.FormComponent | undefined;
|
||||
public sdkProjectCheckboxFormComponent: azdataType.FormComponent | undefined;
|
||||
public newProjectDialogComplete: Deferred<void> | undefined;
|
||||
public newDialogPromise: Promise<void> = new Promise<void>((resolve, reject) => this.newProjectDialogComplete = { resolve, reject });
|
||||
public projectUri: vscode.Uri | undefined;
|
||||
@@ -87,7 +89,7 @@ export class NewProjectDialog extends DialogBase {
|
||||
.withAdditionalProperties({ projectFileExtension: this.model.projectFileExtension, projectTemplateId: this.model.projectTypeId })
|
||||
.send();
|
||||
|
||||
this.projectUri = await this.workspaceService.createProject(this.model.name, vscode.Uri.file(this.model.location), this.model.projectTypeId, this.model.targetPlatform);
|
||||
this.projectUri = await this.workspaceService.createProject(this.model.name, vscode.Uri.file(this.model.location), this.model.projectTypeId, this.model.targetPlatform, this.model.sdkStyleProject);
|
||||
this.newProjectDialogComplete?.resolve();
|
||||
}
|
||||
catch (err) {
|
||||
@@ -122,8 +124,7 @@ export class NewProjectDialog extends DialogBase {
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}, {
|
||||
textValue: projectType.description,
|
||||
linkDisplayValue: projectType.linkDisplayValue
|
||||
textValue: projectType.description
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -138,15 +139,7 @@ export class NewProjectDialog extends DialogBase {
|
||||
selectedCardId: allProjectTypes.length > 0 ? allProjectTypes[0].id : undefined
|
||||
}).component();
|
||||
|
||||
projectTypeRadioCardGroup.onLinkClick(async (value) => {
|
||||
for (let projectType of allProjectTypes) {
|
||||
if (value.cardId === projectType.id) {
|
||||
void vscode.env.openExternal(vscode.Uri.parse(projectType.linkLocation!));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.register(projectTypeRadioCardGroup.onSelectionChanged((e) => {
|
||||
this.register(projectTypeRadioCardGroup.onSelectionChanged(async (e) => {
|
||||
this.model.projectTypeId = e.cardId;
|
||||
const selectedProject = allProjectTypes.find(p => p.id === e.cardId);
|
||||
|
||||
@@ -155,12 +148,30 @@ export class NewProjectDialog extends DialogBase {
|
||||
targetPlatformDropdown.values = selectedProject?.targetPlatforms;
|
||||
targetPlatformDropdown.value = this.getDefaultTargetPlatform(selectedProject);
|
||||
|
||||
this.formBuilder?.addFormItem(this.targetPlatformDropdownFormComponent!);
|
||||
this.formBuilder?.insertFormItem(this.targetPlatformDropdownFormComponent!, 3);
|
||||
} else {
|
||||
// remove the target version dropdown if the selected project type didn't provide values for this
|
||||
this.formBuilder?.removeFormItem(this.targetPlatformDropdownFormComponent!);
|
||||
this.model.targetPlatform = undefined;
|
||||
}
|
||||
|
||||
if (selectedProject?.sdkStyleOption) {
|
||||
sdkProjectCheckbox.checked = true;
|
||||
this.model.sdkStyleProject = true;
|
||||
|
||||
if (selectedProject.sdkStyleLearnMoreUrl) {
|
||||
await sdkLearnMore.updateProperty('url', selectedProject.sdkStyleLearnMoreUrl);
|
||||
sdkFormComponentGroup.addItem(sdkLearnMore);
|
||||
} else {
|
||||
// remove learn more link if the project type didn't provide it
|
||||
sdkFormComponentGroup.removeItem(sdkLearnMore);
|
||||
}
|
||||
|
||||
this.formBuilder?.addFormItem(this.sdkProjectCheckboxFormComponent!);
|
||||
} else {
|
||||
this.model.sdkStyleProject = false;
|
||||
this.formBuilder?.removeFormItem(this.sdkProjectCheckboxFormComponent!);
|
||||
}
|
||||
}));
|
||||
|
||||
const projectNameTextBox = view.modelBuilder.inputBox().withProps({
|
||||
@@ -227,6 +238,34 @@ export class NewProjectDialog extends DialogBase {
|
||||
component: targetPlatformDropdown
|
||||
};
|
||||
|
||||
const sdkProjectCheckbox = view.modelBuilder.checkBox().withProps({
|
||||
checked: true,
|
||||
label: constants.SdkStyleProject
|
||||
}).component();
|
||||
|
||||
this.register(sdkProjectCheckbox.onChanged(() => {
|
||||
this.model.sdkStyleProject = sdkProjectCheckbox.checked;
|
||||
}));
|
||||
|
||||
const sdkLearnMore = view.modelBuilder.hyperlink().withProps({
|
||||
label: constants.LearnMore,
|
||||
url: ''
|
||||
}).component();
|
||||
|
||||
const sdkFormComponentGroup = view.modelBuilder.flexContainer()
|
||||
.withLayout({ flexFlow: 'row', alignItems: 'baseline' })
|
||||
.withItems([sdkProjectCheckbox], { CSSStyles: { flex: '0 0 auto', 'margin-right': '10px' } })
|
||||
.component();
|
||||
|
||||
if (allProjectTypes[0].sdkStyleLearnMoreUrl) {
|
||||
await sdkLearnMore.updateProperty('url', allProjectTypes[0].sdkStyleLearnMoreUrl);
|
||||
sdkFormComponentGroup.addItem(sdkLearnMore);
|
||||
}
|
||||
|
||||
this.sdkProjectCheckboxFormComponent = {
|
||||
component: sdkFormComponentGroup,
|
||||
};
|
||||
|
||||
this.formBuilder = view.modelBuilder.formContainer().withFormItems([
|
||||
{
|
||||
title: constants.TypeTitle,
|
||||
@@ -250,6 +289,11 @@ export class NewProjectDialog extends DialogBase {
|
||||
this.formBuilder.addFormItem(this.targetPlatformDropdownFormComponent);
|
||||
}
|
||||
|
||||
// add sdk style checkbox is the first project has the option
|
||||
if (allProjectTypes[0].sdkStyleOption) {
|
||||
this.formBuilder.addFormItem(this.sdkProjectCheckboxFormComponent);
|
||||
}
|
||||
|
||||
await view.initializeModel(this.formBuilder.component());
|
||||
this.initDialogComplete?.resolve();
|
||||
}
|
||||
|
||||
@@ -19,8 +19,12 @@ export async function createNewProjectWithQuickpick(workspaceService: WorkspaceS
|
||||
return {
|
||||
label: projType.displayName,
|
||||
description: projType.description,
|
||||
id: projType.id
|
||||
} as vscode.QuickPickItem & { id: string };
|
||||
id: projType.id,
|
||||
targetPlatforms: projType.targetPlatforms,
|
||||
defaultTargetPlatform: projType.defaultTargetPlatform,
|
||||
sdkOption: projType.sdkStyleOption,
|
||||
sdkLearnMoreUrl: projType.sdkStyleLearnMoreUrl
|
||||
} as vscode.QuickPickItem & { id: string, sdkOption?: boolean, targetPlatforms?: string[], defaultTargetPlatform?: string, sdkLearnMoreUrl?: string };
|
||||
});
|
||||
|
||||
// 1. Prompt for project type
|
||||
@@ -87,5 +91,79 @@ export async function createNewProjectWithQuickpick(workspaceService: WorkspaceS
|
||||
continue;
|
||||
}
|
||||
|
||||
await workspaceService.createProject(projectName, vscode.Uri.file(projectLocation), projectType.id, undefined);
|
||||
let targetPlatform;
|
||||
if (projectType.targetPlatforms) {
|
||||
// 4. Target platform of the project
|
||||
let targetPlatforms: vscode.QuickPickItem[] = projectType.targetPlatforms.map(targetPlatform => { return { label: targetPlatform }; });
|
||||
|
||||
if (projectType.defaultTargetPlatform) {
|
||||
// move the default target platform to be the first one in the list
|
||||
const defaultIndex = targetPlatforms.findIndex(i => i.label === projectType.defaultTargetPlatform);
|
||||
if (defaultIndex > -1) {
|
||||
targetPlatforms.splice(defaultIndex, 1);
|
||||
}
|
||||
|
||||
// add default next to the default target platform
|
||||
targetPlatforms.unshift({ label: projectType.defaultTargetPlatform, description: constants.Default });
|
||||
}
|
||||
|
||||
const selectedTargetPlatform = await vscode.window.showQuickPick(targetPlatforms, { title: constants.SelectTargetPlatform, ignoreFocusOut: true });
|
||||
if (!selectedTargetPlatform) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
targetPlatform = selectedTargetPlatform.label;
|
||||
}
|
||||
|
||||
let sdkStyle;
|
||||
if (projectType.sdkOption) {
|
||||
// 5. SDK-style project or not
|
||||
const sdkLearnMoreButton: vscode.QuickInputButton = {
|
||||
iconPath: new vscode.ThemeIcon('link-external'),
|
||||
tooltip: constants.LearnMore
|
||||
};
|
||||
const quickPick = vscode.window.createQuickPick();
|
||||
quickPick.items = [{ label: constants.YesRecommended }, { label: constants.No }];
|
||||
quickPick.title = constants.SdkStyleProject;
|
||||
quickPick.ignoreFocusOut = true;
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
|
||||
try {
|
||||
if (projectType.sdkLearnMoreUrl) {
|
||||
// add button to open sdkLearnMoreUrl if it was provided
|
||||
quickPick.buttons = [sdkLearnMoreButton];
|
||||
quickPick.placeholder = constants.SdkLearnMorePlaceholder;
|
||||
}
|
||||
|
||||
let sdkStylePromise = new Promise<boolean | undefined>((resolve) => {
|
||||
disposables.push(
|
||||
quickPick.onDidHide(() => {
|
||||
resolve(undefined);
|
||||
}),
|
||||
quickPick.onDidChangeSelection((item) => {
|
||||
resolve(item[0].label === constants.YesRecommended);
|
||||
}));
|
||||
|
||||
if (projectType.sdkLearnMoreUrl) {
|
||||
disposables.push(quickPick.onDidTriggerButton(async () => {
|
||||
await vscode.env.openExternal(vscode.Uri.parse(projectType.sdkLearnMoreUrl!));
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
quickPick.show();
|
||||
sdkStyle = await sdkStylePromise;
|
||||
quickPick.hide();
|
||||
} finally {
|
||||
disposables.forEach(d => d.dispose());
|
||||
}
|
||||
|
||||
if (sdkStyle === undefined) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await workspaceService.createProject(projectName, vscode.Uri.file(projectLocation), projectType.id, targetPlatform, sdkStyle);
|
||||
}
|
||||
|
||||
@@ -169,10 +169,12 @@ export class ProjectDashboard {
|
||||
.component();
|
||||
header.addItem(locationLabel, { CSSStyles: { 'padding-left': '34px', 'padding-top': '15px', 'padding-bottom': '50px', 'font-size': '16px' } });
|
||||
|
||||
const image = this.projectProvider!.image; // background image added at the bottom right of the header
|
||||
const backgroundImage = this.projectProvider!.image; // background image added at the bottom right of the header
|
||||
// Files need to have the vscode-file scheme to be loaded by ADS
|
||||
const backgroundUri = vscode.Uri.file(backgroundImage!.light.toString()).with({ scheme: 'vscode-file' });
|
||||
headerContainer.addItem(header, {
|
||||
CSSStyles: {
|
||||
'background-image': `url(${vscode.Uri.file(image!.light.toString())})`,
|
||||
'background-image': `url(${backgroundUri})`,
|
||||
'background-repeat': 'no-repeat',
|
||||
'background-position': '85% bottom',
|
||||
'background-size': '10%',
|
||||
|
||||
@@ -197,10 +197,10 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
return ProjectProviderRegistry.getProviderByProjectExtension(projectType);
|
||||
}
|
||||
|
||||
async createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetVersion?: string): Promise<vscode.Uri> {
|
||||
async createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetVersion?: string, sdkStyleProject?: boolean): Promise<vscode.Uri> {
|
||||
const provider = ProjectProviderRegistry.getProviderByProjectType(projectTypeId);
|
||||
if (provider) {
|
||||
const projectFile = await provider.createProject(name, location, projectTypeId, projectTargetVersion);
|
||||
const projectFile = await provider.createProject(name, location, projectTypeId, projectTargetVersion, sdkStyleProject);
|
||||
await this.addProjectsToWorkspace([projectFile]);
|
||||
this._onDidWorkspaceProjectsChange.fire();
|
||||
return projectFile;
|
||||
|
||||
@@ -50,9 +50,9 @@ delayed-stream@~1.0.0:
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
follow-redirects@^1.14.0:
|
||||
version "1.14.7"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
|
||||
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
|
||||
version "1.14.8"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
||||
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
|
||||
|
||||
form-data@^3.0.0:
|
||||
version "3.0.0"
|
||||
|
||||
@@ -38,10 +38,11 @@ export class DashboardWidget {
|
||||
}).component();
|
||||
const header = await this.createHeader(view);
|
||||
const footerContainer = this.createFooter(view);
|
||||
const backgroundImageUri = vscode.Uri.file(this.asAbsolutePath('images/background.svg')).with({ scheme: 'vscode-file' });
|
||||
container.addItem(header, {
|
||||
CSSStyles: {
|
||||
'background-image': `
|
||||
url(${vscode.Uri.file(this.asAbsolutePath('images/background.svg'))}),
|
||||
url(${backgroundImageUri}),
|
||||
linear-gradient(0deg, rgba(0,0,0,0.09) 0%, rgba(0,0,0,0) 100%)
|
||||
`,
|
||||
'background-repeat': 'no-repeat',
|
||||
@@ -300,9 +301,11 @@ export class DashboardWidget {
|
||||
await this._apiWrapper.openExternal(vscode.Uri.parse(linkMetaData.link));
|
||||
}
|
||||
});
|
||||
// Files need to have the vscode-file scheme to be loaded by ADS
|
||||
const imageUri = vscode.Uri.file(this.asAbsolutePath(linkMetaData.iconPath?.light as string || '')).with({ scheme: 'vscode-file' });
|
||||
videosContainer.addItem(video1Container, {
|
||||
CSSStyles: {
|
||||
'background-image': `url(${vscode.Uri.file(this.asAbsolutePath(linkMetaData.iconPath?.light as string || ''))})`,
|
||||
'background-image': `url(${imageUri})`,
|
||||
'background-repeat': 'no-repeat',
|
||||
'background-position': 'top',
|
||||
'width': `${maxWidth}px`,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||
"version": "3.0.0-release.207",
|
||||
"version": "3.0.0-release.214",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-net6.0.zip",
|
||||
"Windows_64": "win-x64-net6.0.zip",
|
||||
|
||||
@@ -514,14 +514,14 @@
|
||||
{
|
||||
"command": "mssql.designTable",
|
||||
"when": "connectionProvider == MSSQL && nodeType == Table && config.workbench.enablePreviewFeatures",
|
||||
"group":"connection@3"
|
||||
"group": "connection@3"
|
||||
},
|
||||
{
|
||||
"command": "mssql.newTable",
|
||||
"when": "connectionProvider == MSSQL && nodeType == Folder && nodeLabel == Tables && config.workbench.enablePreviewFeatures",
|
||||
"group":"connection@1"
|
||||
"group": "connection@1"
|
||||
}
|
||||
],
|
||||
],
|
||||
"notebook/toolbar": [
|
||||
{
|
||||
"command": "mssql.exportNotebookToSql",
|
||||
@@ -759,6 +759,9 @@
|
||||
"connectionProvider": {
|
||||
"providerId": "MSSQL",
|
||||
"displayName": "%mssql.provider.displayName%",
|
||||
"supportedExecutionPlanFileExtensions": [
|
||||
"sqlplan"
|
||||
],
|
||||
"iconPath": [
|
||||
{
|
||||
"id": "mssql:cloud",
|
||||
|
||||
@@ -1096,7 +1096,7 @@ export namespace InitializeTableDesignerRequest {
|
||||
}
|
||||
|
||||
export namespace ProcessTableDesignerEditRequest {
|
||||
export const type = new RequestType<TableDesignerEditRequestParams, azdata.designers.DesignerEditResult, void, void>('tabledesigner/processedit');
|
||||
export const type = new RequestType<TableDesignerEditRequestParams, azdata.designers.DesignerEditResult<azdata.designers.TableDesignerView>, void, void>('tabledesigner/processedit');
|
||||
}
|
||||
|
||||
export namespace PublishTableDesignerChangesRequest {
|
||||
@@ -1108,9 +1108,22 @@ export namespace TableDesignerGenerateScriptRequest {
|
||||
}
|
||||
|
||||
export namespace TableDesignerGenerateChangePreviewReportRequest {
|
||||
export const type = new RequestType<azdata.designers.TableInfo, string, void, void>('tabledesigner/generatepreviewreport');
|
||||
export const type = new RequestType<azdata.designers.TableInfo, azdata.designers.GeneratePreviewReportResult, void, void>('tabledesigner/generatepreviewreport');
|
||||
}
|
||||
export namespace DisposeTableDesignerRequest {
|
||||
export const type = new RequestType<azdata.designers.TableInfo, void, void, void>('tabledesigner/dispose');
|
||||
}
|
||||
// ------------------------------- < Table Designer > ------------------------------------
|
||||
|
||||
|
||||
// ------------------------------- < Execution Plan > ------------------------------------
|
||||
|
||||
export interface GetExecutionPlanParams {
|
||||
graphInfo: azdata.executionPlan.ExecutionPlanGraphInfo,
|
||||
}
|
||||
|
||||
export namespace GetExecutionPlanRequest {
|
||||
export const type = new RequestType<GetExecutionPlanParams, azdata.executionPlan.GetExecutionPlanResult, void, void>('queryexecutionplan/getexecutionplan');
|
||||
}
|
||||
|
||||
// ------------------------------- < Execution Plan > ------------------------------------
|
||||
|
||||
@@ -43,16 +43,20 @@ export class SqlCredentialService extends SqlOpsFeature<any> {
|
||||
|
||||
protected override registerProvider(options: any): Disposable {
|
||||
let readCredential = async (credentialId: string): Promise<azdata.Credential> => {
|
||||
if (Utils.isLinux) {
|
||||
const password = await this._secretStorage.get(credentialId);
|
||||
return {
|
||||
credentialId: credentialId,
|
||||
password: password
|
||||
};
|
||||
}
|
||||
return this._client.sendRequest(Contracts.ReadCredentialRequest.type, { credentialId, password: undefined });
|
||||
};
|
||||
|
||||
let saveCredential = async (credentialId: string, password: string): Promise<boolean> => {
|
||||
if (Utils.isLinux) {
|
||||
/**
|
||||
* This is only done for linux because this is going to be
|
||||
* the default credential system for linux in the next release
|
||||
*/
|
||||
await this._secretStorage.store(credentialId, password);
|
||||
return true;
|
||||
}
|
||||
return this._client.sendRequest(Contracts.SaveCredentialRequest.type, { credentialId, password });
|
||||
};
|
||||
@@ -61,6 +65,7 @@ export class SqlCredentialService extends SqlOpsFeature<any> {
|
||||
if (Utils.isLinux) {
|
||||
try {
|
||||
await this._secretStorage.delete(credentialId);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.log('credential does not exist in native secret store');
|
||||
}
|
||||
|
||||
@@ -1117,7 +1117,7 @@ export class TableDesignerFeature extends SqlOpsFeature<undefined> {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
const processTableEdit = (tableInfo: azdata.designers.TableInfo, tableChangeInfo: azdata.designers.DesignerEdit): Thenable<azdata.designers.DesignerEditResult> => {
|
||||
const processTableEdit = (tableInfo: azdata.designers.TableInfo, tableChangeInfo: azdata.designers.DesignerEdit): Thenable<azdata.designers.DesignerEditResult<azdata.designers.TableDesignerView>> => {
|
||||
let params: contracts.TableDesignerEditRequestParams = {
|
||||
tableInfo: tableInfo,
|
||||
tableChangeInfo: tableChangeInfo
|
||||
@@ -1151,7 +1151,7 @@ export class TableDesignerFeature extends SqlOpsFeature<undefined> {
|
||||
}
|
||||
};
|
||||
|
||||
const generatePreviewReport = (tableInfo: azdata.designers.TableInfo): Thenable<string> => {
|
||||
const generatePreviewReport = (tableInfo: azdata.designers.TableInfo): Thenable<azdata.designers.GeneratePreviewReportResult> => {
|
||||
try {
|
||||
return client.sendRequest(contracts.TableDesignerGenerateChangePreviewReportRequest.type, tableInfo);
|
||||
}
|
||||
@@ -1183,3 +1183,48 @@ export class TableDesignerFeature extends SqlOpsFeature<undefined> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execution Plan Service Feature
|
||||
* TODO: Move this feature to data protocol client repo once stablized
|
||||
*/
|
||||
export class ExecutionPlanServiceFeature extends SqlOpsFeature<undefined> {
|
||||
private static readonly messagesTypes: RPCMessageType[] = [
|
||||
contracts.GetExecutionPlanRequest.type,
|
||||
];
|
||||
|
||||
constructor(client: SqlOpsDataClient) {
|
||||
super(client, ExecutionPlanServiceFeature.messagesTypes);
|
||||
}
|
||||
|
||||
public fillClientCapabilities(capabilities: ClientCapabilities): void {
|
||||
}
|
||||
|
||||
public initialize(capabilities: ServerCapabilities): void {
|
||||
this.register(this.messages, {
|
||||
id: UUID.generateUuid(),
|
||||
registerOptions: undefined
|
||||
});
|
||||
}
|
||||
|
||||
protected registerProvider(options: undefined): Disposable {
|
||||
const client = this._client;
|
||||
|
||||
const getExecutionPlan = (planFile: azdata.executionPlan.ExecutionPlanGraphInfo): Thenable<azdata.executionPlan.GetExecutionPlanResult> => {
|
||||
const params: contracts.GetExecutionPlanParams = { graphInfo: planFile };
|
||||
return client.sendRequest(contracts.GetExecutionPlanRequest.type, params).then(
|
||||
r => r,
|
||||
e => {
|
||||
client.logFailedRequest(contracts.GetExecutionPlanRequest.type, e);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return azdata.dataprotocol.registerExecutionPlanProvider({
|
||||
providerId: client.providerId,
|
||||
getExecutionPlan
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as path from 'path';
|
||||
import { getCommonLaunchArgsAndCleanupOldLogFiles, getOrDownloadServer } from './utils';
|
||||
import { Telemetry, LanguageClientErrorHandler } from './telemetry';
|
||||
import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client';
|
||||
import { TelemetryFeature, AgentServicesFeature, SerializationFeature, AccountFeature, SqlAssessmentServicesFeature, ProfilerFeature, TableDesignerFeature } from './features';
|
||||
import { TelemetryFeature, AgentServicesFeature, SerializationFeature, AccountFeature, SqlAssessmentServicesFeature, ProfilerFeature, TableDesignerFeature, ExecutionPlanServiceFeature } from './features';
|
||||
import { CredentialStore } from './credentialstore/credentialstore';
|
||||
import { AzureResourceProvider } from './resourceProvider/resourceProvider';
|
||||
import { SchemaCompareService } from './schemaCompare/schemaCompareService';
|
||||
@@ -165,7 +165,8 @@ function getClientOptions(context: AppContext): ClientOptions {
|
||||
ProfilerFeature,
|
||||
SqlMigrationService.asFeature(context),
|
||||
SqlCredentialService.asFeature(context),
|
||||
TableDesignerFeature
|
||||
TableDesignerFeature,
|
||||
ExecutionPlanServiceFeature
|
||||
],
|
||||
outputChannel: new CustomOutputChannel()
|
||||
};
|
||||
|
||||
@@ -280,9 +280,9 @@ agent-base@4, agent-base@^4.3.0:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
ajv@^6.5.5:
|
||||
version "6.12.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
|
||||
integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
|
||||
version "6.12.6"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
@@ -694,9 +694,9 @@ extsprintf@^1.2.0:
|
||||
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
|
||||
|
||||
fast-deep-equal@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
|
||||
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
@@ -1899,9 +1899,9 @@ typemoq@^2.1.0:
|
||||
postinstall-build "^5.0.1"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
|
||||
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
|
||||
@@ -329,6 +329,14 @@
|
||||
"Notebook"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
}, {
|
||||
"id": "dib",
|
||||
"extensions": [
|
||||
".dib"
|
||||
],
|
||||
"aliases": [
|
||||
".NET Interactive Notebook"
|
||||
]
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
@@ -703,7 +711,8 @@
|
||||
"connectionProviderIds": [
|
||||
"MSSQL"
|
||||
],
|
||||
"blockedOnSAW": true
|
||||
"blockedOnSAW": true,
|
||||
"supportedLanguages": ["python"]
|
||||
},
|
||||
{
|
||||
"name": "sparkkernel",
|
||||
@@ -711,6 +720,7 @@
|
||||
"connectionProviderIds": [
|
||||
"MSSQL"
|
||||
],
|
||||
"supportedLanguages": ["scala"],
|
||||
"blockedOnSAW": true
|
||||
},
|
||||
{
|
||||
@@ -719,17 +729,20 @@
|
||||
"connectionProviderIds": [
|
||||
"MSSQL"
|
||||
],
|
||||
"supportedLanguages": ["r"],
|
||||
"blockedOnSAW": true
|
||||
},
|
||||
{
|
||||
"name": "python3",
|
||||
"displayName": "Python 3",
|
||||
"connectionProviderIds": []
|
||||
"connectionProviderIds": [],
|
||||
"supportedLanguages": ["python"]
|
||||
},
|
||||
{
|
||||
"name": "powershell",
|
||||
"displayName": "PowerShell",
|
||||
"connectionProviderIds": []
|
||||
"connectionProviderIds": [],
|
||||
"supportedLanguages": ["powershell"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { nb } from 'azdata';
|
||||
import { Kernel, KernelMessage } from '@jupyterlab/services';
|
||||
|
||||
@@ -90,7 +91,7 @@ export class JupyterKernel implements nb.IKernel {
|
||||
requestExecute(content: nb.IExecuteRequest, disposeOnDone?: boolean): nb.IFuture {
|
||||
content.code = Array.isArray(content.code) ? content.code.join('') : content.code;
|
||||
content.code = content.code.replace(/\r+\n/gm, '\n'); // Remove \r (if it exists) from newlines
|
||||
let futureImpl = this.kernelImpl.requestExecute(content as KernelMessage.IExecuteRequest & { cellIndex: number }, disposeOnDone);
|
||||
let futureImpl = this.kernelImpl.requestExecute(content as KernelMessage.IExecuteRequest & { notebookUri: vscode.Uri, cellUri: vscode.Uri, language: string, cellIndex: number }, disposeOnDone);
|
||||
return new JupyterFuture(futureImpl);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,10 @@ describe('Jupyter Session', function (): void {
|
||||
// When I request execute
|
||||
let future = kernel.requestExecute({
|
||||
code: code,
|
||||
cellIndex: 0
|
||||
cellIndex: 0,
|
||||
cellUri: undefined,
|
||||
notebookUri: undefined,
|
||||
language: ''
|
||||
}, true);
|
||||
|
||||
// Then expect wrapper to be returned
|
||||
|
||||
@@ -1798,10 +1798,10 @@ uri-js@^4.2.2:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
url-parse@1.5.8, url-parse@~1.4.3:
|
||||
version "1.5.8"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.8.tgz#3f8090e4d6f80053eb861ec496049849f700337e"
|
||||
integrity sha512-9JZ5zDrn9wJoOy/t+rH00HHejbU8dq9VsOYVu272TYDrCiyVAgHKUSpPh3ruZIpv8PMVR+NXLZvfRPJv8xAcQw==
|
||||
url-parse@^1.5.8, url-parse@~1.4.3:
|
||||
version "1.5.10"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
|
||||
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
|
||||
dependencies:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
@@ -531,9 +531,9 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
follow-redirects@^1.14.0:
|
||||
version "1.14.7"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
|
||||
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
|
||||
version "1.14.8"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
||||
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
||||
@@ -898,8 +898,7 @@ export class SchemaCompareMainWindow {
|
||||
throw new Error(`Unsupported SchemaCompareEndpointType: ${getSchemaCompareEndpointString(this.targetEndpointInfo.endpointType)}`);
|
||||
}
|
||||
|
||||
if (!result || !result.success) {
|
||||
|
||||
if (!result || !result.success || result.errorMessage !== '') {
|
||||
TelemetryReporter.createErrorEvent(TelemetryViews.SchemaCompareMainWindow, 'SchemaCompareApplyFailed', undefined, getTelemetryErrorType(result?.errorMessage))
|
||||
.withAdditionalProperties({
|
||||
'operationId': this.comparisonResult.operationId,
|
||||
@@ -912,6 +911,11 @@ export class SchemaCompareMainWindow {
|
||||
this.generateScriptButton.title = loc.generateScriptEnabledMessage;
|
||||
this.applyButton.enabled = true;
|
||||
this.applyButton.title = loc.applyEnabledMessage;
|
||||
} else if (this.targetEndpointInfo.endpointType === mssql.SchemaCompareEndpointType.Project) {
|
||||
const workspaceApi = getDataWorkspaceExtensionApi();
|
||||
workspaceApi.showProjectsView();
|
||||
|
||||
void vscode.window.showInformationMessage(loc.applySuccess);
|
||||
}
|
||||
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.SchemaCompareMainWindow, 'SchemaCompareApplyEnded')
|
||||
@@ -920,13 +924,6 @@ export class SchemaCompareMainWindow {
|
||||
'operationId': this.comparisonResult.operationId,
|
||||
'targetType': getSchemaCompareEndpointString(this.targetEndpointInfo.endpointType)
|
||||
}).send();
|
||||
|
||||
if (this.targetEndpointInfo.endpointType === mssql.SchemaCompareEndpointType.Project) {
|
||||
const workspaceApi = getDataWorkspaceExtensionApi();
|
||||
workspaceApi.showProjectsView();
|
||||
|
||||
void vscode.window.showInformationMessage(loc.applySuccess);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 847 B After Width: | Height: | Size: 847 B |
|
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 852 B |
|
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 339 B |
|
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 338 B |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 282 B |
|
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
|
Before Width: | Height: | Size: 247 B After Width: | Height: | Size: 247 B |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 969 B After Width: | Height: | Size: 969 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -32,32 +32,32 @@
|
||||
"command": "tempdb.startEvent",
|
||||
"title": "Start",
|
||||
"icon": {
|
||||
"light": "./dist/media/launch.svg",
|
||||
"dark": "./dist/media/launch_inverse.svg"
|
||||
"light": "./images/launch.svg",
|
||||
"dark": "./images/launch_inverse.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "tempdb.stopEvent",
|
||||
"title": "Stop",
|
||||
"icon": {
|
||||
"light": "./dist/media/blocker.svg",
|
||||
"dark": "./dist/media/blocker_inverse.svg"
|
||||
"light": "./images/blocker.svg",
|
||||
"dark": "./images/blocker_inverse.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "tempdb.contention",
|
||||
"title": "Contention Help for Tempdb",
|
||||
"icon": {
|
||||
"light": "./dist/media/documentation.svg",
|
||||
"dark": "./dist/media/documentation_inverse.svg"
|
||||
"light": "./images/documentation.svg",
|
||||
"dark": "./images/documentation_inverse.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "tempdb.pauseEvent",
|
||||
"title": "Toggle Auto Refresh",
|
||||
"icon": {
|
||||
"light": "./dist/media/insights.svg",
|
||||
"dark": "./dist/media/insights_inverse.svg"
|
||||
"light": "./images/insights.svg",
|
||||
"dark": "./images/insights_inverse.svg"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -77,8 +77,8 @@
|
||||
"id": "server-reports-monitoring",
|
||||
"title": "Monitor",
|
||||
"icon": {
|
||||
"light": "./dist/media/monitor.svg",
|
||||
"dark": "./dist/media/monitor_inverse.svg"
|
||||
"light": "./images/monitor.svg",
|
||||
"dark": "./images/monitor_inverse.svg"
|
||||
},
|
||||
"container": {
|
||||
"server-reports-monitoring-container": {}
|
||||
@@ -88,8 +88,8 @@
|
||||
"id": "server-reports-performance",
|
||||
"title": "Performance",
|
||||
"icon": {
|
||||
"light": "./dist/media/performance.svg",
|
||||
"dark": "./dist/media/performance_inverse.svg"
|
||||
"light": "./images/performance.svg",
|
||||
"dark": "./images/performance_inverse.svg"
|
||||
},
|
||||
"container": {
|
||||
"server-reports-performance-container": {}
|
||||
@@ -104,8 +104,8 @@
|
||||
"group": "monitoring",
|
||||
"description": "TempDB reports",
|
||||
"icon": {
|
||||
"light": "./dist/media/tempdb.svg",
|
||||
"dark": "./dist/media/tempdb_inverse.svg"
|
||||
"light": "./images/tempdb.svg",
|
||||
"dark": "./images/tempdb_inverse.svg"
|
||||
},
|
||||
"container": {
|
||||
"server-reports-tempdb-container": {}
|
||||
@@ -125,7 +125,7 @@
|
||||
"columnsAsLabels": true
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/all_db_space_used.sql"
|
||||
"queryFile": "./sql/all_db_space_used.sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -140,7 +140,7 @@
|
||||
"columnsAsLabels": false
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/cpumetric.sql"
|
||||
"queryFile": "./sql/cpumetric.sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -156,7 +156,7 @@
|
||||
"columnsAsLabels": false
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/backup_size_trend.sql"
|
||||
"queryFile": "./sql/backup_size_trend.sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -171,9 +171,9 @@
|
||||
"columnsAsLabels": true
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/waits_paul_randal.sql",
|
||||
"queryFile": "./sql/waits_paul_randal.sql",
|
||||
"details": {
|
||||
"queryFile": "./dist/sql/waits_detail_paul_randal.sql",
|
||||
"queryFile": "./sql/waits_detail_paul_randal.sql",
|
||||
"label": {
|
||||
"column": "WaitType",
|
||||
"state": []
|
||||
@@ -194,9 +194,9 @@
|
||||
"columnsAsLabels": true
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/wait_resources.sql",
|
||||
"queryFile": "./sql/wait_resources.sql",
|
||||
"details": {
|
||||
"queryFile": "./dist/sql/wait_resources.sql",
|
||||
"queryFile": "./sql/wait_resources.sql",
|
||||
"label": {
|
||||
"column": "WaitType",
|
||||
"state": []
|
||||
@@ -217,7 +217,7 @@
|
||||
"columnsAsLabels": true
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/memorybydb.sql"
|
||||
"queryFile": "./sql/memorybydb.sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -231,7 +231,7 @@
|
||||
"legendPosition": "none"
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/typeofContentions.sql",
|
||||
"queryFile": "./sql/typeofContentions.sql",
|
||||
"autoRefreshInterval": 0.05
|
||||
}
|
||||
},
|
||||
@@ -247,7 +247,7 @@
|
||||
"xAxisLabel": "Object Ids for System Tables"
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/metadataContention.sql",
|
||||
"queryFile": "./sql/metadataContention.sql",
|
||||
"autoRefreshInterval": 0.05
|
||||
}
|
||||
},
|
||||
@@ -263,7 +263,7 @@
|
||||
"xAxisLabel": "Page Types"
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/allocationContention.sql",
|
||||
"queryFile": "./sql/allocationContention.sql",
|
||||
"autoRefreshInterval": 0.05
|
||||
}
|
||||
}
|
||||
|
||||
13
extensions/sql-bindings/.eslintrc.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"project": "./extensions/sql-bindings/tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-floating-promises": [
|
||||
"error",
|
||||
{
|
||||
"ignoreVoid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
1
extensions/sql-bindings/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.vsix
|
||||
21
extensions/sql-bindings/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
// This task is meant for running the VS Code version of the extension. See VSCODE_DEVELOPMENT.md for more information
|
||||
{
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"name": "Launch Extension in VS Code",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
9
extensions/sql-bindings/.vscodeignore
Normal file
@@ -0,0 +1,9 @@
|
||||
src
|
||||
out
|
||||
tsconfig.json
|
||||
.gitignore
|
||||
coverage
|
||||
coverConfig.json
|
||||
extension.webpack.config.js
|
||||
*.vsix
|
||||
yarn.lock
|
||||
29
extensions/sql-bindings/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Microsoft SQL Bindings for Azure Data Studio and VS Code
|
||||
|
||||
## Overview
|
||||
|
||||
Microsoft SQL Bindings for Azure Data Studio and VS Code enables users to develop Azure Functions with Azure SQL bindings
|
||||
|
||||
### VS Code
|
||||
|
||||
This extension is bundled into the `SQL Server (MSSQL)` extension for VS Code and will be installed automatically when that extension is updated or installed.
|
||||
|
||||
### Azure Data Studio
|
||||
|
||||
This extension is provided as a separate extension in the marketplace.
|
||||
|
||||
Please report issues and feature requests [here.](https://github.com/microsoft/azuredatastudio/issues)
|
||||
|
||||
## 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.
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt).
|
||||
11
extensions/sql-bindings/VSCODE_DEVELOPMENT.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# VS Code Extension Development
|
||||
|
||||
For working on the VS Code version of the package follow these steps for local development/testing.
|
||||
|
||||
1. Copy the values from [package.vscode.json](./package.vscode.json) into [package.json](./package.json) (overwriting the properties with the same name there)
|
||||
2. Compile Azure Data Studio as normal and wait for it to finish
|
||||
3. Run `code <PathToAzureDataStudioSource>/extensions/sql-bindings` from the command line to open a new VS Code instance at the `sql-bindings` folder
|
||||
4. Run the `Launch Extension in VS Code` launch target from the `Run and Debug` view
|
||||
5. This should launch an `Extension Development Host` version of VS Code that is running the extension from sources.
|
||||
|
||||
If you have the compilation running as watch then once you make changes you can just reload the window to pick up the latest changes being made.
|
||||
20
extensions/sql-bindings/coverConfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"relativeSourcePath": "..",
|
||||
"relativeCoverageDir": "../../coverage",
|
||||
"ignorePatterns": [
|
||||
"**/node_modules/**",
|
||||
"**/test/**",
|
||||
"extension.js"
|
||||
],
|
||||
"reports": [
|
||||
"cobertura",
|
||||
"lcov",
|
||||
"json"
|
||||
],
|
||||
"verbose": false,
|
||||
"remapOptions": {
|
||||
"basePath": "..",
|
||||
"useAbsolutePaths": true
|
||||
}
|
||||
}
|
||||
17
extensions/sql-bindings/extension.webpack.config.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
//@ts-check
|
||||
|
||||
'use strict';
|
||||
|
||||
const withDefaults = require('../shared.webpack.config');
|
||||
|
||||
module.exports = withDefaults({
|
||||
context: __dirname,
|
||||
entry: {
|
||||
extension: './src/extension.ts'
|
||||
}
|
||||
});
|
||||
82
extensions/sql-bindings/package.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "sql-bindings",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"version": "0.0.1",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"engines": {
|
||||
"vscode": "^1.30.1",
|
||||
"azdata": ">=1.35.0"
|
||||
},
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||
"icon": "",
|
||||
"aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e",
|
||||
"activationEvents": [
|
||||
"onCommand:sqlBindings.addSqlBinding",
|
||||
"onCommand:sqlBindings.createAzureFunction"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/azuredatastudio.git"
|
||||
},
|
||||
"extensionDependencies": [
|
||||
"Microsoft.mssql"
|
||||
],
|
||||
"capabilities": {
|
||||
"virtualWorkspaces": false,
|
||||
"untrustedWorkspaces": {
|
||||
"supported": true
|
||||
}
|
||||
},
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "sqlBindings.addSqlBinding",
|
||||
"title": "%sqlBindings.addSqlBinding%",
|
||||
"category": "MS SQL"
|
||||
},
|
||||
{
|
||||
"command": "sqlBindings.createAzureFunction",
|
||||
"title": "%sqlBindings.createAzureFunction%",
|
||||
"category": "MS SQL",
|
||||
"when": "view == objectExplorer && viewItem == Table"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "sqlBindings.addSqlBinding",
|
||||
"when": "editorLangId == csharp && !azdataAvailable && resourceScheme != untitled"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "sqlBindings.createAzureFunction",
|
||||
"when": "view == objectExplorer && viewItem == Table",
|
||||
"group": "zAzure_Function@1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/ads-extension-telemetry": "^1.1.5",
|
||||
"fast-glob": "^3.2.7",
|
||||
"jsonc-parser": "^2.3.1",
|
||||
"promisify-child-process": "^3.1.1",
|
||||
"vscode-nls": "^4.1.2",
|
||||
"vscode-languageclient": "5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.14.16",
|
||||
"tslint": "^5.8.0",
|
||||
"should": "^13.2.1",
|
||||
"sinon": "^9.0.2",
|
||||
"typemoq": "^2.1.0",
|
||||
"@microsoft/vscodetestcover": "^1.2.0",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-junit-reporter": "^1.17.0",
|
||||
"mocha-multi-reporters": "^1.1.7"
|
||||
}
|
||||
}
|
||||
6
extensions/sql-bindings/package.nls.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"displayName": "SQL Bindings",
|
||||
"description": "Enables users to develop and publish Azure Functions with Azure SQL bindings",
|
||||
"sqlBindings.addSqlBinding": "Add SQL Binding (preview)",
|
||||
"sqlBindings.createAzureFunction": "Create Azure Function with SQL binding"
|
||||
}
|
||||
7
extensions/sql-bindings/package.vscode.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "sql-bindings-vscode",
|
||||
"publisher": "ms-mssql",
|
||||
"extensionDependencies": [
|
||||
"ms-mssql.mssql"
|
||||
]
|
||||
}
|
||||
526
extensions/sql-bindings/src/common/azureFunctionsUtils.ts
Normal file
@@ -0,0 +1,526 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as vscode from 'vscode';
|
||||
import * as utils from './utils';
|
||||
import * as constants from './constants';
|
||||
import { BindingType } from 'sql-bindings';
|
||||
import * as path from 'path';
|
||||
import { ConnectionDetails, IConnectionInfo } from 'vscode-mssql';
|
||||
// https://github.com/microsoft/vscode-azurefunctions/blob/main/src/vscode-azurefunctions.api.d.ts
|
||||
import { AzureFunctionsExtensionApi } from '../typings/vscode-azurefunctions.api';
|
||||
// https://github.com/microsoft/vscode-azuretools/blob/main/ui/api.d.ts
|
||||
import { AzureExtensionApiProvider } from '../typings/vscode-azuretools.api';
|
||||
/**
|
||||
* Represents the settings in an Azure function project's locawl.settings.json file
|
||||
*/
|
||||
export interface ILocalSettingsJson {
|
||||
IsEncrypted?: boolean;
|
||||
Values?: { [key: string]: string };
|
||||
Host?: { [key: string]: string };
|
||||
ConnectionStrings?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface IFileFunctionObject {
|
||||
filePromise: Promise<string>;
|
||||
watcherDisposable: vscode.Disposable;
|
||||
}
|
||||
|
||||
/**
|
||||
* copied and modified from vscode-azurefunctions extension
|
||||
* https://github.com/microsoft/vscode-azurefunctions/blob/main/src/funcConfig/local.settings.ts
|
||||
* @param localSettingsPath full path to local.settings.json
|
||||
* @returns settings in local.settings.json. If no settings are found, returns default "empty" settings
|
||||
*/
|
||||
export async function getLocalSettingsJson(localSettingsPath: string): Promise<ILocalSettingsJson> {
|
||||
if (fs.existsSync(localSettingsPath)) {
|
||||
const data: string = (fs.readFileSync(localSettingsPath)).toString();
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error(utils.formatString(constants.failedToParse(error.message), constants.azureFunctionLocalSettingsFileName, error.message));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
IsEncrypted: false // Include this by default otherwise the func cli assumes settings are encrypted and fails to run
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new setting to a project's local.settings.json file
|
||||
* modified from setLocalAppSetting code from vscode-azurefunctions extension
|
||||
* @param projectFolder full path to project folder
|
||||
* @param key Key of the new setting
|
||||
* @param value Value of the new setting
|
||||
* @returns true if successful adding the new setting, false if unsuccessful
|
||||
*/
|
||||
export async function setLocalAppSetting(projectFolder: string, key: string, value: string): Promise<boolean> {
|
||||
const localSettingsPath: string = path.join(projectFolder, constants.azureFunctionLocalSettingsFileName);
|
||||
const settings: ILocalSettingsJson = await getLocalSettingsJson(localSettingsPath);
|
||||
|
||||
settings.Values = settings.Values || {};
|
||||
if (settings.Values[key] === value) {
|
||||
// don't do anything if it's the same as the existing value
|
||||
return true;
|
||||
} else if (settings.Values[key]) {
|
||||
const result = await vscode.window.showWarningMessage(constants.settingAlreadyExists(key), { modal: true }, constants.yesString);
|
||||
if (result !== constants.yesString) {
|
||||
// key already exists and user doesn't want to overwrite it
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
settings.Values[key] = value;
|
||||
void fs.promises.writeFile(localSettingsPath, JSON.stringify(settings, undefined, 2));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Azure Functions extension API if it is installed
|
||||
* if it is not installed, prompt the user to install directly, learn more, or do not install
|
||||
* @returns the Azure Functions extension API if it is installed, prompt if it is not installed
|
||||
*/
|
||||
export async function getAzureFunctionsExtensionApi(): Promise<AzureFunctionsExtensionApi | undefined> {
|
||||
let apiProvider = await vscode.extensions.getExtension(constants.azureFunctionsExtensionName)?.activate() as AzureExtensionApiProvider;
|
||||
if (!apiProvider) {
|
||||
const response = await vscode.window.showInformationMessage(constants.azureFunctionsExtensionNotFound,
|
||||
constants.install, constants.learnMore, constants.doNotInstall);
|
||||
if (response === constants.install) {
|
||||
const extensionInstalled = new Promise<void>((resolve, reject) => {
|
||||
const timeout = setTimeout(async () => {
|
||||
reject(new Error(constants.timeoutExtensionError));
|
||||
extensionChange.dispose();
|
||||
}, 10000);
|
||||
let extensionChange = vscode.extensions.onDidChange(async () => {
|
||||
if (vscode.extensions.getExtension(constants.azureFunctionsExtensionName)) {
|
||||
resolve();
|
||||
extensionChange.dispose();
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
});
|
||||
});
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: constants.azureFunctionsExtensionName,
|
||||
cancellable: false
|
||||
}, async (_progress, _token) => {
|
||||
await vscode.commands.executeCommand('workbench.extensions.installExtension', constants.azureFunctionsExtensionName);
|
||||
}
|
||||
);
|
||||
// the extension has not been notified that the azure function extension is installed so wait till it is to then activate it
|
||||
await extensionInstalled;
|
||||
apiProvider = await vscode.extensions.getExtension(constants.azureFunctionsExtensionName)?.activate() as AzureExtensionApiProvider;
|
||||
} else if (response === constants.learnMore) {
|
||||
await vscode.env.openExternal(vscode.Uri.parse(constants.linkToAzureFunctionExtension));
|
||||
return undefined;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
const azureFunctionApi = apiProvider.getApi<AzureFunctionsExtensionApi>('*');
|
||||
if (azureFunctionApi) {
|
||||
return azureFunctionApi;
|
||||
} else {
|
||||
void vscode.window.showErrorMessage(constants.azureFunctionsExtensionNotInstalled);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO REMOVE defaultSqlBindingTextLines
|
||||
* Overwrites the Azure function methods body to work with the binding
|
||||
* @param filePath is the path for the function file (.cs for C# functions)
|
||||
*/
|
||||
export function overwriteAzureFunctionMethodBody(filePath: string): void {
|
||||
let defaultBindedFunctionText = fs.readFileSync(filePath, 'utf-8');
|
||||
// Replace default binding text
|
||||
let newValueLines = defaultBindedFunctionText.split(os.EOL);
|
||||
const defaultFunctionTextToSkip = new Set(constants.defaultSqlBindingTextLines);
|
||||
let replacedValueLines = [];
|
||||
for (let defaultLine of newValueLines) {
|
||||
// Skipped lines
|
||||
if (defaultFunctionTextToSkip.has(defaultLine.trimStart())) {
|
||||
continue;
|
||||
} else if (defaultLine.trimStart() === constants.defaultBindingResult) { // Result change
|
||||
replacedValueLines.push(defaultLine.replace(constants.defaultBindingResult, constants.sqlBindingResult));
|
||||
} else {
|
||||
// Normal lines to be included
|
||||
replacedValueLines.push(defaultLine);
|
||||
}
|
||||
}
|
||||
defaultBindedFunctionText = replacedValueLines.join(os.EOL);
|
||||
fs.writeFileSync(filePath, defaultBindedFunctionText, 'utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the azure function project for the user to choose from a list of projects files
|
||||
* If only one project is found that project is used to add the binding to
|
||||
* if no project is found, user is informed there needs to be a C# Azure Functions project
|
||||
* @returns the selected project file path
|
||||
*/
|
||||
export async function getAzureFunctionProject(): Promise<string | undefined> {
|
||||
let selectedProjectFile: string | undefined = '';
|
||||
if (vscode.workspace.workspaceFolders === undefined || vscode.workspace.workspaceFolders.length === 0) {
|
||||
return selectedProjectFile;
|
||||
} else {
|
||||
const projectFiles = await getAzureFunctionProjectFiles();
|
||||
if (projectFiles !== undefined) {
|
||||
if (projectFiles.length > 1) {
|
||||
// select project to add azure function to
|
||||
selectedProjectFile = (await vscode.window.showQuickPick(projectFiles, {
|
||||
canPickMany: false,
|
||||
title: constants.selectProject,
|
||||
ignoreFocusOut: true
|
||||
}));
|
||||
return selectedProjectFile;
|
||||
} else if (projectFiles.length === 1) {
|
||||
// only one azure function project found
|
||||
return projectFiles[0];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the azure function project files based on the host file found in the same folder
|
||||
* @returns the azure function project files paths
|
||||
*/
|
||||
export async function getAzureFunctionProjectFiles(): Promise<string[] | undefined> {
|
||||
let projFiles: string[] = [];
|
||||
const hostFiles = await getHostFiles();
|
||||
if (!hostFiles) {
|
||||
return undefined;
|
||||
}
|
||||
for (let host of hostFiles) {
|
||||
let projectFile = await vscode.workspace.findFiles(new vscode.RelativePattern(path.dirname(host), '*.csproj'));
|
||||
projectFile.filter(file => path.dirname(file.fsPath) === path.dirname(host) ? projFiles.push(file?.fsPath) : projFiles);
|
||||
}
|
||||
return projFiles.length > 0 ? projFiles : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the host files from the workspace
|
||||
* @returns the host file paths
|
||||
*/
|
||||
export async function getHostFiles(): Promise<string[] | undefined> {
|
||||
const hostUris = await vscode.workspace.findFiles('**/host.json');
|
||||
const hostFiles = hostUris.map(uri => uri.fsPath);
|
||||
return hostFiles.length > 0 ? hostFiles : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local.settings.json file path
|
||||
* @param projectFile path of the azure function project
|
||||
* @returns the local.settings.json file path
|
||||
*/
|
||||
export async function getSettingsFile(projectFile: string): Promise<string | undefined> {
|
||||
return path.join(path.dirname(projectFile), 'local.settings.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the new function file once the file is created and the watcher disposable
|
||||
* @param projectFile is the path to the project file
|
||||
* @returns the function file path once created and the watcher disposable
|
||||
*/
|
||||
export function waitForNewFunctionFile(projectFile: string): IFileFunctionObject {
|
||||
const watcher = vscode.workspace.createFileSystemWatcher((
|
||||
path.dirname(projectFile), '**/*.cs'), false, true, true);
|
||||
const filePromise = new Promise<string>((resolve, _) => {
|
||||
watcher.onDidCreate((e) => {
|
||||
resolve(e.fsPath);
|
||||
});
|
||||
});
|
||||
return {
|
||||
filePromise,
|
||||
watcherDisposable: watcher
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the new host project file once it has created and the watcher disposable
|
||||
* @returns the host file path once created and the watcher disposable
|
||||
*/
|
||||
export function waitForNewHostFile(): IFileFunctionObject {
|
||||
const watcher = vscode.workspace.createFileSystemWatcher('**/host.json', false, true, true);
|
||||
const filePromise = new Promise<string>((resolve, _) => {
|
||||
watcher.onDidCreate((e) => {
|
||||
resolve(e.fsPath);
|
||||
});
|
||||
});
|
||||
return {
|
||||
filePromise,
|
||||
watcherDisposable: watcher
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the required nuget package to the project
|
||||
* @param selectedProjectFile is the users selected project file path
|
||||
*/
|
||||
export async function addNugetReferenceToProjectFile(selectedProjectFile: string): Promise<void> {
|
||||
await utils.executeCommand(`dotnet add ${selectedProjectFile} package ${constants.sqlExtensionPackageName} --prerelease`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Sql Connection String to the local.settings.json
|
||||
* @param connectionString of the SQL Server connection that was chosen by the user
|
||||
*/
|
||||
export async function addConnectionStringToConfig(connectionString: string, projectFile: string): Promise<void> {
|
||||
const settingsFile = await getSettingsFile(projectFile);
|
||||
if (settingsFile) {
|
||||
await setLocalAppSetting(path.dirname(settingsFile), constants.sqlConnectionString, connectionString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Azure Functions project that contains the given file if the project is open in one of the workspace folders
|
||||
* @param fileUri file that the containing project needs to be found for
|
||||
* @returns uri of project or undefined if project couldn't be found
|
||||
*/
|
||||
export async function getAFProjectContainingFile(fileUri: vscode.Uri): Promise<vscode.Uri | undefined> {
|
||||
// get functions csprojs in the workspace
|
||||
const projectPromises = vscode.workspace.workspaceFolders?.map(f => utils.getAllProjectsInFolder(f.uri, '.csproj')) ?? [];
|
||||
const functionsProjects = (await Promise.all(projectPromises)).reduce((prev, curr) => prev.concat(curr), []).filter(p => isFunctionProject(path.dirname(p.fsPath)));
|
||||
|
||||
// look for project folder containing file if there's more than one
|
||||
if (functionsProjects.length > 1) {
|
||||
// TODO: figure out which project contains the file
|
||||
// the new style csproj doesn't list all the files in the project anymore, unless the file isn't in the same folder
|
||||
// so we can't rely on using that to check
|
||||
console.error('need to find which project contains the file ' + fileUri.fsPath);
|
||||
return undefined;
|
||||
} else if (functionsProjects.length === 0) {
|
||||
throw new Error(constants.noAzureFunctionsProjectsInWorkspace);
|
||||
} else {
|
||||
return functionsProjects[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Use 'host.json' as an indicator that this is a functions project
|
||||
// copied from verifyIsproject.ts in vscode-azurefunctions extension
|
||||
export async function isFunctionProject(folderPath: string): Promise<boolean> {
|
||||
return fs.existsSync(path.join(folderPath, constants.hostFileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to select type of binding and returns result
|
||||
*/
|
||||
export async function promptForBindingType(): Promise<(vscode.QuickPickItem & { type: BindingType }) | undefined> {
|
||||
const inputOutputItems: (vscode.QuickPickItem & { type: BindingType })[] = [
|
||||
{
|
||||
label: constants.input,
|
||||
type: BindingType.input
|
||||
},
|
||||
{
|
||||
label: constants.output,
|
||||
type: BindingType.output
|
||||
}
|
||||
];
|
||||
|
||||
const selectedBinding = (await vscode.window.showQuickPick(inputOutputItems, {
|
||||
canPickMany: false,
|
||||
title: constants.selectBindingType,
|
||||
ignoreFocusOut: true
|
||||
}));
|
||||
|
||||
return selectedBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to enter object name for the SQL query
|
||||
* @param bindingType Type of SQL Binding
|
||||
*/
|
||||
export async function promptForObjectName(bindingType: BindingType): Promise<string | undefined> {
|
||||
return vscode.window.showInputBox({
|
||||
prompt: bindingType === BindingType.input ? constants.sqlTableOrViewToQuery : constants.sqlTableToUpsert,
|
||||
placeHolder: constants.placeHolderObject,
|
||||
validateInput: input => input ? undefined : constants.nameMustNotBeEmpty,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to enter connection setting and updates it from AF project
|
||||
* @param projectUri Azure Function project uri
|
||||
*/
|
||||
export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.Uri | undefined): Promise<string | undefined> {
|
||||
let connectionStringSettingName: string | undefined;
|
||||
const vscodeMssqlApi = await utils.getVscodeMssqlApi();
|
||||
|
||||
// show the settings from project's local.settings.json if there's an AF functions project
|
||||
if (projectUri) {
|
||||
let settings;
|
||||
try {
|
||||
settings = await getLocalSettingsJson(path.join(path.dirname(projectUri.fsPath!), constants.azureFunctionLocalSettingsFileName));
|
||||
} catch (e) {
|
||||
void vscode.window.showErrorMessage(utils.getErrorMessage(e));
|
||||
return;
|
||||
}
|
||||
|
||||
let existingSettings: (vscode.QuickPickItem)[] = [];
|
||||
if (settings?.Values) {
|
||||
existingSettings = Object.keys(settings.Values).map(setting => {
|
||||
return {
|
||||
label: setting
|
||||
} as vscode.QuickPickItem;
|
||||
});
|
||||
}
|
||||
|
||||
existingSettings.unshift({ label: constants.createNewLocalAppSettingWithIcon });
|
||||
let sqlConnectionStringSettingExists = existingSettings.find(s => s.label === constants.sqlConnectionStringSetting);
|
||||
|
||||
while (!connectionStringSettingName) {
|
||||
const selectedSetting = await vscode.window.showQuickPick(existingSettings, {
|
||||
canPickMany: false,
|
||||
title: constants.selectSetting,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
if (!selectedSetting) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedSetting.label === constants.createNewLocalAppSettingWithIcon) {
|
||||
const newConnectionStringSettingName = await vscode.window.showInputBox(
|
||||
{
|
||||
title: constants.enterConnectionStringSettingName,
|
||||
ignoreFocusOut: true,
|
||||
value: sqlConnectionStringSettingExists ? '' : constants.sqlConnectionStringSetting,
|
||||
validateInput: input => input ? undefined : constants.nameMustNotBeEmpty
|
||||
}
|
||||
) ?? '';
|
||||
|
||||
if (!newConnectionStringSettingName) {
|
||||
// go back to select setting quickpick if user escapes from inputting the setting name in case they changed their mind
|
||||
continue;
|
||||
}
|
||||
|
||||
// show the connection string methods (user input and connection profile options)
|
||||
const listOfConnectionStringMethods = [constants.connectionProfile, constants.userConnectionString];
|
||||
while (true) {
|
||||
const selectedConnectionStringMethod = await vscode.window.showQuickPick(listOfConnectionStringMethods, {
|
||||
canPickMany: false,
|
||||
title: constants.selectConnectionString,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
if (!selectedConnectionStringMethod) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
let connectionString: string = '';
|
||||
let includePassword: string | undefined;
|
||||
let connectionInfo: IConnectionInfo | undefined;
|
||||
let connectionDetails: ConnectionDetails;
|
||||
if (selectedConnectionStringMethod === constants.userConnectionString) {
|
||||
// User chooses to enter connection string manually
|
||||
connectionString = await vscode.window.showInputBox(
|
||||
{
|
||||
title: constants.enterConnectionString,
|
||||
ignoreFocusOut: true,
|
||||
value: 'Server=localhost;Initial Catalog={db_name};User ID=sa;Password={your_password};Persist Security Info=False',
|
||||
validateInput: input => input ? undefined : constants.valueMustNotBeEmpty
|
||||
}
|
||||
) ?? '';
|
||||
} else {
|
||||
// Let user choose from existing connections to create connection string from
|
||||
connectionInfo = await vscodeMssqlApi.promptForConnection(true);
|
||||
if (!connectionInfo) {
|
||||
// User cancelled return to selectedConnectionStringMethod prompt
|
||||
continue;
|
||||
}
|
||||
connectionDetails = { options: connectionInfo };
|
||||
try {
|
||||
// Prompt to include password in connection string if authentication type is SqlLogin and connection has password saved
|
||||
if (connectionInfo.authenticationType === 'SqlLogin' && connectionInfo.password) {
|
||||
includePassword = await vscode.window.showQuickPick([constants.yesString, constants.noString], {
|
||||
title: constants.includePassword,
|
||||
canPickMany: false,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
if (includePassword === constants.yesString) {
|
||||
// set connection string to include password
|
||||
connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, true, false);
|
||||
}
|
||||
}
|
||||
// set connection string to not include the password if connection info does not include password, or user chooses to not include password, or authentication type is not sql login
|
||||
if (includePassword !== constants.yesString) {
|
||||
connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, false, false);
|
||||
}
|
||||
} catch (e) {
|
||||
// failed to get connection string for selected connection and will go back to prompt for connection string methods
|
||||
console.warn(e);
|
||||
void vscode.window.showErrorMessage(constants.failedToGetConnectionString);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (connectionString) {
|
||||
try {
|
||||
const projectFolder: string = path.dirname(projectUri.fsPath);
|
||||
const localSettingsPath: string = path.join(projectFolder, constants.azureFunctionLocalSettingsFileName);
|
||||
let userPassword: string | undefined;
|
||||
// Ask user to enter password if auth type is sql login and password is not saved
|
||||
if (connectionInfo?.authenticationType === 'SqlLogin' && !connectionInfo?.password) {
|
||||
userPassword = await vscode.window.showInputBox({
|
||||
prompt: constants.enterPasswordPrompt,
|
||||
placeHolder: constants.enterPasswordManually,
|
||||
ignoreFocusOut: true,
|
||||
password: true,
|
||||
validateInput: input => input ? undefined : constants.valueMustNotBeEmpty
|
||||
});
|
||||
if (userPassword) {
|
||||
// if user enters password replace password placeholder with user entered password
|
||||
connectionString = connectionString.replace(constants.passwordPlaceholder, userPassword);
|
||||
}
|
||||
}
|
||||
if (includePassword !== constants.yesString && !userPassword && connectionInfo?.authenticationType === 'SqlLogin') {
|
||||
// if user does not want to include password or user does not enter password, show warning message that they will have to enter it manually later in local.settings.json
|
||||
void vscode.window.showWarningMessage(constants.userPasswordLater, constants.openFile, constants.closeButton).then(async (result) => {
|
||||
if (result === constants.openFile) {
|
||||
// open local.settings.json file
|
||||
void vscode.commands.executeCommand(constants.vscodeOpenCommand, vscode.Uri.file(localSettingsPath));
|
||||
}
|
||||
});
|
||||
}
|
||||
const success = await setLocalAppSetting(projectFolder, newConnectionStringSettingName, connectionString);
|
||||
if (success) {
|
||||
// exit both loops and insert binding
|
||||
connectionStringSettingName = newConnectionStringSettingName;
|
||||
break;
|
||||
} else {
|
||||
void vscode.window.showErrorMessage(constants.selectConnectionError());
|
||||
}
|
||||
} catch (e) {
|
||||
// display error message and show select setting quickpick again
|
||||
void vscode.window.showErrorMessage(constants.selectConnectionError(e));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If user cancels out of this or doesn't want to overwrite an existing setting
|
||||
// just return them to the select setting quickpick in case they changed their mind
|
||||
connectionStringSettingName = selectedSetting.label;
|
||||
}
|
||||
}
|
||||
// Add sql extension package reference to project. If the reference is already there, it doesn't get added again
|
||||
await addNugetReferenceToProjectFile(projectUri.fsPath);
|
||||
} else {
|
||||
// if no AF project was found or there's more than one AF functions project in the workspace,
|
||||
// ask for the user to input the setting name
|
||||
connectionStringSettingName = await vscode.window.showInputBox({
|
||||
prompt: constants.connectionStringSetting,
|
||||
placeHolder: constants.connectionStringSettingPlaceholder,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
}
|
||||
return connectionStringSettingName;
|
||||
}
|
||||
90
extensions/sql-bindings/src/common/constants.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as utils from '../common/utils';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
// Azure Functions
|
||||
export const azureFunctionsExtensionName = 'ms-azuretools.vscode-azurefunctions';
|
||||
export const sqlConnectionString = 'SqlConnectionString';
|
||||
export const linkToAzureFunctionExtension = 'https://docs.microsoft.com/azure/azure-functions/functions-develop-vs-code';
|
||||
export const sqlBindingsDoc = 'https://aka.ms/sqlbindings';
|
||||
export const defaultSqlBindingTextLines =
|
||||
[
|
||||
'log.LogInformation(\"C# HTTP trigger function processed a request.\");',
|
||||
'string name = req.Query[\"name\"];',
|
||||
'string requestBody = await new StreamReader(req.Body).ReadToEndAsync();',
|
||||
'dynamic data = JsonConvert.DeserializeObject(requestBody);',
|
||||
'name = name ?? data?.name;',
|
||||
'string responseMessage = string.IsNullOrEmpty(name) ? \"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.\" : $\"Hello, {name}. This HTTP triggered function executed successfully.\";'
|
||||
];
|
||||
export const defaultBindingResult = 'return new OkObjectResult(responseMessage);';
|
||||
export const sqlBindingResult = `return new OkObjectResult(result);`;
|
||||
export const sqlExtensionPackageName = 'Microsoft.Azure.WebJobs.Extensions.Sql';
|
||||
export const functionNameTitle = localize('functionNameTitle', 'Function Name');
|
||||
export const selectProject = localize('selectProject', 'Select the Azure Function project for the SQL Binding');
|
||||
export const azureFunctionsExtensionNotFound = localize('azureFunctionsExtensionNotFound', 'The Azure Functions extension is required to create a new Azure Function with SQL binding but is not installed, install it now?');
|
||||
export const install = localize('install', 'Install');
|
||||
export const learnMore = localize('learnMore', 'Learn more');
|
||||
export const doNotInstall = localize('doNotInstall', 'Do not install');
|
||||
export const createProject = localize('createProject', 'Create Azure Function Project');
|
||||
export const selectAzureFunctionProjFolder = localize('selectAzureFunctionProjFolder', 'Select folder for the Azure Function project');
|
||||
export const timeoutExtensionError = localize('timeoutExtensionError', 'Timed out waiting for extension to install');
|
||||
export const timeoutAzureFunctionFileError = localize('timeoutAzureFunctionFileError', 'Timed out waiting for Azure Function file to be created');
|
||||
export const timeoutProjectError = localize('timeoutProjectError', 'Timed out waiting for project to be created');
|
||||
export const errorNewAzureFunction = localize('errorNewAzureFunction', 'Error creating new Azure Function: {0}');
|
||||
export const azureFunctionsExtensionNotInstalled = localize('azureFunctionsExtensionNotInstalled', 'Azure Functions extension must be installed in order to use this feature.');
|
||||
export const azureFunctionsProjectMustBeOpened = localize('azureFunctionsProjectMustBeOpened', 'A C# Azure Functions project must be present in order to create a new Azure Function for this table.');
|
||||
|
||||
// Insert SQL binding
|
||||
export const hostFileName = 'host.json';
|
||||
export const placeHolderObject = '[dbo].[table1]';
|
||||
export const sqlBindingsHelpLink = 'https://github.com/Azure/azure-functions-sql-extension/blob/main/README.md';
|
||||
export const passwordPlaceholder = '******';
|
||||
export const azureFunctionLocalSettingsFileName = 'local.settings.json';
|
||||
export const vscodeOpenCommand = 'vscode.open';
|
||||
|
||||
export const nameMustNotBeEmpty = localize('nameMustNotBeEmpty', "Name must not be empty");
|
||||
export const yesString = localize('yesString', "Yes");
|
||||
export const noString = localize('noString', "No");
|
||||
export const input = localize('input', "Input");
|
||||
export const output = localize('output', "Output");
|
||||
export const selectBindingType = localize('selectBindingType', "Select type of binding");
|
||||
export const selectAzureFunction = localize('selectAzureFunction', "Select an Azure function in the current file to add SQL binding to");
|
||||
export const sqlTableOrViewToQuery = localize('sqlTableOrViewToQuery', "SQL table or view to query");
|
||||
export const sqlTableToUpsert = localize('sqlTableToUpsert', "SQL table to upsert into");
|
||||
export const connectionStringSetting = localize('connectionStringSetting', "Connection string setting name");
|
||||
export const selectSetting = localize('selectSetting', "Select SQL connection string setting from local.settings.json");
|
||||
export const connectionStringSettingPlaceholder = localize('connectionStringSettingPlaceholder', "Connection string setting specified in \"local.settings.json\"");
|
||||
export const noAzureFunctionsInFile = localize('noAzureFunctionsInFile', "No Azure functions in the current active file");
|
||||
export const noAzureFunctionsProjectsInWorkspace = localize('noAzureFunctionsProjectsInWorkspace', "No Azure functions projects found in the workspace");
|
||||
export const addPackage = localize('addPackage', "Add Package");
|
||||
export const createNewLocalAppSetting = localize('createNewLocalAppSetting', 'Create new local app setting');
|
||||
export const createNewLocalAppSettingWithIcon = `$(add) ${createNewLocalAppSetting}`;
|
||||
export const sqlConnectionStringSetting = 'SqlConnectionString';
|
||||
export const valueMustNotBeEmpty = localize('valueMustNotBeEmpty', "Value must not be empty");
|
||||
export const enterConnectionStringSettingName = localize('enterConnectionStringSettingName', "Enter connection string setting name");
|
||||
export const enterConnectionString = localize('enterConnectionString', "Enter connection string");
|
||||
export const saveChangesInFile = localize('saveChangesInFile', "There are unsaved changes in the current file. Save now?");
|
||||
export const save = localize('save', "Save");
|
||||
export function settingAlreadyExists(settingName: string) { return localize('SettingAlreadyExists', 'Local app setting \'{0}\' already exists. Overwrite?', settingName); }
|
||||
export function failedToParse(errorMessage: string) { return localize('failedToParse', 'Failed to parse "{0}": {1}.', azureFunctionLocalSettingsFileName, errorMessage); }
|
||||
export function jsonParseError(error: string, line: number, column: number) { return localize('jsonParseError', '{0} near line "{1}", column "{2}"', error, line, column); }
|
||||
export const moreInformation = localize('moreInformation', "More Information");
|
||||
export const addPackageReferenceMessage = localize('addPackageReferenceMessage', 'To use SQL bindings, ensure your Azure Functions project has a reference to {0}', sqlExtensionPackageName);
|
||||
export const addSqlBindingPackageError = localize('addSqlBindingPackageError', 'Error adding Sql Binding extension package to project');
|
||||
export const failedToGetConnectionString = localize('failedToGetConnectionString', 'An error occurred generating the connection string for the selected connection');
|
||||
export const connectionProfile = localize('connectionProfile', 'Select a connection profile');
|
||||
export const userConnectionString = localize('userConnectionString', 'Enter connection string');
|
||||
export const selectConnectionString = localize('selectConnectionString', 'Select SQL connection string method');
|
||||
export const selectConnectionError = (err?: any) => err ? localize('selectConnectionError', "Failed to set connection string app setting: {0}", utils.getErrorMessage(err)) : localize('unableToSetConnectionString', "Failed to set connection string app setting");
|
||||
export const includePassword = localize('includePassword', 'Do you want to include the password from this connection in your local.settings.json file?');
|
||||
export const enterPasswordPrompt = localize('enterPasswordPrompt', 'Enter the password to be used for the connection string');
|
||||
export const enterPasswordManually = localize('enterPasswordManually', 'Enter password or press escape to cancel');
|
||||
export const userPasswordLater = localize('userPasswordLater', 'In order to user the SQL connection string later you will need to manually enter the password in your local.settings.json file.');
|
||||
export const openFile = localize('openFile', "Open File");
|
||||
export const closeButton = localize('closeButton', "Close");
|
||||
45
extensions/sql-bindings/src/common/parseJson.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// copied from vscode-azurefunctions extension
|
||||
|
||||
import * as jsonc from 'jsonc-parser';
|
||||
import * as constants from './constants';
|
||||
|
||||
/**
|
||||
* Parses and returns JSON
|
||||
* Has extra logic to remove a BOM character if it exists and handle comments
|
||||
*/
|
||||
export function parseJson<T extends object>(data: string): T {
|
||||
if (data.charCodeAt(0) === 0xFEFF) {
|
||||
data = data.slice(1);
|
||||
}
|
||||
|
||||
const errors: jsonc.ParseError[] = [];
|
||||
const result: T = <T>jsonc.parse(data, errors, { allowTrailingComma: true });
|
||||
if (errors.length > 0) {
|
||||
const [line, column]: [number, number] = getLineAndColumnFromOffset(data, errors[0].offset);
|
||||
throw new Error(constants.jsonParseError(jsonc.printParseErrorCode(errors[0].error), line, column));
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export function getLineAndColumnFromOffset(data: string, offset: number): [number, number] {
|
||||
const lines: string[] = data.split('\n');
|
||||
let charCount: number = 0;
|
||||
let lineCount: number = 0;
|
||||
let column: number = 0;
|
||||
for (const line of lines) {
|
||||
lineCount += 1;
|
||||
const lineLength: number = line.length + 1;
|
||||
charCount += lineLength;
|
||||
if (charCount >= offset) {
|
||||
column = offset - (charCount - lineLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [lineCount, column];
|
||||
}
|
||||
19
extensions/sql-bindings/src/common/telemetry.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import AdsTelemetryReporter from '@microsoft/ads-extension-telemetry';
|
||||
import { getPackageInfo } from './utils';
|
||||
|
||||
const packageInfo = getPackageInfo()!;
|
||||
|
||||
export const TelemetryReporter = new AdsTelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||
|
||||
export enum TelemetryViews {
|
||||
SqlBindingsQuickPick = 'SqlBindingsQuickPick'
|
||||
}
|
||||
|
||||
export enum TelemetryActions {
|
||||
startAddSqlBinding = 'startAddSqlBinding',
|
||||
finishAddSqlBinding = 'finishAddSqlBinding'
|
||||
}
|
||||
185
extensions/sql-bindings/src/common/utils.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as azdataType from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as vscodeMssql from 'vscode-mssql';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as glob from 'fast-glob';
|
||||
import * as cp from 'child_process';
|
||||
|
||||
export interface ValidationResult {
|
||||
errorMessage: string;
|
||||
validated: boolean
|
||||
}
|
||||
|
||||
export interface IPackageInfo {
|
||||
name: string;
|
||||
fullName: string;
|
||||
version: string;
|
||||
aiKey: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consolidates on the error message string
|
||||
*/
|
||||
export function getErrorMessage(error: any): string {
|
||||
return (error instanceof Error)
|
||||
? (typeof error.message === 'string' ? error.message : '')
|
||||
: typeof error === 'string' ? error : `${JSON.stringify(error, undefined, '\t')}`;
|
||||
}
|
||||
|
||||
export async function getAzureFunctionService(): Promise<vscodeMssql.IAzureFunctionsService> {
|
||||
if (getAzdataApi()) {
|
||||
// this isn't supported in ADS
|
||||
throw new Error('Azure Functions service is not supported in Azure Data Studio');
|
||||
} else {
|
||||
const api = await getVscodeMssqlApi();
|
||||
return api.azureFunctions;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getVscodeMssqlApi(): Promise<vscodeMssql.IExtension> {
|
||||
const ext = vscode.extensions.getExtension(vscodeMssql.extension.name) as vscode.Extension<vscodeMssql.IExtension>;
|
||||
return ext.activate();
|
||||
}
|
||||
|
||||
// Try to load the azdata API - but gracefully handle the failure in case we're running
|
||||
// in a context where the API doesn't exist (such as VS Code)
|
||||
let azdataApi: typeof azdataType | undefined = undefined;
|
||||
try {
|
||||
azdataApi = require('azdata');
|
||||
if (!azdataApi?.version) {
|
||||
// webpacking makes the require return an empty object instead of throwing an error so make sure we clear the var
|
||||
azdataApi = undefined;
|
||||
}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the azdata API if it's available in the context this extension is running in.
|
||||
* @returns The azdata API if it's available
|
||||
*/
|
||||
export function getAzdataApi(): typeof azdataType | undefined {
|
||||
return azdataApi;
|
||||
}
|
||||
|
||||
export async function executeCommand(command: string, cwd?: string): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
cp.exec(command, { maxBuffer: 500 * 1024, cwd: cwd }, (error: Error | null, stdout: string, stderr: string) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
if (stderr && stderr.length > 0) {
|
||||
reject(new Error(stderr));
|
||||
return;
|
||||
}
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the projects of the specified extension in the folder
|
||||
* @param folder
|
||||
* @param projectExtension project extension to filter on
|
||||
* @returns array of project uris
|
||||
*/
|
||||
export async function getAllProjectsInFolder(folder: vscode.Uri, projectExtension: string): Promise<vscode.Uri[]> {
|
||||
// path needs to use forward slashes for glob to work
|
||||
const escapedPath = glob.escapePath(folder.fsPath.replace(/\\/g, '/'));
|
||||
|
||||
// filter for projects with the specified project extension
|
||||
const projFilter = path.posix.join(escapedPath, '**', `*${projectExtension}`);
|
||||
|
||||
// glob will return an array of file paths with forward slashes, so they need to be converted back if on windows
|
||||
return (await glob(projFilter)).map(p => vscode.Uri.file(path.resolve(p)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a string. Behaves like C#'s string.Format() function.
|
||||
*/
|
||||
export function formatString(str: string, ...args: any[]): string {
|
||||
// This is based on code originally from https://github.com/Microsoft/vscode/blob/master/src/vs/nls.js
|
||||
// License: https://github.com/Microsoft/vscode/blob/master/LICENSE.txt
|
||||
let result: string;
|
||||
if (args.length === 0) {
|
||||
result = str;
|
||||
} else {
|
||||
result = str.replace(/\{(\d+)\}/g, (match, rest) => {
|
||||
let index = rest[0];
|
||||
return typeof args[index] !== 'undefined' ? args[index] : match;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a quoted full name for the object
|
||||
* @param schema of the object
|
||||
* @param objectName object chosen by the user
|
||||
* @returns the quoted and escaped full name of the specified schema and object
|
||||
*/
|
||||
export function generateQuotedFullName(schema: string, objectName: string): string {
|
||||
return `[${escapeClosingBrackets(schema)}].[${escapeClosingBrackets(objectName)}]`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will reject after the specified timeout
|
||||
* @param errorMessage error message to be returned in the rejection
|
||||
* @param ms timeout in milliseconds. Default is 10 seconds
|
||||
* @returns a promise that rejects after the specified timeout
|
||||
*/
|
||||
export function timeoutPromise(errorMessage: string, ms: number = 10000): Promise<string> {
|
||||
return new Promise((_, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(new Error(errorMessage));
|
||||
}, ms);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a unique file name
|
||||
* Increment the file name by adding 1 to function name if the file already exists
|
||||
* Undefined if the filename suffix count becomes greater than 1024
|
||||
* @param folderPath selected project folder path
|
||||
* @param fileName base filename to use
|
||||
* @returns a promise with the unique file name, or undefined
|
||||
*/
|
||||
export async function getUniqueFileName(folderPath: string, fileName: string): Promise<string | undefined> {
|
||||
let count: number = 0;
|
||||
const maxCount: number = 1024;
|
||||
let uniqueFileName = fileName;
|
||||
|
||||
while (count < maxCount) {
|
||||
if (!fs.existsSync(path.join(folderPath, uniqueFileName + '.cs'))) {
|
||||
return uniqueFileName;
|
||||
}
|
||||
count += 1;
|
||||
uniqueFileName = fileName + count.toString();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function escapeClosingBrackets(str: string): string {
|
||||
return str.replace(']', ']]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the package info for the extension based on where the extension is installed
|
||||
* @returns the package info object
|
||||
*/
|
||||
export function getPackageInfo(): IPackageInfo {
|
||||
const packageJson = require('../../package.json');
|
||||
return {
|
||||
name: packageJson.name,
|
||||
fullName: `${packageJson.publisher}.${packageJson.name}`,
|
||||
version: packageJson.version,
|
||||
aiKey: packageJson.aiKey
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AddSqlBindingParams, GetAzureFunctionsParams, GetAzureFunctionsResult, ResultStatus } from 'sql-bindings';
|
||||
import { RequestType } from 'vscode-languageclient';
|
||||
|
||||
/**
|
||||
* Adds a SQL Binding to a specified Azure function in a file
|
||||
*/
|
||||
export namespace AddSqlBindingRequest {
|
||||
export const type = new RequestType<AddSqlBindingParams, ResultStatus, void, void>('azureFunctions/sqlBinding');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of the Azure functions in a file
|
||||
*/
|
||||
export namespace GetAzureFunctionsRequest {
|
||||
export const type = new RequestType<GetAzureFunctionsParams, GetAzureFunctionsResult, void, void>('azureFunctions/getAzureFunctions');
|
||||
}
|
||||
103
extensions/sql-bindings/src/dialogs/addSqlBindingQuickpick.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 constants from '../common/constants';
|
||||
import * as utils from '../common/utils';
|
||||
import * as azureFunctionsUtils from '../common/azureFunctionsUtils';
|
||||
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
||||
|
||||
export async function launchAddSqlBindingQuickpick(uri: vscode.Uri | undefined): Promise<void> {
|
||||
TelemetryReporter.sendActionEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.startAddSqlBinding);
|
||||
if (!uri) {
|
||||
// this command only shows in the command palette when the active editor is a .cs file, so we can safely assume that's the scenario
|
||||
// when this is called without a uri
|
||||
uri = vscode.window.activeTextEditor!.document.uri;
|
||||
|
||||
if (vscode.window.activeTextEditor!.document.isDirty) {
|
||||
const result = await vscode.window.showWarningMessage(constants.saveChangesInFile, { modal: true }, constants.save);
|
||||
|
||||
if (result !== constants.save) {
|
||||
return;
|
||||
}
|
||||
|
||||
await vscode.window.activeTextEditor!.document.save();
|
||||
}
|
||||
}
|
||||
|
||||
// get all the Azure functions in the file
|
||||
const azureFunctionsService = await utils.getAzureFunctionService();
|
||||
let getAzureFunctionsResult;
|
||||
try {
|
||||
getAzureFunctionsResult = await azureFunctionsService.getAzureFunctions(uri.fsPath);
|
||||
} catch (e) {
|
||||
void vscode.window.showErrorMessage(utils.getErrorMessage(e));
|
||||
return;
|
||||
}
|
||||
|
||||
const azureFunctions = getAzureFunctionsResult.azureFunctions;
|
||||
|
||||
if (azureFunctions.length === 0) {
|
||||
void vscode.window.showErrorMessage(constants.noAzureFunctionsInFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. select Azure function from the current file
|
||||
const azureFunctionName = (await vscode.window.showQuickPick(azureFunctions, {
|
||||
canPickMany: false,
|
||||
title: constants.selectAzureFunction,
|
||||
ignoreFocusOut: true
|
||||
}));
|
||||
|
||||
if (!azureFunctionName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. select input or output binding
|
||||
const selectedBinding = await azureFunctionsUtils.promptForBindingType();
|
||||
|
||||
if (!selectedBinding) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. ask for object name for the binding
|
||||
const objectName = await azureFunctionsUtils.promptForObjectName(selectedBinding.type);
|
||||
|
||||
if (!objectName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. ask for connection string setting name
|
||||
let projectUri: vscode.Uri | undefined;
|
||||
try {
|
||||
projectUri = await azureFunctionsUtils.getAFProjectContainingFile(uri);
|
||||
} catch (e) {
|
||||
// continue even if there's no AF project found. The binding should still be able to be added as long as there was an azure function found in the file earlier
|
||||
}
|
||||
|
||||
let connectionStringSettingName = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(projectUri);
|
||||
if (!connectionStringSettingName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. insert binding
|
||||
try {
|
||||
const result = await azureFunctionsService.addSqlBinding(selectedBinding.type, uri.fsPath, azureFunctionName, objectName, connectionStringSettingName);
|
||||
|
||||
if (!result.success) {
|
||||
void vscode.window.showErrorMessage(result.errorMessage);
|
||||
TelemetryReporter.sendErrorEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding);
|
||||
return;
|
||||
}
|
||||
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding)
|
||||
.withAdditionalProperties({ bindingType: selectedBinding.label })
|
||||
.send();
|
||||
} catch (e) {
|
||||
void vscode.window.showErrorMessage(utils.getErrorMessage(e));
|
||||
TelemetryReporter.sendErrorEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding);
|
||||
return;
|
||||
}
|
||||
}
|
||||
56
extensions/sql-bindings/src/extension.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ITreeNodeInfo } from 'vscode-mssql';
|
||||
import { IExtension, BindingType } from 'sql-bindings';
|
||||
import { getAzdataApi, getVscodeMssqlApi } from './common/utils';
|
||||
import { addSqlBinding, createAzureFunction, getAzureFunctions } from './services/azureFunctionsService';
|
||||
import { launchAddSqlBindingQuickpick } from './dialogs/addSqlBindingQuickpick';
|
||||
import { promptForBindingType, promptAndUpdateConnectionStringSetting, promptForObjectName } from './common/azureFunctionsUtils';
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<IExtension> {
|
||||
const vscodeMssqlApi = await getVscodeMssqlApi();
|
||||
void vscode.commands.executeCommand('setContext', 'azdataAvailable', !!getAzdataApi());
|
||||
// register the add sql binding command
|
||||
context.subscriptions.push(vscode.commands.registerCommand('sqlBindings.addSqlBinding', async (uri: vscode.Uri | undefined) => { return launchAddSqlBindingQuickpick(uri); }));
|
||||
// Generate Azure Function command
|
||||
context.subscriptions.push(vscode.commands.registerCommand('sqlBindings.createAzureFunction', async (node: ITreeNodeInfo) => {
|
||||
let connectionInfo = node.connectionInfo;
|
||||
// set the database containing the selected table so it can be used
|
||||
// for the initial catalog property of the connection string
|
||||
let newNode: ITreeNodeInfo = node;
|
||||
while (newNode) {
|
||||
if (newNode.nodeType === 'Database') {
|
||||
connectionInfo.database = newNode.metadata.name;
|
||||
break;
|
||||
} else {
|
||||
newNode = newNode.parentNode;
|
||||
}
|
||||
}
|
||||
const connectionDetails = vscodeMssqlApi.createConnectionDetails(connectionInfo);
|
||||
const connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, false, false);
|
||||
await createAzureFunction(connectionString, node.metadata.schema, node.metadata.name);
|
||||
}));
|
||||
return {
|
||||
addSqlBinding: async (bindingType: BindingType, filePath: string, functionName: string, objectName: string, connectionStringSetting: string) => {
|
||||
return addSqlBinding(bindingType, filePath, functionName, objectName, connectionStringSetting);
|
||||
},
|
||||
promptForBindingType: async () => {
|
||||
return promptForBindingType();
|
||||
},
|
||||
promptForObjectName: async (bindingType: BindingType) => {
|
||||
return promptForObjectName(bindingType);
|
||||
},
|
||||
promptAndUpdateConnectionStringSetting: async (projectUri: vscode.Uri | undefined) => {
|
||||
return promptAndUpdateConnectionStringSetting(projectUri);
|
||||
},
|
||||
getAzureFunctions: async (filePath: string) => {
|
||||
return getAzureFunctions(filePath);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
}
|
||||
170
extensions/sql-bindings/src/services/azureFunctionsService.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 path from 'path';
|
||||
import * as utils from '../common/utils';
|
||||
import * as azureFunctionUtils from '../common/azureFunctionsUtils';
|
||||
import * as constants from '../common/constants';
|
||||
import * as azureFunctionsContracts from '../contracts/azureFunctions/azureFunctionsContracts';
|
||||
import { AddSqlBindingParams, BindingType, GetAzureFunctionsParams, GetAzureFunctionsResult, ResultStatus } from 'sql-bindings';
|
||||
|
||||
export const hostFileName: string = 'host.json';
|
||||
|
||||
|
||||
export async function createAzureFunction(connectionString: string, schema: string, table: string): Promise<void> {
|
||||
const azureFunctionApi = await azureFunctionUtils.getAzureFunctionsExtensionApi();
|
||||
if (!azureFunctionApi) {
|
||||
return;
|
||||
}
|
||||
let projectFile = await azureFunctionUtils.getAzureFunctionProject();
|
||||
let newHostProjectFile!: azureFunctionUtils.IFileFunctionObject;
|
||||
let hostFile: string;
|
||||
|
||||
if (!projectFile) {
|
||||
let projectCreate = await vscode.window.showErrorMessage(constants.azureFunctionsProjectMustBeOpened,
|
||||
constants.createProject, constants.learnMore);
|
||||
if (projectCreate === constants.learnMore) {
|
||||
void vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(constants.sqlBindingsDoc));
|
||||
return;
|
||||
} else if (projectCreate === constants.createProject) {
|
||||
// start the create azure function project flow
|
||||
try {
|
||||
// because of an AF extension API issue, we have to get the newly created file by adding a watcher
|
||||
// issue: https://github.com/microsoft/vscode-azurefunctions/issues/3052
|
||||
newHostProjectFile = await azureFunctionUtils.waitForNewHostFile();
|
||||
await azureFunctionApi.createFunction({});
|
||||
const timeoutForHostFile = utils.timeoutPromise(constants.timeoutProjectError);
|
||||
hostFile = await Promise.race([newHostProjectFile.filePromise, timeoutForHostFile]);
|
||||
if (hostFile) {
|
||||
// start the add sql binding flow
|
||||
projectFile = await azureFunctionUtils.getAzureFunctionProject();
|
||||
}
|
||||
} catch (error) {
|
||||
void vscode.window.showErrorMessage(utils.formatString(constants.errorNewAzureFunction, error.message ?? error));
|
||||
return;
|
||||
} finally {
|
||||
newHostProjectFile.watcherDisposable.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (projectFile) {
|
||||
// because of an AF extension API issue, we have to get the newly created file by adding a watcher
|
||||
// issue: https://github.com/microsoft/vscode-azurefunctions/issues/2908
|
||||
const newFunctionFileObject = azureFunctionUtils.waitForNewFunctionFile(projectFile);
|
||||
let functionFile: string;
|
||||
let functionName: string;
|
||||
|
||||
try {
|
||||
// get function name from user
|
||||
let uniqueFunctionName = await utils.getUniqueFileName(path.dirname(projectFile), table);
|
||||
functionName = await vscode.window.showInputBox({
|
||||
title: constants.functionNameTitle,
|
||||
value: uniqueFunctionName,
|
||||
ignoreFocusOut: true,
|
||||
validateInput: input => input ? undefined : constants.nameMustNotBeEmpty
|
||||
}) as string;
|
||||
if (!functionName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create C# HttpTrigger
|
||||
await azureFunctionApi.createFunction({
|
||||
language: 'C#',
|
||||
templateId: 'HttpTrigger',
|
||||
functionName: functionName,
|
||||
folderPath: projectFile
|
||||
});
|
||||
|
||||
// check for the new function file to be created and dispose of the file system watcher
|
||||
const timeoutForFunctionFile = utils.timeoutPromise(constants.timeoutAzureFunctionFileError);
|
||||
functionFile = await Promise.race([newFunctionFileObject.filePromise, timeoutForFunctionFile]);
|
||||
} finally {
|
||||
newFunctionFileObject.watcherDisposable.dispose();
|
||||
}
|
||||
|
||||
// select input or output binding
|
||||
const inputOutputItems: (vscode.QuickPickItem & { type: BindingType })[] = [
|
||||
{
|
||||
label: constants.input,
|
||||
type: BindingType.input
|
||||
},
|
||||
{
|
||||
label: constants.output,
|
||||
type: BindingType.output
|
||||
}
|
||||
];
|
||||
|
||||
const selectedBinding = await vscode.window.showQuickPick(inputOutputItems, {
|
||||
canPickMany: false,
|
||||
title: constants.selectBindingType,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
|
||||
if (!selectedBinding) {
|
||||
return;
|
||||
}
|
||||
|
||||
await azureFunctionUtils.addNugetReferenceToProjectFile(projectFile);
|
||||
await azureFunctionUtils.addConnectionStringToConfig(connectionString, projectFile);
|
||||
|
||||
let objectName = utils.generateQuotedFullName(schema, table);
|
||||
const azureFunctionsService = await utils.getAzureFunctionService();
|
||||
await azureFunctionsService.addSqlBinding(
|
||||
selectedBinding.type,
|
||||
functionFile,
|
||||
functionName,
|
||||
objectName,
|
||||
constants.sqlConnectionString
|
||||
);
|
||||
|
||||
azureFunctionUtils.overwriteAzureFunctionMethodBody(functionFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a SQL Binding to a specified Azure function in a file
|
||||
* @param bindingType Type of SQL Binding
|
||||
* @param filePath Path of the file where the Azure Functions are
|
||||
* @param functionName Name of the function where the SQL Binding is to be added
|
||||
* @param objectName Name of Object for the SQL Query
|
||||
* @param connectionStringSetting Setting for the connection string
|
||||
* @returns Azure Function SQL binding
|
||||
*/
|
||||
export async function addSqlBinding(
|
||||
bindingType: BindingType,
|
||||
filePath: string,
|
||||
functionName: string,
|
||||
objectName: string,
|
||||
connectionStringSetting: string
|
||||
): Promise<ResultStatus> {
|
||||
const params: AddSqlBindingParams = {
|
||||
bindingType: bindingType,
|
||||
filePath: filePath,
|
||||
functionName: functionName,
|
||||
objectName: objectName,
|
||||
connectionStringSetting: connectionStringSetting
|
||||
};
|
||||
|
||||
const vscodeMssqlApi = await utils.getVscodeMssqlApi();
|
||||
|
||||
return vscodeMssqlApi.sendRequest(azureFunctionsContracts.AddSqlBindingRequest.type, params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the names of the Azure functions in the file
|
||||
* @param filePath Path of the file to get the Azure functions
|
||||
* @returns array of names of Azure functions in the file
|
||||
*/
|
||||
export async function getAzureFunctions(filePath: string): Promise<GetAzureFunctionsResult> {
|
||||
const params: GetAzureFunctionsParams = {
|
||||
filePath: filePath
|
||||
};
|
||||
const vscodeMssqlApi = await utils.getVscodeMssqlApi();
|
||||
|
||||
return vscodeMssqlApi.sendRequest(azureFunctionsContracts.GetAzureFunctionsRequest.type, params);
|
||||
}
|
||||
121
extensions/sql-bindings/src/sql-bindings.d.ts
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
declare module 'sql-bindings' {
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export const enum extension {
|
||||
name = 'Microsoft.sql-bindings',
|
||||
vsCodeName = 'ms-mssql.sql-bindings-vscode'
|
||||
}
|
||||
|
||||
/**
|
||||
* sql bindings extension
|
||||
*/
|
||||
export interface IExtension {
|
||||
/**
|
||||
* Adds a SQL Binding to a specified Azure function in a file
|
||||
* @param bindingType Type of SQL Binding
|
||||
* @param filePath Path of the file where the Azure Functions are
|
||||
* @param functionName Name of the function where the SQL Binding is to be added
|
||||
* @param objectName Name of Object for the SQL Query
|
||||
* @param connectionStringSetting Setting for the connection string
|
||||
*/
|
||||
addSqlBinding(bindingType: BindingType, filePath: string, functionName: string, objectName: string, connectionStringSetting: string): Promise<ResultStatus>;
|
||||
|
||||
/**
|
||||
* Prompts the user to select type of binding and returns result
|
||||
*/
|
||||
promptForBindingType(): Promise<(vscode.QuickPickItem & { type: BindingType }) | undefined>;
|
||||
|
||||
/**
|
||||
* Prompts the user to enter object name for the SQL query
|
||||
* @param bindingType Type of SQL Binding
|
||||
*/
|
||||
promptForObjectName(bindingType: BindingType): Promise<string | undefined>;
|
||||
|
||||
/**
|
||||
* Prompts the user to enter connection setting and updates it from AF project
|
||||
* @param projectUri Azure Function project uri
|
||||
*/
|
||||
promptAndUpdateConnectionStringSetting(projectUri: vscode.Uri | undefined): Promise<string | undefined>;
|
||||
|
||||
/**
|
||||
* Gets the names of the Azure Functions in the file
|
||||
* @param filePath Path of the file to get the Azure Functions
|
||||
* @returns array of names of Azure Functions in the file
|
||||
*/
|
||||
getAzureFunctions(filePath: string): Promise<GetAzureFunctionsResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for adding a SQL binding to an Azure function
|
||||
*/
|
||||
export interface AddSqlBindingParams {
|
||||
/**
|
||||
* Absolute file path of file to add SQL binding
|
||||
*/
|
||||
filePath: string;
|
||||
|
||||
/**
|
||||
* Name of function to add SQL binding
|
||||
*/
|
||||
functionName: string;
|
||||
|
||||
/**
|
||||
* Name of object to use in SQL binding
|
||||
*/
|
||||
objectName: string;
|
||||
|
||||
/**
|
||||
* Type of Azure function binding
|
||||
*/
|
||||
bindingType: BindingType;
|
||||
|
||||
/**
|
||||
* Name of SQL connection string setting specified in local.settings.json
|
||||
*/
|
||||
connectionStringSetting: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Azure Functions binding type
|
||||
*/
|
||||
export const enum BindingType {
|
||||
input,
|
||||
output
|
||||
}
|
||||
|
||||
/**
|
||||
* Base result object from a request to the SQL Tools Service
|
||||
*/
|
||||
export interface ResultStatus {
|
||||
success: boolean;
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for getting the names of the Azure Functions in a file
|
||||
*/
|
||||
export interface GetAzureFunctionsParams {
|
||||
/**
|
||||
* Absolute file path of file to get Azure Functions
|
||||
*/
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result from a get Azure Functions request
|
||||
*/
|
||||
export interface GetAzureFunctionsResult extends ResultStatus {
|
||||
/**
|
||||
* Array of names of Azure Functions in the file
|
||||
*/
|
||||
azureFunctions: string[];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import * as sinon from 'sinon';
|
||||
import * as vscode from 'vscode';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as utils from '../../common/utils';
|
||||
import * as constants from '../../common/constants';
|
||||
import * as azureFunctionUtils from '../../common/azureFunctionsUtils';
|
||||
|
||||
import { createTestUtils, TestUtils, createTestCredentials } from '../testUtils';
|
||||
import { launchAddSqlBindingQuickpick } from '../../dialogs/addSqlBindingQuickpick';
|
||||
|
||||
let testUtils: TestUtils;
|
||||
describe('Add SQL Binding quick pick', () => {
|
||||
beforeEach(function (): void {
|
||||
testUtils = createTestUtils();
|
||||
});
|
||||
|
||||
afterEach(function (): void {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('Should show error if the file contains no Azure Functions', async function (): Promise<void> {
|
||||
sinon.stub(utils, 'getAzureFunctionService').resolves(testUtils.azureFunctionService.object);
|
||||
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||
const spy = sinon.spy(vscode.window, 'showErrorMessage');
|
||||
testUtils.azureFunctionService.setup(x => x.getAzureFunctions(TypeMoq.It.isAny())).returns(async () => {
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
errorMessage: '',
|
||||
azureFunctions: []
|
||||
});
|
||||
});
|
||||
await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'));
|
||||
|
||||
const msg = constants.noAzureFunctionsInFile;
|
||||
should(spy.calledOnce).be.true('showErrorMessage should have been called exactly once');
|
||||
should(spy.calledWith(msg)).be.true(`showErrorMessage not called with expected message '${msg}' Actual '${spy.getCall(0).args[0]}'`);
|
||||
});
|
||||
|
||||
it('Should show error if adding SQL binding was not successful', async function (): Promise<void> {
|
||||
sinon.stub(utils, 'getAzureFunctionService').resolves(testUtils.azureFunctionService.object);
|
||||
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||
const spy = sinon.spy(vscode.window, 'showErrorMessage');
|
||||
testUtils.azureFunctionService.setup(x => x.getAzureFunctions(TypeMoq.It.isAny())).returns(async () => {
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
errorMessage: '',
|
||||
azureFunctions: ['af1', 'af2']
|
||||
});
|
||||
});
|
||||
//failure since no AFs are found in the project
|
||||
sinon.stub(azureFunctionUtils, 'getAFProjectContainingFile').resolves(undefined);
|
||||
const errormsg = 'Error inserting binding';
|
||||
testUtils.azureFunctionService.setup(x => x.addSqlBinding(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(async () => {
|
||||
return Promise.resolve({
|
||||
success: false,
|
||||
errorMessage: errormsg
|
||||
});
|
||||
});
|
||||
|
||||
// select Azure function
|
||||
let quickpickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: 'af1' });
|
||||
// select input or output binding
|
||||
quickpickStub.onSecondCall().resolves({ label: constants.input });
|
||||
// give object name
|
||||
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('dbo.table1');
|
||||
// give connection string setting name
|
||||
inputBoxStub.onSecondCall().resolves('sqlConnectionString');
|
||||
|
||||
await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'));
|
||||
|
||||
should(spy.calledOnce).be.true('showErrorMessage should have been called exactly once');
|
||||
should(spy.calledWith(errormsg)).be.true(`showErrorMessage not called with expected message '${errormsg}' Actual '${spy.getCall(0).args[0]}'`);
|
||||
});
|
||||
|
||||
it('Should show error connection profile does not connect', async function (): Promise<void> {
|
||||
sinon.stub(utils, 'getAzureFunctionService').resolves(testUtils.azureFunctionService.object);
|
||||
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||
let connectionCreds = createTestCredentials();
|
||||
|
||||
sinon.stub(azureFunctionUtils, 'getAFProjectContainingFile').resolves(vscode.Uri.file('testUri'));
|
||||
testUtils.azureFunctionService.setup(x => x.getAzureFunctions(TypeMoq.It.isAny())).returns(async () => {
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
errorMessage: '',
|
||||
azureFunctions: ['af1']
|
||||
});
|
||||
});
|
||||
|
||||
// Mocks connect call to mssql
|
||||
let error = new Error('Connection Request Failed');
|
||||
testUtils.vscodeMssqlIExtension.setup(x => x.connect(TypeMoq.It.isAny(), undefined)).throws(error);
|
||||
|
||||
// Mocks promptForConnection
|
||||
testUtils.vscodeMssqlIExtension.setup(x => x.promptForConnection(true)).returns(() => Promise.resolve(connectionCreds));
|
||||
let quickpickStub = sinon.stub(vscode.window, 'showQuickPick');
|
||||
// select Azure function
|
||||
quickpickStub.onFirstCall().resolves({ label: 'af1' });
|
||||
// select input or output binding
|
||||
quickpickStub.onSecondCall().resolves({ label: constants.input });
|
||||
|
||||
// give object name
|
||||
let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('dbo.table1');
|
||||
|
||||
// select connection profile
|
||||
quickpickStub.onThirdCall().resolves({ label: constants.createNewLocalAppSettingWithIcon });
|
||||
|
||||
// give connection string setting name
|
||||
inputBoxStub.onSecondCall().resolves('SqlConnectionString');
|
||||
|
||||
// select connection profile method
|
||||
quickpickStub.onCall(3).resolves({ label: constants.connectionProfile });
|
||||
|
||||
await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'));
|
||||
|
||||
// should go back to the select connection string methods
|
||||
should(quickpickStub.callCount === 5);
|
||||
should(quickpickStub.getCall(4).args).deepEqual([
|
||||
[constants.connectionProfile, constants.userConnectionString],
|
||||
{
|
||||
canPickMany: false,
|
||||
ignoreFocusOut: true,
|
||||
title: constants.selectConnectionString
|
||||
}]);
|
||||
});
|
||||
});
|
||||
48
extensions/sql-bindings/src/test/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as testRunner from '@microsoft/vscodetestcover';
|
||||
|
||||
const suite = 'SQL Bindings Extension Tests';
|
||||
|
||||
const mochaOptions: any = {
|
||||
ui: 'bdd',
|
||||
useColors: true,
|
||||
timeout: 10000
|
||||
};
|
||||
|
||||
// set relevant mocha options from the environment
|
||||
if (process.env.ADS_TEST_GREP) {
|
||||
mochaOptions.grep = process.env.ADS_TEST_GREP;
|
||||
console.log(`setting options.grep to: ${mochaOptions.grep}`);
|
||||
}
|
||||
if (process.env.ADS_TEST_INVERT_GREP) {
|
||||
mochaOptions.invert = parseInt(process.env.ADS_TEST_INVERT_GREP);
|
||||
console.log(`setting options.invert to: ${mochaOptions.invert}`);
|
||||
}
|
||||
if (process.env.ADS_TEST_TIMEOUT) {
|
||||
mochaOptions.timeout = parseInt(process.env.ADS_TEST_TIMEOUT);
|
||||
console.log(`setting options.timeout to: ${mochaOptions.timeout}`);
|
||||
}
|
||||
if (process.env.ADS_TEST_RETRIES) {
|
||||
mochaOptions.retries = parseInt(process.env.ADS_TEST_RETRIES);
|
||||
console.log(`setting options.retries to: ${mochaOptions.retries}`);
|
||||
}
|
||||
|
||||
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
|
||||
mochaOptions.reporter = 'mocha-multi-reporters';
|
||||
mochaOptions.reporterOptions = {
|
||||
reporterEnabled: 'spec, mocha-junit-reporter',
|
||||
mochaJunitReporterReporterOptions: {
|
||||
testsuitesTitle: `${suite} ${process.platform}`,
|
||||
mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
testRunner.configure(mochaOptions, { coverConfig: '../../coverConfig.json' });
|
||||
|
||||
export = testRunner;
|
||||
391
extensions/sql-bindings/src/test/testUtils.ts
Normal file
@@ -0,0 +1,391 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 path from 'path';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as mssql from '../../../mssql/src/mssql';
|
||||
import * as vscodeMssql from 'vscode-mssql';
|
||||
import { RequestType } from 'vscode-languageclient';
|
||||
import { BindingType, GetAzureFunctionsResult } from 'sql-bindings';
|
||||
|
||||
export interface TestUtils {
|
||||
context: vscode.ExtensionContext;
|
||||
dacFxService: TypeMoq.IMock<mssql.IDacFxService>;
|
||||
azureFunctionService: TypeMoq.IMock<vscodeMssql.IAzureFunctionsService>;
|
||||
outputChannel: vscode.OutputChannel;
|
||||
vscodeMssqlIExtension: TypeMoq.IMock<vscodeMssql.IExtension>
|
||||
dacFxMssqlService: TypeMoq.IMock<vscodeMssql.IDacFxService>;
|
||||
schemaCompareService: TypeMoq.IMock<vscodeMssql.ISchemaCompareService>;
|
||||
}
|
||||
|
||||
export const mockDacFxResult = {
|
||||
operationId: '',
|
||||
success: true,
|
||||
errorMessage: '',
|
||||
report: ''
|
||||
};
|
||||
|
||||
export const mockDacFxOptionsResult: mssql.DacFxOptionsResult = {
|
||||
success: true,
|
||||
errorMessage: '',
|
||||
deploymentOptions: {
|
||||
ignoreTableOptions: false,
|
||||
ignoreSemicolonBetweenStatements: false,
|
||||
ignoreRouteLifetime: false,
|
||||
ignoreRoleMembership: false,
|
||||
ignoreQuotedIdentifiers: false,
|
||||
ignorePermissions: false,
|
||||
ignorePartitionSchemes: false,
|
||||
ignoreObjectPlacementOnPartitionScheme: false,
|
||||
ignoreNotForReplication: false,
|
||||
ignoreLoginSids: false,
|
||||
ignoreLockHintsOnIndexes: false,
|
||||
ignoreKeywordCasing: false,
|
||||
ignoreIndexPadding: false,
|
||||
ignoreIndexOptions: false,
|
||||
ignoreIncrement: false,
|
||||
ignoreIdentitySeed: false,
|
||||
ignoreUserSettingsObjects: false,
|
||||
ignoreFullTextCatalogFilePath: false,
|
||||
ignoreWhitespace: false,
|
||||
ignoreWithNocheckOnForeignKeys: false,
|
||||
verifyCollationCompatibility: false,
|
||||
unmodifiableObjectWarnings: false,
|
||||
treatVerificationErrorsAsWarnings: false,
|
||||
scriptRefreshModule: false,
|
||||
scriptNewConstraintValidation: false,
|
||||
scriptFileSize: false,
|
||||
scriptDeployStateChecks: false,
|
||||
scriptDatabaseOptions: false,
|
||||
scriptDatabaseCompatibility: false,
|
||||
scriptDatabaseCollation: false,
|
||||
runDeploymentPlanExecutors: false,
|
||||
registerDataTierApplication: false,
|
||||
populateFilesOnFileGroups: false,
|
||||
noAlterStatementsToChangeClrTypes: false,
|
||||
includeTransactionalScripts: false,
|
||||
includeCompositeObjects: false,
|
||||
allowUnsafeRowLevelSecurityDataMovement: false,
|
||||
ignoreWithNocheckOnCheckConstraints: false,
|
||||
ignoreFillFactor: false,
|
||||
ignoreFileSize: false,
|
||||
ignoreFilegroupPlacement: false,
|
||||
doNotAlterReplicatedObjects: false,
|
||||
doNotAlterChangeDataCaptureObjects: false,
|
||||
disableAndReenableDdlTriggers: false,
|
||||
deployDatabaseInSingleUserMode: false,
|
||||
createNewDatabase: false,
|
||||
compareUsingTargetCollation: false,
|
||||
commentOutSetVarDeclarations: false,
|
||||
blockWhenDriftDetected: false,
|
||||
blockOnPossibleDataLoss: false,
|
||||
backupDatabaseBeforeChanges: false,
|
||||
allowIncompatiblePlatform: false,
|
||||
allowDropBlockingAssemblies: false,
|
||||
dropConstraintsNotInSource: false,
|
||||
dropDmlTriggersNotInSource: false,
|
||||
dropExtendedPropertiesNotInSource: false,
|
||||
dropIndexesNotInSource: false,
|
||||
ignoreFileAndLogFilePath: false,
|
||||
ignoreExtendedProperties: false,
|
||||
ignoreDmlTriggerState: false,
|
||||
ignoreDmlTriggerOrder: false,
|
||||
ignoreDefaultSchema: false,
|
||||
ignoreDdlTriggerState: false,
|
||||
ignoreDdlTriggerOrder: false,
|
||||
ignoreCryptographicProviderFilePath: false,
|
||||
verifyDeployment: false,
|
||||
ignoreComments: false,
|
||||
ignoreColumnCollation: false,
|
||||
ignoreAuthorizer: false,
|
||||
ignoreAnsiNulls: false,
|
||||
generateSmartDefaults: false,
|
||||
dropStatisticsNotInSource: false,
|
||||
dropRoleMembersNotInSource: false,
|
||||
dropPermissionsNotInSource: false,
|
||||
dropObjectsNotInSource: false,
|
||||
ignoreColumnOrder: false,
|
||||
doNotDropObjectTypes: [],
|
||||
excludeObjectTypes: []
|
||||
}
|
||||
};
|
||||
|
||||
export class MockDacFxService implements mssql.IDacFxService {
|
||||
public exportBacpac(_: string, __: string, ___: string, ____: azdata.TaskExecutionMode): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public importBacpac(_: string, __: string, ___: string, ____: azdata.TaskExecutionMode): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public extractDacpac(_: string, __: string, ___: string, ____: string, _____: string, ______: azdata.TaskExecutionMode): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public createProjectFromDatabase(_: string, __: string, ___: string, ____: string, _____: string, ______: mssql.ExtractTarget, _______: azdata.TaskExecutionMode): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public deployDacpac(_: string, __: string, ___: boolean, ____: string, _____: azdata.TaskExecutionMode, ______?: Record<string, string>): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public generateDeployScript(_: string, __: string, ___: string, ____: azdata.TaskExecutionMode, ______?: Record<string, string>): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public generateDeployPlan(_: string, __: string, ___: string, ____: azdata.TaskExecutionMode): Thenable<mssql.GenerateDeployPlanResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public getOptionsFromProfile(_: string): Thenable<mssql.DacFxOptionsResult> { return Promise.resolve(mockDacFxOptionsResult); }
|
||||
public validateStreamingJob(_: string, __: string): Thenable<mssql.ValidateStreamingJobResult> { return Promise.resolve(mockDacFxResult); }
|
||||
}
|
||||
|
||||
export const mockResultStatus = {
|
||||
success: true,
|
||||
errorMessage: ''
|
||||
};
|
||||
|
||||
export const mockGetAzureFunctionsResult = {
|
||||
success: true,
|
||||
errorMessage: '',
|
||||
azureFunctions: []
|
||||
};
|
||||
|
||||
export class MockAzureFunctionService implements vscodeMssql.IAzureFunctionsService {
|
||||
addSqlBinding(_: BindingType, __: string, ___: string, ____: string, _____: string): Thenable<vscodeMssql.ResultStatus> { return Promise.resolve(mockResultStatus); }
|
||||
getAzureFunctions(_: string): Thenable<GetAzureFunctionsResult> { return Promise.resolve(mockGetAzureFunctionsResult); }
|
||||
}
|
||||
|
||||
export const mockDacFxMssqlOptionResult: vscodeMssql.DacFxOptionsResult = {
|
||||
success: true,
|
||||
errorMessage: '',
|
||||
deploymentOptions: {
|
||||
ignoreTableOptions: false,
|
||||
ignoreSemicolonBetweenStatements: false,
|
||||
ignoreRouteLifetime: false,
|
||||
ignoreRoleMembership: false,
|
||||
ignoreQuotedIdentifiers: false,
|
||||
ignorePermissions: false,
|
||||
ignorePartitionSchemes: false,
|
||||
ignoreObjectPlacementOnPartitionScheme: false,
|
||||
ignoreNotForReplication: false,
|
||||
ignoreLoginSids: false,
|
||||
ignoreLockHintsOnIndexes: false,
|
||||
ignoreKeywordCasing: false,
|
||||
ignoreIndexPadding: false,
|
||||
ignoreIndexOptions: false,
|
||||
ignoreIncrement: false,
|
||||
ignoreIdentitySeed: false,
|
||||
ignoreUserSettingsObjects: false,
|
||||
ignoreFullTextCatalogFilePath: false,
|
||||
ignoreWhitespace: false,
|
||||
ignoreWithNocheckOnForeignKeys: false,
|
||||
verifyCollationCompatibility: false,
|
||||
unmodifiableObjectWarnings: false,
|
||||
treatVerificationErrorsAsWarnings: false,
|
||||
scriptRefreshModule: false,
|
||||
scriptNewConstraintValidation: false,
|
||||
scriptFileSize: false,
|
||||
scriptDeployStateChecks: false,
|
||||
scriptDatabaseOptions: false,
|
||||
scriptDatabaseCompatibility: false,
|
||||
scriptDatabaseCollation: false,
|
||||
runDeploymentPlanExecutors: false,
|
||||
registerDataTierApplication: false,
|
||||
populateFilesOnFileGroups: false,
|
||||
noAlterStatementsToChangeClrTypes: false,
|
||||
includeTransactionalScripts: false,
|
||||
includeCompositeObjects: false,
|
||||
allowUnsafeRowLevelSecurityDataMovement: false,
|
||||
ignoreWithNocheckOnCheckConstraints: false,
|
||||
ignoreFillFactor: false,
|
||||
ignoreFileSize: false,
|
||||
ignoreFilegroupPlacement: false,
|
||||
doNotAlterReplicatedObjects: false,
|
||||
doNotAlterChangeDataCaptureObjects: false,
|
||||
disableAndReenableDdlTriggers: false,
|
||||
deployDatabaseInSingleUserMode: false,
|
||||
createNewDatabase: false,
|
||||
compareUsingTargetCollation: false,
|
||||
commentOutSetVarDeclarations: false,
|
||||
blockWhenDriftDetected: false,
|
||||
blockOnPossibleDataLoss: false,
|
||||
backupDatabaseBeforeChanges: false,
|
||||
allowIncompatiblePlatform: false,
|
||||
allowDropBlockingAssemblies: false,
|
||||
dropConstraintsNotInSource: false,
|
||||
dropDmlTriggersNotInSource: false,
|
||||
dropExtendedPropertiesNotInSource: false,
|
||||
dropIndexesNotInSource: false,
|
||||
ignoreFileAndLogFilePath: false,
|
||||
ignoreExtendedProperties: false,
|
||||
ignoreDmlTriggerState: false,
|
||||
ignoreDmlTriggerOrder: false,
|
||||
ignoreDefaultSchema: false,
|
||||
ignoreDdlTriggerState: false,
|
||||
ignoreDdlTriggerOrder: false,
|
||||
ignoreCryptographicProviderFilePath: false,
|
||||
verifyDeployment: false,
|
||||
ignoreComments: false,
|
||||
ignoreColumnCollation: false,
|
||||
ignoreAuthorizer: false,
|
||||
ignoreAnsiNulls: false,
|
||||
generateSmartDefaults: false,
|
||||
dropStatisticsNotInSource: false,
|
||||
dropRoleMembersNotInSource: false,
|
||||
dropPermissionsNotInSource: false,
|
||||
dropObjectsNotInSource: false,
|
||||
ignoreColumnOrder: false,
|
||||
doNotDropObjectTypes: [],
|
||||
excludeObjectTypes: []
|
||||
}
|
||||
};
|
||||
|
||||
export class MockDacFxMssqlService implements vscodeMssql.IDacFxService {
|
||||
public exportBacpac(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode): Thenable<vscodeMssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public importBacpac(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public extractDacpac(_: string, __: string, ___: string, ____: string, _____: string, ______: vscodeMssql.TaskExecutionMode): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public createProjectFromDatabase(_: string, __: string, ___: string, ____: string, _____: string, ______: vscodeMssql.ExtractTarget, _______: vscodeMssql.TaskExecutionMode): Thenable<vscodeMssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public deployDacpac(_: string, __: string, ___: boolean, ____: string, _____: vscodeMssql.TaskExecutionMode, ______?: Record<string, string>): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public generateDeployScript(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode, ______?: Record<string, string>): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public generateDeployPlan(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode): Thenable<vscodeMssql.GenerateDeployPlanResult> { return Promise.resolve(mockDacFxResult); }
|
||||
public getOptionsFromProfile(_: string): Thenable<vscodeMssql.DacFxOptionsResult> { return Promise.resolve(mockDacFxMssqlOptionResult); }
|
||||
public validateStreamingJob(_: string, __: string): Thenable<mssql.ValidateStreamingJobResult> { return Promise.resolve(mockDacFxResult); }
|
||||
}
|
||||
|
||||
export class MockSchemaCompareService implements vscodeMssql.ISchemaCompareService {
|
||||
schemaCompareGetDefaultOptions(): Thenable<vscodeMssql.SchemaCompareOptionsResult> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export class MockVscodeMssqlIExtension implements vscodeMssql.IExtension {
|
||||
sqlToolsServicePath: string = '';
|
||||
dacFx: vscodeMssql.IDacFxService;
|
||||
schemaCompare: vscodeMssql.ISchemaCompareService;
|
||||
azureFunctions: vscodeMssql.IAzureFunctionsService;
|
||||
|
||||
constructor() {
|
||||
this.dacFx = new MockDacFxMssqlService;
|
||||
this.schemaCompare = new MockSchemaCompareService;
|
||||
this.azureFunctions = new MockAzureFunctionService;
|
||||
}
|
||||
sendRequest<P, R, E, R0>(_: RequestType<P, R, E, R0>, __?: P): Promise<R> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
promptForConnection(_?: boolean): Promise<vscodeMssql.IConnectionInfo | undefined> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
connect(_: vscodeMssql.IConnectionInfo, __?: boolean): Promise<string> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
listDatabases(_: string): Promise<string[]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getDatabaseNameFromTreeNode(_: vscodeMssql.ITreeNodeInfo): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
getConnectionString(_: string | vscodeMssql.ConnectionDetails, ___?: boolean, _____?: boolean): Promise<string> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
createConnectionDetails(_: vscodeMssql.IConnectionInfo): vscodeMssql.ConnectionDetails {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export function createTestUtils(): TestUtils {
|
||||
let extensionPath = path.join(__dirname, '..', '..');
|
||||
|
||||
return {
|
||||
context: {
|
||||
subscriptions: [],
|
||||
workspaceState: {
|
||||
get: () => { return undefined; },
|
||||
update: () => { return Promise.resolve(); },
|
||||
keys: () => []
|
||||
},
|
||||
globalState: {
|
||||
setKeysForSync: (): void => { },
|
||||
get: (): any | undefined => { return Promise.resolve(); },
|
||||
update: (): Thenable<void> => { return Promise.resolve(); },
|
||||
keys: () => []
|
||||
},
|
||||
extensionPath: extensionPath,
|
||||
asAbsolutePath: () => { return ''; },
|
||||
storagePath: '',
|
||||
globalStoragePath: '',
|
||||
logPath: '',
|
||||
extensionUri: vscode.Uri.parse(''),
|
||||
environmentVariableCollection: undefined as any,
|
||||
extensionMode: undefined as any,
|
||||
globalStorageUri: vscode.Uri.parse('test://'),
|
||||
logUri: vscode.Uri.parse('test://'),
|
||||
storageUri: vscode.Uri.parse('test://'),
|
||||
secrets: undefined as any,
|
||||
extension: undefined as any
|
||||
},
|
||||
dacFxService: TypeMoq.Mock.ofType(MockDacFxService),
|
||||
azureFunctionService: TypeMoq.Mock.ofType(MockAzureFunctionService),
|
||||
vscodeMssqlIExtension: TypeMoq.Mock.ofType(MockVscodeMssqlIExtension),
|
||||
dacFxMssqlService: TypeMoq.Mock.ofType(MockDacFxMssqlService),
|
||||
schemaCompareService: TypeMoq.Mock.ofType(MockSchemaCompareService),
|
||||
outputChannel: {
|
||||
name: '',
|
||||
append: () => { },
|
||||
appendLine: () => { },
|
||||
clear: () => { },
|
||||
show: () => { },
|
||||
hide: () => { },
|
||||
dispose: () => { }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Mock test data
|
||||
export const mockConnectionProfile: azdata.IConnectionProfile = {
|
||||
connectionName: 'My Connection',
|
||||
serverName: 'My Server',
|
||||
databaseName: 'My Database',
|
||||
userName: 'My User',
|
||||
password: 'My Pwd',
|
||||
authenticationType: 'SqlLogin',
|
||||
savePassword: false,
|
||||
groupFullName: 'My groupName',
|
||||
groupId: 'My GroupId',
|
||||
providerName: 'My Server',
|
||||
saveProfile: true,
|
||||
id: 'My Id',
|
||||
options: {
|
||||
server: 'My Server',
|
||||
database: 'My Database',
|
||||
user: 'My User',
|
||||
password: 'My Pwd',
|
||||
authenticationType: 'SqlLogin',
|
||||
connectionName: 'My Connection Name'
|
||||
}
|
||||
};
|
||||
|
||||
export function createTestCredentials(): vscodeMssql.IConnectionInfo {
|
||||
const creds: vscodeMssql.IConnectionInfo = {
|
||||
server: 'my-server',
|
||||
database: 'my_db',
|
||||
user: 'sa',
|
||||
password: '12345678',
|
||||
email: 'test-email',
|
||||
accountId: 'test-account-id',
|
||||
tenantId: 'test-tenant-id',
|
||||
port: 1234,
|
||||
authenticationType: 'test',
|
||||
azureAccountToken: '',
|
||||
expiresOn: 0,
|
||||
encrypt: false,
|
||||
trustServerCertificate: false,
|
||||
persistSecurityInfo: false,
|
||||
connectTimeout: 15,
|
||||
connectRetryCount: 0,
|
||||
connectRetryInterval: 0,
|
||||
applicationName: 'vscode-mssql',
|
||||
workstationId: 'test',
|
||||
applicationIntent: '',
|
||||
currentLanguage: '',
|
||||
pooling: true,
|
||||
maxPoolSize: 15,
|
||||
minPoolSize: 0,
|
||||
loadBalanceTimeout: 0,
|
||||
replication: false,
|
||||
attachDbFilename: '',
|
||||
failoverPartner: '',
|
||||
multiSubnetFailover: false,
|
||||
multipleActiveResultSets: false,
|
||||
packetSize: 8192,
|
||||
typeSystemVersion: 'Latest',
|
||||
connectionString: ''
|
||||
};
|
||||
return creds;
|
||||
}
|
||||