mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-27 18:47:22 -05:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2acd37a1b1 | ||
|
|
85786b48c3 | ||
|
|
acd6257bae | ||
|
|
f007d707b6 | ||
|
|
b118b4bc7a | ||
|
|
f3edece70b | ||
|
|
4de376c357 | ||
|
|
0753b63ad0 | ||
|
|
0523190fbb | ||
|
|
de994972db | ||
|
|
3b06473c49 | ||
|
|
bb75143282 | ||
|
|
3857f11dc9 | ||
|
|
0918d93a18 | ||
|
|
25d96d041e | ||
|
|
0dc88501cf | ||
|
|
bac7eccbaf | ||
|
|
585d609ebb | ||
|
|
168385b6f1 | ||
|
|
eb4612100d | ||
|
|
d89ce8f9ec | ||
|
|
d84dd31491 | ||
|
|
b6632547a2 | ||
|
|
79669f073c | ||
|
|
e078e3bc48 | ||
|
|
72d035be98 | ||
|
|
f6e7b56946 | ||
|
|
2893659983 | ||
|
|
f220f6486a | ||
|
|
f94a9d0d58 | ||
|
|
9bcd7cdd80 | ||
|
|
5e1978e29f | ||
|
|
e4614582cd | ||
|
|
0fbc3d1cb6 | ||
|
|
3a70bea70d | ||
|
|
ea84e60fa0 | ||
|
|
10ac5fdfab | ||
|
|
1c621da4c7 | ||
|
|
6e5fc9c495 | ||
|
|
e3daec38c6 | ||
|
|
11636f8fc3 | ||
|
|
8c08d5117b | ||
|
|
fa62ec1f34 | ||
|
|
dac1970c43 | ||
|
|
e27a57715c | ||
|
|
e6ca724571 | ||
|
|
57d2ceec9d | ||
|
|
d87855cf1f | ||
|
|
1656a9c285 | ||
|
|
89a845d994 | ||
|
|
80901c9a7b | ||
|
|
0ace033a6f | ||
|
|
df5df38a55 | ||
|
|
4199cec393 | ||
|
|
9b296c9f42 | ||
|
|
dbb40d820c | ||
|
|
2ca6e7ae64 | ||
|
|
d7d13c7218 | ||
|
|
485e185270 | ||
|
|
8420d9f04e | ||
|
|
9a7810cbee | ||
|
|
0fd3b25ccd | ||
|
|
921e546fd7 | ||
|
|
5fe72d318b | ||
|
|
5105694fb3 | ||
|
|
53cb97af9b | ||
|
|
a7c4a259ad | ||
|
|
5498c1c3a9 | ||
|
|
3e5b7fd3e6 | ||
|
|
e3873828e0 | ||
|
|
db57eb9581 | ||
|
|
8d46aef40c | ||
|
|
cb2a02d82c | ||
|
|
52abcd68af | ||
|
|
5cb9b36329 | ||
|
|
13d2ce7d5d | ||
|
|
64c375a12d | ||
|
|
8449888db8 | ||
|
|
cebbd04d10 | ||
|
|
d7a425239b | ||
|
|
b4deca3b4b | ||
|
|
92e0b2e130 | ||
|
|
28230b67d4 | ||
|
|
af36d79c53 | ||
|
|
0999060827 | ||
|
|
0e6117f044 | ||
|
|
fdc21bfb9e |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
|||||||
cat .build/coverage-single/lcov.info ./extensions/admin-tool-ext-win/coverage/lcov.info ./extensions/agent/coverage/lcov.info ./extensions/azurecore/coverage/lcov.info ./extensions/cms/coverage/lcov.info ./extensions/dacpac/coverage/lcov.info ./extensions/schema-compare/coverage/lcov.info ./extensions/notebook/coverage/lcov.info ./extensions/resource-deployment/coverage/lcov.info ./extensions/machine-learning/coverage/lcov.info > .build/coverage-combined/lcov.info
|
cat .build/coverage-single/lcov.info ./extensions/admin-tool-ext-win/coverage/lcov.info ./extensions/agent/coverage/lcov.info ./extensions/azurecore/coverage/lcov.info ./extensions/cms/coverage/lcov.info ./extensions/dacpac/coverage/lcov.info ./extensions/schema-compare/coverage/lcov.info ./extensions/notebook/coverage/lcov.info ./extensions/resource-deployment/coverage/lcov.info ./extensions/machine-learning/coverage/lcov.info > .build/coverage-combined/lcov.info
|
||||||
name: Merge coverage reports
|
name: Merge coverage reports
|
||||||
- name: Upload Code Coverage
|
- name: Upload Code Coverage
|
||||||
uses: coverallsapp/github-action@v1.0.1
|
uses: coverallsapp/github-action@v1.1.1
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
path-to-lcov: '.build/coverage-combined/lcov.info'
|
path-to-lcov: '.build/coverage-combined/lcov.info'
|
||||||
|
|||||||
1
.github/workflows/on-issue-open.yml
vendored
1
.github/workflows/on-issue-open.yml
vendored
@@ -19,5 +19,6 @@ jobs:
|
|||||||
- name: Run CopyCat
|
- name: Run CopyCat
|
||||||
uses: ./actions/build/actions/copycat
|
uses: ./actions/build/actions/copycat
|
||||||
with:
|
with:
|
||||||
|
token: ${{secrets.TRIAGE_PAT}}
|
||||||
owner: anthonydresser
|
owner: anthonydresser
|
||||||
repo: testissues
|
repo: testissues
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"useTabs": true,
|
|
||||||
"printWidth": 120,
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": true
|
|
||||||
}
|
|
||||||
19
.vscode/launch.json
vendored
19
.vscode/launch.json
vendored
@@ -145,6 +145,25 @@
|
|||||||
"order": 2
|
"order": 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Main Process",
|
||||||
|
"runtimeExecutable": "${workspaceFolder}/scripts/code.sh",
|
||||||
|
"windows": {
|
||||||
|
"runtimeExecutable": "${workspaceFolder}/scripts/code.bat",
|
||||||
|
},
|
||||||
|
"runtimeArgs": [
|
||||||
|
"--no-cached-data"
|
||||||
|
],
|
||||||
|
"outFiles": [
|
||||||
|
"${workspaceFolder}/out/**/*.js"
|
||||||
|
],
|
||||||
|
"presentation": {
|
||||||
|
"group": "1_vscode",
|
||||||
|
"order": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "chrome",
|
"type": "chrome",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
|
|||||||
19
.vscode/searches/es6.code-search
vendored
19
.vscode/searches/es6.code-search
vendored
@@ -2,7 +2,24 @@
|
|||||||
# Flags: CaseSensitive WordMatch
|
# Flags: CaseSensitive WordMatch
|
||||||
# ContextLines: 2
|
# ContextLines: 2
|
||||||
|
|
||||||
10 results - 4 files
|
16 results - 5 files
|
||||||
|
|
||||||
|
src/vs/base/browser/dom.ts:
|
||||||
|
81 };
|
||||||
|
82
|
||||||
|
83: /** @deprecated ES6 - use classList*/
|
||||||
|
84 export const hasClass: (node: HTMLElement | SVGElement, className: string) => boolean = _classList.hasClass.bind(_classList);
|
||||||
|
85: /** @deprecated ES6 - use classList*/
|
||||||
|
86 export const addClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.addClass.bind(_classList);
|
||||||
|
87: /** @deprecated ES6 - use classList*/
|
||||||
|
88 export const addClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.addClasses.bind(_classList);
|
||||||
|
89: /** @deprecated ES6 - use classList*/
|
||||||
|
90 export const removeClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.removeClass.bind(_classList);
|
||||||
|
91: /** @deprecated ES6 - use classList*/
|
||||||
|
92 export const removeClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.removeClasses.bind(_classList);
|
||||||
|
93: /** @deprecated ES6 - use classList*/
|
||||||
|
94 export const toggleClass: (node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean) => void = _classList.toggleClass.bind(_classList);
|
||||||
|
95
|
||||||
|
|
||||||
src/vs/base/common/arrays.ts:
|
src/vs/base/common/arrays.ts:
|
||||||
401
|
401
|
||||||
|
|||||||
2
.yarnrc
2
.yarnrc
@@ -1,3 +1,3 @@
|
|||||||
disturl "https://atom.io/download/electron"
|
disturl "https://atom.io/download/electron"
|
||||||
target "7.2.2"
|
target "7.2.4"
|
||||||
runtime "electron"
|
runtime "electron"
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## Version 1.17.1
|
||||||
|
* Release date: April 29, 2020
|
||||||
|
* Release status: General Availability
|
||||||
|
* Hotfix for https://github.com/microsoft/azuredatastudio/milestone/54?closed=1
|
||||||
|
|
||||||
## Version 1.17.0
|
## Version 1.17.0
|
||||||
* Release date: April 27, 2020
|
* Release date: April 27, 2020
|
||||||
* Release status: General Availability
|
* Release status: General Availability
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ expressly granted herein, whether by implication, estoppel or otherwise.
|
|||||||
node-fetch: https://github.com/bitinn/node-fetch
|
node-fetch: https://github.com/bitinn/node-fetch
|
||||||
node-pty: https://github.com/Tyriar/node-pty
|
node-pty: https://github.com/Tyriar/node-pty
|
||||||
nsfw: https://github.com/Axosoft/nsfw
|
nsfw: https://github.com/Axosoft/nsfw
|
||||||
|
optimist: https://github.com/substack/node-optimist
|
||||||
primeng: https://github.com/primefaces/primeng
|
primeng: https://github.com/primefaces/primeng
|
||||||
process-nextick-args: https://github.com/calvinmetcalf/process-nextick-args
|
process-nextick-args: https://github.com/calvinmetcalf/process-nextick-args
|
||||||
pty.js: https://github.com/chjj/pty.js
|
pty.js: https://github.com/chjj/pty.js
|
||||||
@@ -72,6 +73,7 @@ expressly granted herein, whether by implication, estoppel or otherwise.
|
|||||||
vscode-textmate: https://github.com/Microsoft/vscode-textmate
|
vscode-textmate: https://github.com/Microsoft/vscode-textmate
|
||||||
winreg: https://github.com/fresc81/node-winreg
|
winreg: https://github.com/fresc81/node-winreg
|
||||||
xterm: https://github.com/sourcelair/xterm.js
|
xterm: https://github.com/sourcelair/xterm.js
|
||||||
|
yargs: https://github.com/yargs/yargs
|
||||||
yauzl: https://github.com/thejoshwolfe/yauzl
|
yauzl: https://github.com/thejoshwolfe/yauzl
|
||||||
zone.js: https://www.npmjs.com/package/zone
|
zone.js: https://www.npmjs.com/package/zone
|
||||||
|
|
||||||
@@ -1451,6 +1453,32 @@ SOFTWARE.
|
|||||||
=========================================
|
=========================================
|
||||||
END OF nsfw NOTICES AND INFORMATION
|
END OF nsfw NOTICES AND INFORMATION
|
||||||
|
|
||||||
|
%% optimist NOTICES AND INFORMATION BEGIN HERE
|
||||||
|
=========================================
|
||||||
|
Copyright 2010 James Halliday (mail@substack.net)
|
||||||
|
|
||||||
|
This project is free software released under the MIT/X11 license:
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
=========================================
|
||||||
|
END OF optimist NOTICES AND INFORMATION
|
||||||
|
|
||||||
%% primeng NOTICES AND INFORMATION BEGIN HERE
|
%% primeng NOTICES AND INFORMATION BEGIN HERE
|
||||||
=========================================
|
=========================================
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
@@ -2222,6 +2250,32 @@ THE SOFTWARE.
|
|||||||
=========================================
|
=========================================
|
||||||
END OF xterm NOTICES AND INFORMATION
|
END OF xterm NOTICES AND INFORMATION
|
||||||
|
|
||||||
|
%% yargs NOTICES AND INFORMATION BEGIN HERE
|
||||||
|
=========================================
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright 2010 James Halliday (mail@substack.net); Modified work Copyright 2014 Contributors (ben@npmjs.com)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
=========================================
|
||||||
|
END OF yargs NOTICES AND INFORMATION
|
||||||
|
|
||||||
%% yauzl NOTICES AND INFORMATION BEGIN HERE
|
%% yauzl NOTICES AND INFORMATION BEGIN HERE
|
||||||
=========================================
|
=========================================
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|||||||
@@ -2,54 +2,76 @@ steps:
|
|||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: "12.13.0"
|
versionSpec: "12.13.0"
|
||||||
|
|
||||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@3 # {{SQL CARBON EDIT}} update version
|
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@3 # {{SQL CARBON EDIT}} update version
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: "1.x"
|
versionSpec: "1.x"
|
||||||
|
|
||||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||||
inputs:
|
inputs:
|
||||||
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
||||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
||||||
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
CHILD_CONCURRENCY=1 yarn --frozen-lockfile
|
CHILD_CONCURRENCY=1 yarn --frozen-lockfile
|
||||||
displayName: Install Dependencies
|
displayName: Install Dependencies
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||||
|
|
||||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||||
inputs:
|
inputs:
|
||||||
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
||||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
||||||
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
yarn electron x64
|
yarn electron x64
|
||||||
displayName: Download Electron
|
displayName: Download Electron
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
yarn gulp hygiene
|
yarn gulp hygiene
|
||||||
displayName: Run Hygiene Checks
|
displayName: Run Hygiene Checks
|
||||||
|
|
||||||
- script: | # {{SQL CARBON EDIT}} add step
|
- script: | # {{SQL CARBON EDIT}} add step
|
||||||
yarn strict-vscode
|
yarn strict-vscode
|
||||||
displayName: Run Strict Null Check.
|
displayName: Run Strict Null Check.
|
||||||
|
|
||||||
# - script: | {{SQL CARBON EDIT}} remove step
|
# - script: | {{SQL CARBON EDIT}} remove step
|
||||||
# yarn monaco-compile-check
|
# yarn monaco-compile-check
|
||||||
# displayName: Run Monaco Editor Checks
|
# displayName: Run Monaco Editor Checks
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
yarn valid-layers-check
|
yarn valid-layers-check
|
||||||
displayName: Run Valid Layers Checks
|
displayName: Run Valid Layers Checks
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
yarn compile
|
yarn compile
|
||||||
displayName: Compile Sources
|
displayName: Compile Sources
|
||||||
|
|
||||||
# - script: | {{SQL CARBON EDIT}} remove step
|
# - script: | {{SQL CARBON EDIT}} remove step
|
||||||
# yarn download-builtin-extensions
|
# yarn download-builtin-extensions
|
||||||
# displayName: Download Built-in Extensions
|
# displayName: Download Built-in Extensions
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
./scripts/test.sh --tfs "Unit Tests"
|
./scripts/test.sh --tfs "Unit Tests"
|
||||||
displayName: Run Unit Tests (Electron)
|
displayName: Run Unit Tests (Electron)
|
||||||
|
|
||||||
# - script: | {{SQL CARBON EDIT}} disable
|
# - script: | {{SQL CARBON EDIT}} disable
|
||||||
# yarn test-browser --browser chromium --browser webkit --browser firefox
|
# yarn test-browser --browser chromium --browser webkit --browser firefox
|
||||||
# displayName: Run Unit Tests (Browser)
|
# displayName: Run Unit Tests (Browser)
|
||||||
|
|
||||||
# - script: | {{SQL CARBON EDIT}} disable
|
# - script: | {{SQL CARBON EDIT}} disable
|
||||||
# ./scripts/test-integration.sh --tfs "Integration Tests"
|
# ./scripts/test-integration.sh --tfs "Integration Tests"
|
||||||
# displayName: Run Integration Tests (Electron)
|
# displayName: Run Integration Tests (Electron)
|
||||||
|
|
||||||
|
# - task: PublishPipelineArtifact@0
|
||||||
|
# inputs:
|
||||||
|
# artifactName: crash-dump-macos
|
||||||
|
# targetPath: .build/crashes
|
||||||
|
# displayName: 'Publish Crash Reports'
|
||||||
|
# condition: succeededOrFailed()
|
||||||
|
|
||||||
- task: PublishTestResults@2
|
- task: PublishTestResults@2
|
||||||
displayName: Publish Tests Results
|
displayName: Publish Tests Results
|
||||||
inputs:
|
inputs:
|
||||||
|
|||||||
@@ -2,13 +2,5 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.cs.allow-jit</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.cs.disable-library-validation</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -152,15 +152,29 @@ steps:
|
|||||||
displayName: Run smoke tests (Browser)
|
displayName: Run smoke tests (Browser)
|
||||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||||
|
|
||||||
|
- task: PublishPipelineArtifact@0
|
||||||
|
inputs:
|
||||||
|
artifactName: crash-dump-macos
|
||||||
|
targetPath: .build/crashes
|
||||||
|
displayName: 'Publish Crash Reports'
|
||||||
|
condition: succeededOrFailed()
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
set -e
|
set -e
|
||||||
|
APP_ROOT=$(agent.builddirectory)/VSCode-darwin
|
||||||
|
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||||
|
HELPER_APP_NAME="`echo $APP_NAME | sed -e 's/^Visual Studio //;s/\.app$//'`"
|
||||||
|
APP_FRAMEWORK_PATH="$APP_ROOT/$APP_NAME/Contents/Frameworks"
|
||||||
security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
|
security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
|
||||||
security default-keychain -s $(agent.tempdirectory)/buildagent.keychain
|
security default-keychain -s $(agent.tempdirectory)/buildagent.keychain
|
||||||
security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
|
security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain
|
||||||
echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12
|
echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12
|
||||||
security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign
|
security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign
|
||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain
|
||||||
codesign -s 99FM488X57 --deep --force --options runtime --entitlements build/azure-pipelines/darwin/entitlements.plist $(agent.builddirectory)/VSCode-darwin/*.app
|
codesign -s 99FM488X57 --deep --force --options runtime --entitlements build/azure-pipelines/darwin/entitlements.plist "$APP_ROOT"/*.app
|
||||||
|
codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-gpu-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (GPU).app"
|
||||||
|
codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-plugin-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (Plugin).app"
|
||||||
|
codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-renderer-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (Renderer).app"
|
||||||
displayName: Set Hardened Entitlements
|
displayName: Set Hardened Entitlements
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
|
|||||||
@@ -114,14 +114,14 @@ steps:
|
|||||||
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||||
yarn smoketest --build "$APP_ROOT/$APP_NAME" --screenshots "$(build.artifactstagingdirectory)/smokeshots"
|
yarn smoketest --build "$APP_ROOT/$APP_NAME" --screenshots "$(build.artifactstagingdirectory)/smokeshots"
|
||||||
displayName: Run smoke tests (Electron)
|
displayName: Run smoke tests (Electron)
|
||||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'))
|
||||||
|
|
||||||
- script: |
|
# - script: |
|
||||||
set -e
|
# set -e
|
||||||
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/azuredatastudio-reh-web-darwin" \
|
# VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/azuredatastudio-reh-web-darwin" \
|
||||||
yarn smoketest --web --headless --screenshots "$(build.artifactstagingdirectory)/smokeshots"
|
# yarn smoketest --web --headless --screenshots "$(build.artifactstagingdirectory)/smokeshots"
|
||||||
displayName: Run smoke tests (Browser)
|
# displayName: Run smoke tests (Browser)
|
||||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
# condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'))
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
set -e
|
set -e
|
||||||
|
|||||||
@@ -7,57 +7,80 @@ steps:
|
|||||||
sudo chmod +x /etc/init.d/xvfb
|
sudo chmod +x /etc/init.d/xvfb
|
||||||
sudo update-rc.d xvfb defaults
|
sudo update-rc.d xvfb defaults
|
||||||
sudo service xvfb start
|
sudo service xvfb start
|
||||||
|
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: "12.13.0"
|
versionSpec: "12.13.0"
|
||||||
|
|
||||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@3
|
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@3
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: "1.x"
|
versionSpec: "1.x"
|
||||||
|
|
||||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||||
inputs:
|
inputs:
|
||||||
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
||||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
||||||
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
CHILD_CONCURRENCY=1 yarn --frozen-lockfile
|
CHILD_CONCURRENCY=1 yarn --frozen-lockfile
|
||||||
displayName: Install Dependencies
|
displayName: Install Dependencies
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||||
|
|
||||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||||
inputs:
|
inputs:
|
||||||
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
||||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
||||||
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
yarn electron x64
|
yarn electron x64
|
||||||
displayName: Download Electron
|
displayName: Download Electron
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
yarn gulp hygiene
|
yarn gulp hygiene
|
||||||
displayName: Run Hygiene Checks
|
displayName: Run Hygiene Checks
|
||||||
|
|
||||||
- script: | # {{SQL CARBON EDIT}} add strict null check
|
- script: | # {{SQL CARBON EDIT}} add strict null check
|
||||||
yarn strict-vscode
|
yarn strict-vscode
|
||||||
|
|
||||||
displayName: Run Strict Null Check
|
displayName: Run Strict Null Check
|
||||||
# - script: | {{SQL CARBON EDIT}} remove monaco editor checks
|
# - script: | {{SQL CARBON EDIT}} remove monaco editor checks
|
||||||
# yarn monaco-compile-check
|
# yarn monaco-compile-check
|
||||||
# displayName: Run Monaco Editor Checks
|
# displayName: Run Monaco Editor Checks
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
yarn valid-layers-check
|
yarn valid-layers-check
|
||||||
displayName: Run Valid Layers Checks
|
displayName: Run Valid Layers Checks
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
yarn compile
|
yarn compile
|
||||||
displayName: Compile Sources
|
displayName: Compile Sources
|
||||||
|
|
||||||
# - script: | {{SQL CARBON EDIT}} remove step
|
# - script: | {{SQL CARBON EDIT}} remove step
|
||||||
# yarn download-builtin-extensions
|
# yarn download-builtin-extensions
|
||||||
# displayName: Download Built-in Extensions
|
# displayName: Download Built-in Extensions
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests"
|
DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests"
|
||||||
displayName: Run Unit Tests (Electron)
|
displayName: Run Unit Tests (Electron)
|
||||||
|
|
||||||
# - script: | {{SQL CARBON EDIT}} disable
|
# - script: | {{SQL CARBON EDIT}} disable
|
||||||
# DISPLAY=:10 yarn test-browser --browser chromium
|
# DISPLAY=:10 yarn test-browser --browser chromium
|
||||||
# displayName: Run Unit Tests (Browser)
|
# displayName: Run Unit Tests (Browser)
|
||||||
|
|
||||||
# - script: | {{SQL CARBON EDIT}} disable
|
# - script: | {{SQL CARBON EDIT}} disable
|
||||||
# DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests"
|
# DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests"
|
||||||
# displayName: Run Integration Tests (Electron)
|
# displayName: Run Integration Tests (Electron)
|
||||||
|
|
||||||
|
# - task: PublishPipelineArtifact@0
|
||||||
|
# inputs:
|
||||||
|
# artifactName: crash-dump-linux
|
||||||
|
# targetPath: .build/crashes
|
||||||
|
# displayName: 'Publish Crash Reports'
|
||||||
|
# condition: succeededOrFailed()
|
||||||
|
|
||||||
- task: PublishTestResults@2
|
- task: PublishTestResults@2
|
||||||
displayName: Publish Tests Results
|
displayName: Publish Tests Results
|
||||||
inputs:
|
inputs:
|
||||||
|
|||||||
@@ -140,6 +140,13 @@ steps:
|
|||||||
displayName: Run integration tests (Browser)
|
displayName: Run integration tests (Browser)
|
||||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||||
|
|
||||||
|
- task: PublishPipelineArtifact@0
|
||||||
|
inputs:
|
||||||
|
artifactName: crash-dump-linux
|
||||||
|
targetPath: .build/crashes
|
||||||
|
displayName: 'Publish Crash Reports'
|
||||||
|
condition: succeededOrFailed()
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
set -e
|
set -e
|
||||||
yarn gulp "vscode-linux-x64-build-deb"
|
yarn gulp "vscode-linux-x64-build-deb"
|
||||||
|
|||||||
@@ -16,12 +16,10 @@ steps:
|
|||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: "10.15.1"
|
versionSpec: "10.15.1"
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'))
|
|
||||||
|
|
||||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@3
|
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@3
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: "1.x"
|
versionSpec: "1.x"
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'))
|
|
||||||
|
|
||||||
- task: AzureKeyVault@1
|
- task: AzureKeyVault@1
|
||||||
displayName: 'Azure Key Vault: Get Secrets'
|
displayName: 'Azure Key Vault: Get Secrets'
|
||||||
@@ -56,26 +54,25 @@ steps:
|
|||||||
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
||||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
||||||
vstsFeed: 'BuildCache'
|
vstsFeed: 'BuildCache'
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'))
|
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
set -e
|
set -e
|
||||||
CHILD_CONCURRENCY=1 yarn --frozen-lockfile
|
CHILD_CONCURRENCY=1 yarn --frozen-lockfile
|
||||||
displayName: Install dependencies
|
displayName: Install dependencies
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), ne(variables['CacheRestored'], 'true'))
|
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||||
|
|
||||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||||
inputs:
|
inputs:
|
||||||
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
||||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
||||||
vstsFeed: 'BuildCache'
|
vstsFeed: 'BuildCache'
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), ne(variables['CacheRestored'], 'true'))
|
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
set -e
|
set -e
|
||||||
yarn postinstall
|
yarn postinstall
|
||||||
displayName: Run postinstall scripts
|
displayName: Run postinstall scripts
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), eq(variables['CacheRestored'], 'true'))
|
condition: and(succeeded(), eq(variables['CacheRestored'], 'true'))
|
||||||
|
|
||||||
# Mixin must run before optimize, because the CSS loader will
|
# Mixin must run before optimize, because the CSS loader will
|
||||||
# inline small SVGs
|
# inline small SVGs
|
||||||
@@ -113,7 +110,6 @@ steps:
|
|||||||
|
|
||||||
node build/azure-pipelines/common/copyArtifacts.js
|
node build/azure-pipelines/common/copyArtifacts.js
|
||||||
displayName: Write Version Information
|
displayName: Write Version Information
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'))
|
|
||||||
|
|
||||||
- task: PublishBuildArtifacts@1
|
- task: PublishBuildArtifacts@1
|
||||||
displayName: 'Publish Artifact: drop'
|
displayName: 'Publish Artifact: drop'
|
||||||
|
|||||||
@@ -2,59 +2,83 @@ steps:
|
|||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: "12.13.0"
|
versionSpec: "12.13.0"
|
||||||
|
|
||||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@3 # {{SQL CARBON EDIT}} update version
|
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@3 # {{SQL CARBON EDIT}} update version
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: "1.x"
|
versionSpec: "1.x"
|
||||||
|
|
||||||
- task: UsePythonVersion@0
|
- task: UsePythonVersion@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '2.x'
|
versionSpec: '2.x'
|
||||||
addToPath: true
|
addToPath: true
|
||||||
|
|
||||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1
|
||||||
inputs:
|
inputs:
|
||||||
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
||||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
||||||
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
||||||
|
|
||||||
- powershell: |
|
- powershell: |
|
||||||
yarn --frozen-lockfile
|
yarn --frozen-lockfile
|
||||||
env:
|
env:
|
||||||
CHILD_CONCURRENCY: "1"
|
CHILD_CONCURRENCY: "1"
|
||||||
displayName: Install Dependencies
|
displayName: Install Dependencies
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||||
|
|
||||||
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1
|
||||||
inputs:
|
inputs:
|
||||||
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock, !samples/**/yarn.lock'
|
||||||
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
targetfolder: '**/node_modules, !**/node_modules/**/node_modules, !samples/**/node_modules'
|
||||||
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
vstsFeed: 'npm-cache' # {{SQL CARBON EDIT}} update build cache
|
||||||
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
condition: and(succeeded(), ne(variables['CacheRestored'], 'true'))
|
||||||
|
|
||||||
- powershell: |
|
- powershell: |
|
||||||
yarn electron
|
yarn electron
|
||||||
|
displayName: Download Electron
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
yarn gulp hygiene
|
yarn gulp hygiene
|
||||||
displayName: Run Hygiene Checks
|
displayName: Run Hygiene Checks
|
||||||
|
|
||||||
- script: | # {{SQL CARBON EDIT}} add step
|
- script: | # {{SQL CARBON EDIT}} add step
|
||||||
yarn strict-vscode
|
yarn strict-vscode
|
||||||
displayName: Run Strict Null Check
|
displayName: Run Strict Null Check
|
||||||
|
|
||||||
# - powershell: | {{SQL CARBON EDIT}} remove step
|
# - powershell: | {{SQL CARBON EDIT}} remove step
|
||||||
# yarn monaco-compile-check
|
# yarn monaco-compile-check
|
||||||
# displayName: Run Monaco Editor Checks
|
# displayName: Run Monaco Editor Checks
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
yarn valid-layers-check
|
yarn valid-layers-check
|
||||||
displayName: Run Valid Layers Checks
|
displayName: Run Valid Layers Checks
|
||||||
|
|
||||||
- powershell: |
|
- powershell: |
|
||||||
yarn compile
|
yarn compile
|
||||||
displayName: Compile Sources
|
displayName: Compile Sources
|
||||||
|
|
||||||
# - powershell: | {{SQL CARBON EDIT}} remove step
|
# - powershell: | {{SQL CARBON EDIT}} remove step
|
||||||
# yarn download-builtin-extensions
|
# yarn download-builtin-extensions
|
||||||
# displayName: Download Built-in Extensions
|
# displayName: Download Built-in Extensions
|
||||||
|
|
||||||
- powershell: |
|
- powershell: |
|
||||||
.\scripts\test.bat --tfs "Unit Tests"
|
.\scripts\test.bat --tfs "Unit Tests"
|
||||||
displayName: Run Unit Tests (Electron)
|
displayName: Run Unit Tests (Electron)
|
||||||
|
|
||||||
# - powershell: | {{SQL CARBON EDIT}} disable
|
# - powershell: | {{SQL CARBON EDIT}} disable
|
||||||
# yarn test-browser --browser chromium --browser firefox
|
# yarn test-browser --browser chromium --browser firefox
|
||||||
# displayName: Run Unit Tests (Browser)
|
# displayName: Run Unit Tests (Browser)
|
||||||
|
|
||||||
# - powershell: | {{SQL CARBON EDIT}} disable
|
# - powershell: | {{SQL CARBON EDIT}} disable
|
||||||
# .\scripts\test-integration.bat --tfs "Integration Tests"
|
# .\scripts\test-integration.bat --tfs "Integration Tests"
|
||||||
# displayName: Run Integration Tests (Electron)
|
# displayName: Run Integration Tests (Electron)
|
||||||
|
|
||||||
|
# - task: PublishPipelineArtifact@0
|
||||||
|
# displayName: 'Publish Crash Reports'
|
||||||
|
# inputs:
|
||||||
|
# artifactName: crash-dump-windows
|
||||||
|
# targetPath: .build\crashes
|
||||||
|
# condition: succeededOrFailed()
|
||||||
|
|
||||||
- task: PublishTestResults@2
|
- task: PublishTestResults@2
|
||||||
displayName: Publish Tests Results
|
displayName: Publish Tests Results
|
||||||
inputs:
|
inputs:
|
||||||
|
|||||||
@@ -149,6 +149,13 @@ steps:
|
|||||||
displayName: Run integration tests (Browser)
|
displayName: Run integration tests (Browser)
|
||||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
||||||
|
|
||||||
|
- task: PublishPipelineArtifact@0
|
||||||
|
inputs:
|
||||||
|
artifactName: crash-dump-windows-$(VSCODE_ARCH)
|
||||||
|
targetPath: .build\crashes
|
||||||
|
displayName: 'Publish Crash Reports'
|
||||||
|
condition: succeededOrFailed()
|
||||||
|
|
||||||
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
|
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
|
||||||
inputs:
|
inputs:
|
||||||
ConnectedServiceName: 'ESRP CodeSign'
|
ConnectedServiceName: 'ESRP CodeSign'
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ function prepareDebPackage(arch) {
|
|||||||
.pipe(replace('@@NAME_SHORT@@', product.nameShort))
|
.pipe(replace('@@NAME_SHORT@@', product.nameShort))
|
||||||
.pipe(replace('@@NAME@@', product.applicationName))
|
.pipe(replace('@@NAME@@', product.applicationName))
|
||||||
.pipe(replace('@@EXEC@@', `/usr/share/${product.applicationName}/${product.applicationName}`))
|
.pipe(replace('@@EXEC@@', `/usr/share/${product.applicationName}/${product.applicationName}`))
|
||||||
.pipe(replace('@@ICON@@', `/usr/share/pixmaps/${product.linuxIconName}.png`))
|
.pipe(replace('@@ICON@@', product.linuxIconName))
|
||||||
.pipe(replace('@@URLPROTOCOL@@', product.urlProtocol));
|
.pipe(replace('@@URLPROTOCOL@@', product.urlProtocol));
|
||||||
|
|
||||||
const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' })
|
const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' })
|
||||||
|
|||||||
@@ -430,6 +430,7 @@ function markNodes(languageService, options) {
|
|||||||
|| ts.isIndexSignatureDeclaration(member)
|
|| ts.isIndexSignatureDeclaration(member)
|
||||||
|| ts.isCallSignatureDeclaration(member)
|
|| ts.isCallSignatureDeclaration(member)
|
||||||
|| memberName === '[Symbol.iterator]'
|
|| memberName === '[Symbol.iterator]'
|
||||||
|
|| memberName === '[Symbol.toStringTag]'
|
||||||
|| memberName === 'toJSON'
|
|| memberName === 'toJSON'
|
||||||
|| memberName === 'toString'
|
|| memberName === 'toString'
|
||||||
|| memberName === 'dispose' // TODO: keeping all `dispose` methods
|
|| memberName === 'dispose' // TODO: keeping all `dispose` methods
|
||||||
|
|||||||
@@ -548,6 +548,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt
|
|||||||
|| ts.isIndexSignatureDeclaration(member)
|
|| ts.isIndexSignatureDeclaration(member)
|
||||||
|| ts.isCallSignatureDeclaration(member)
|
|| ts.isCallSignatureDeclaration(member)
|
||||||
|| memberName === '[Symbol.iterator]'
|
|| memberName === '[Symbol.iterator]'
|
||||||
|
|| memberName === '[Symbol.toStringTag]'
|
||||||
|| memberName === 'toJSON'
|
|| memberName === 'toJSON'
|
||||||
|| memberName === 'toString'
|
|| memberName === 'toString'
|
||||||
|| memberName === 'dispose'// TODO: keeping all `dispose` methods
|
|| memberName === 'dispose'// TODO: keeping all `dispose` methods
|
||||||
@@ -795,8 +796,8 @@ function getRealNodeSymbol(checker: ts.TypeChecker, node: ts.Node): [ts.Symbol |
|
|||||||
|
|
||||||
let symbol = (
|
let symbol = (
|
||||||
ts.isShorthandPropertyAssignment(node)
|
ts.isShorthandPropertyAssignment(node)
|
||||||
? checker.getShorthandAssignmentValueSymbol(node)
|
? checker.getShorthandAssignmentValueSymbol(node)
|
||||||
: checker.getSymbolAtLocation(node)
|
: checker.getSymbolAtLocation(node)
|
||||||
);
|
);
|
||||||
|
|
||||||
let importNode: ts.Declaration | null = null;
|
let importNode: ts.Declaration | null = null;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
"rollup-plugin-commonjs": "^10.1.0",
|
"rollup-plugin-commonjs": "^10.1.0",
|
||||||
"rollup-plugin-node-resolve": "^5.2.0",
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
"terser": "4.3.8",
|
"terser": "4.3.8",
|
||||||
"typescript": "^3.9.0-dev.20200427",
|
"typescript": "^3.9.1-rc",
|
||||||
"vsce": "1.48.0",
|
"vsce": "1.48.0",
|
||||||
"vscode-telemetry-extractor": "^1.5.4",
|
"vscode-telemetry-extractor": "^1.5.4",
|
||||||
"xml2js": "^0.4.17"
|
"xml2js": "^0.4.17"
|
||||||
|
|||||||
@@ -3462,10 +3462,10 @@ typescript@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
|
||||||
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
|
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
|
||||||
|
|
||||||
typescript@^3.9.0-dev.20200427:
|
typescript@^3.9.1-rc:
|
||||||
version "3.9.0-dev.20200427"
|
version "3.9.1-rc"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.0-dev.20200427.tgz#e42d606d938575dfb7b0b66f04a31b5f0eb0be57"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.1-rc.tgz#81d5a5a0a597e224b6e2af8dffb46524b2eaf5f3"
|
||||||
integrity sha512-ja/GhL7BHT+VQZiLoYMGJt2CP1Pdr0EhYefv4LLw4tVooSuCDB8SDKT/i/HwsoPgQ4ZaYfg1vPl+1RhiO3bwJg==
|
integrity sha512-+cPv8L2Vd4KidCotqi2wjegBZ5n47CDRUu/QiLVu2YbeXAz78hIfcai9ziBiNI6JTGTVwUqXRug2UZxDcxhvFw==
|
||||||
|
|
||||||
typical@^4.0.0:
|
typical@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
|
|||||||
@@ -60,12 +60,12 @@
|
|||||||
"git": {
|
"git": {
|
||||||
"name": "electron",
|
"name": "electron",
|
||||||
"repositoryUrl": "https://github.com/electron/electron",
|
"repositoryUrl": "https://github.com/electron/electron",
|
||||||
"commitHash": "959e80cc53cbebf8eb1d62eb2d14fa8fd86b0394"
|
"commitHash": "0552e0d5de46ffa3b481d741f1db5c779e201565"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"isOnlyProductionDependency": true,
|
"isOnlyProductionDependency": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "7.2.2"
|
"version": "7.2.4"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"component": {
|
"component": {
|
||||||
|
|||||||
@@ -325,6 +325,7 @@ export abstract class AzureAuth implements vscode.Disposable {
|
|||||||
return tenants;
|
return tenants;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
console.log(ex);
|
console.log(ex);
|
||||||
|
console.log(JSON.stringify(ex?.response?.data, undefined, 2));
|
||||||
throw new Error('Error retreiving tenant information');
|
throw new Error('Error retreiving tenant information');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { WorkspaceConfiguration, ConfigurationTarget } from 'vscode';
|
|
||||||
import { Account } from 'azdata';
|
import { Account } from 'azdata';
|
||||||
|
|
||||||
import { azureResource } from '../azure-resource';
|
import { azureResource } from '../azure-resource';
|
||||||
@@ -53,25 +52,8 @@ export class AzureResourceSubscriptionFilterService implements IAzureResourceSub
|
|||||||
for (const accountId in selectedSubscriptionsCache) {
|
for (const accountId in selectedSubscriptionsCache) {
|
||||||
filters.push(...selectedSubscriptionsCache[accountId].map((subcription) => `${accountId}/${subcription.id}/${subcription.name}`));
|
filters.push(...selectedSubscriptionsCache[accountId].map((subcription) => `${accountId}/${subcription.id}/${subcription.name}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
const resourceFilterConfig = this._config.inspect<string[]>(AzureResourceSubscriptionFilterService.filterConfigName);
|
|
||||||
let configTarget = ConfigurationTarget.Global;
|
|
||||||
if (resourceFilterConfig) {
|
|
||||||
if (resourceFilterConfig.workspaceFolderValue) {
|
|
||||||
configTarget = ConfigurationTarget.WorkspaceFolder;
|
|
||||||
} else if (resourceFilterConfig.workspaceValue) {
|
|
||||||
configTarget = ConfigurationTarget.Workspace;
|
|
||||||
} else if (resourceFilterConfig.globalValue) {
|
|
||||||
configTarget = ConfigurationTarget.Global;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await this._config.update(AzureResourceSubscriptionFilterService.filterConfigName, filters, configTarget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _config: WorkspaceConfiguration = undefined;
|
|
||||||
private _cacheService: IAzureResourceCacheService = undefined;
|
private _cacheService: IAzureResourceCacheService = undefined;
|
||||||
private _cacheKey: string = undefined;
|
private _cacheKey: string = undefined;
|
||||||
|
|
||||||
private static readonly filterConfigName = 'azure.resource.config.filter';
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ export class IconPathHelper {
|
|||||||
export namespace cssStyles {
|
export namespace cssStyles {
|
||||||
export const title = { 'font-size': '14px', 'font-weight': '600' };
|
export const title = { 'font-size': '14px', 'font-weight': '600' };
|
||||||
export const tableHeader = { 'text-align': 'left', 'font-weight': 'bold', 'text-transform': 'uppercase', 'font-size': '10px', 'user-select': 'text' };
|
export const tableHeader = { 'text-align': 'left', 'font-weight': 'bold', 'text-transform': 'uppercase', 'font-size': '10px', 'user-select': 'text' };
|
||||||
export const hyperlink = { 'user-select': 'text', 'color': '#0078d4', 'text-decoration': 'underline', 'cursor': 'pointer' };
|
|
||||||
export const text = { 'margin-block-start': '0px', 'margin-block-end': '0px' };
|
export const text = { 'margin-block-start': '0px', 'margin-block-end': '0px' };
|
||||||
export const overflowEllipsisText = { ...text, 'overflow': 'hidden', 'text-overflow': 'ellipsis' };
|
export const overflowEllipsisText = { ...text, 'overflow': 'hidden', 'text-overflow': 'ellipsis' };
|
||||||
export const nonSelectableText = { ...cssStyles.text, 'user-select': 'none' };
|
export const nonSelectableText = { ...cssStyles.text, 'user-select': 'none' };
|
||||||
|
|||||||
@@ -363,7 +363,7 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage {
|
|||||||
.withProperties<azdata.HyperlinkComponentProperties>({
|
.withProperties<azdata.HyperlinkComponentProperties>({
|
||||||
label: getServiceNameDisplayText(serviceStatus.serviceName),
|
label: getServiceNameDisplayText(serviceStatus.serviceName),
|
||||||
url: '',
|
url: '',
|
||||||
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
|
CSSStyles: { ...cssStyles.text }
|
||||||
}).component();
|
}).component();
|
||||||
nameCell.onDidClick(() => {
|
nameCell.onDidClick(() => {
|
||||||
this.dashboard.switchToServiceTab(serviceStatus.serviceName);
|
this.dashboard.switchToServiceTab(serviceStatus.serviceName);
|
||||||
@@ -458,7 +458,7 @@ function createEndpointComponent(modelBuilder: azdata.ModelBuilder, endpoint: En
|
|||||||
.withProperties<azdata.HyperlinkComponentProperties>({
|
.withProperties<azdata.HyperlinkComponentProperties>({
|
||||||
label: endpoint.endpoint,
|
label: endpoint.endpoint,
|
||||||
title: endpoint.endpoint,
|
title: endpoint.endpoint,
|
||||||
url: endpoint.endpoint, CSSStyles: { ...cssStyles.hyperlink }
|
url: endpoint.endpoint
|
||||||
})
|
})
|
||||||
.component();
|
.component();
|
||||||
}
|
}
|
||||||
@@ -468,7 +468,7 @@ function createEndpointComponent(modelBuilder: azdata.ModelBuilder, endpoint: En
|
|||||||
title: endpoint.endpoint,
|
title: endpoint.endpoint,
|
||||||
label: endpoint.endpoint,
|
label: endpoint.endpoint,
|
||||||
url: '',
|
url: '',
|
||||||
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
|
CSSStyles: { ...cssStyles.text }
|
||||||
}).component();
|
}).component();
|
||||||
endpointCell.onDidClick(async () => {
|
endpointCell.onDidClick(async () => {
|
||||||
const connProfile = bdcModel.getSqlServerMasterConnectionProfile();
|
const connProfile = bdcModel.getSqlServerMasterConnectionProfile();
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ export class BdcDashboardResourceStatusPage extends BdcDashboardPage {
|
|||||||
url: instanceStatus.dashboards.nodeMetricsUrl,
|
url: instanceStatus.dashboards.nodeMetricsUrl,
|
||||||
title: instanceStatus.dashboards.nodeMetricsUrl,
|
title: instanceStatus.dashboards.nodeMetricsUrl,
|
||||||
ariaLabel: loc.viewNodeMetrics(instanceStatus.dashboards.nodeMetricsUrl),
|
ariaLabel: loc.viewNodeMetrics(instanceStatus.dashboards.nodeMetricsUrl),
|
||||||
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
|
CSSStyles: { ...cssStyles.text }
|
||||||
}).component());
|
}).component());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ export class BdcDashboardResourceStatusPage extends BdcDashboardPage {
|
|||||||
url: instanceStatus.dashboards.sqlMetricsUrl,
|
url: instanceStatus.dashboards.sqlMetricsUrl,
|
||||||
title: instanceStatus.dashboards.sqlMetricsUrl,
|
title: instanceStatus.dashboards.sqlMetricsUrl,
|
||||||
ariaLabel: loc.viewSqlMetrics(instanceStatus.dashboards.sqlMetricsUrl),
|
ariaLabel: loc.viewSqlMetrics(instanceStatus.dashboards.sqlMetricsUrl),
|
||||||
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
|
CSSStyles: { ...cssStyles.text }
|
||||||
}).component());
|
}).component());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,7 +332,7 @@ export class BdcDashboardResourceStatusPage extends BdcDashboardPage {
|
|||||||
url: instanceStatus.dashboards.logsUrl,
|
url: instanceStatus.dashboards.logsUrl,
|
||||||
title: instanceStatus.dashboards.logsUrl,
|
title: instanceStatus.dashboards.logsUrl,
|
||||||
ariaLabel: loc.viewLogs(instanceStatus.dashboards.logsUrl),
|
ariaLabel: loc.viewLogs(instanceStatus.dashboards.logsUrl),
|
||||||
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
|
CSSStyles: { ...cssStyles.text }
|
||||||
}).component());
|
}).component());
|
||||||
}
|
}
|
||||||
return row;
|
return row;
|
||||||
|
|||||||
@@ -282,33 +282,42 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"allOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"oneOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
"allOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"oneOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/dockerfileContainer"
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/dockerfileContainer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/imageContainer"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/imageContainer"
|
"$ref": "#/definitions/nonComposeBase"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/nonComposeBase"
|
"$ref": "#/definitions/composeContainer"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/composeContainer"
|
"$ref": "#/definitions/devContainerCommon"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/devContainerCommon"
|
"type": "object",
|
||||||
|
"$ref": "#/definitions/devContainerCommon",
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
|
|
||||||
export default abstract class ControllerBase implements vscode.Disposable {
|
|
||||||
protected _context: vscode.ExtensionContext;
|
|
||||||
|
|
||||||
protected constructor(context: vscode.ExtensionContext) {
|
|
||||||
this._context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get extensionContext(): vscode.ExtensionContext {
|
|
||||||
return this._context;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract activate(): Promise<boolean>;
|
|
||||||
|
|
||||||
abstract deactivate(): void;
|
|
||||||
|
|
||||||
public dispose(): void {
|
|
||||||
this.deactivate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,20 +4,17 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import ControllerBase from './controllerBase';
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { DataTierApplicationWizard } from '../wizard/dataTierApplicationWizard';
|
import { DataTierApplicationWizard } from '../wizard/dataTierApplicationWizard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main controller class that initializes the extension
|
* The main controller class that initializes the extension
|
||||||
*/
|
*/
|
||||||
export default class MainController extends ControllerBase {
|
export default class MainController implements vscode.Disposable {
|
||||||
|
|
||||||
public constructor(context: vscode.ExtensionContext) {
|
public constructor(private context: vscode.ExtensionContext) {
|
||||||
super(context);
|
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
*/
|
|
||||||
public deactivate(): void {
|
public deactivate(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,4 +26,12 @@ export default class MainController extends ControllerBase {
|
|||||||
private initializeDacFxWizard() {
|
private initializeDacFxWizard() {
|
||||||
azdata.tasks.registerTask('dacFx.start', (profile: azdata.IConnectionProfile, ...args: any[]) => new DataTierApplicationWizard().start(profile, args));
|
azdata.tasks.registerTask('dacFx.start', (profile: azdata.IConnectionProfile, ...args: any[]) => new DataTierApplicationWizard().start(profile, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get extensionContext(): vscode.ExtensionContext {
|
||||||
|
return this.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose(): void {
|
||||||
|
this.deactivate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,9 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
import ControllerBase from './controllers/controllerBase';
|
|
||||||
import MainController from './controllers/mainController';
|
import MainController from './controllers/mainController';
|
||||||
|
|
||||||
let controllers: ControllerBase[] = [];
|
let controllers: MainController[] = [];
|
||||||
|
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
let activations: Promise<boolean>[] = [];
|
let activations: Promise<boolean>[] = [];
|
||||||
|
|||||||
27
extensions/dacpac/src/test/mainController.test.ts
Normal file
27
extensions/dacpac/src/test/mainController.test.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 MainController from '../controllers/mainController';
|
||||||
|
import { TestContext, createContext } from './testContext';
|
||||||
|
|
||||||
|
let testContext: TestContext;
|
||||||
|
|
||||||
|
function createController(): MainController {
|
||||||
|
let controller = new MainController(testContext.context);
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('MainController', function (): void {
|
||||||
|
before(async function (): Promise<void> {
|
||||||
|
testContext = createContext();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should create new instance successfully', async function (): Promise<void> {
|
||||||
|
let controller: MainController;
|
||||||
|
should.doesNotThrow(() => controller = createController());
|
||||||
|
should.notEqual(controller.extensionContext, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
36
extensions/dacpac/src/test/testContext.ts
Normal file
36
extensions/dacpac/src/test/testContext.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
export interface TestContext {
|
||||||
|
context: vscode.ExtensionContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createContext(): TestContext {
|
||||||
|
let extensionPath = path.join(__dirname, '..', '..');
|
||||||
|
|
||||||
|
return {
|
||||||
|
context: {
|
||||||
|
subscriptions: [],
|
||||||
|
workspaceState: {
|
||||||
|
get: () => { return Promise.resolve(); },
|
||||||
|
update: () => { return Promise.resolve(); }
|
||||||
|
},
|
||||||
|
globalState: {
|
||||||
|
get: () => { return Promise.resolve(); },
|
||||||
|
update: () => { return Promise.resolve(); }
|
||||||
|
},
|
||||||
|
extensionPath: extensionPath,
|
||||||
|
asAbsolutePath: () => { return ''; },
|
||||||
|
storagePath: '',
|
||||||
|
globalStoragePath: '',
|
||||||
|
logPath: '',
|
||||||
|
extensionUri: vscode.Uri.parse(''),
|
||||||
|
environmentVariableCollection: undefined as any
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -7,10 +7,16 @@ import 'mocha';
|
|||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
import { DataTierApplicationWizard, Operation } from '../wizard/dataTierApplicationWizard';
|
import { DataTierApplicationWizard, Operation } from '../wizard/dataTierApplicationWizard';
|
||||||
|
import { DacFxDataModel } from '../wizard/api/models';
|
||||||
|
|
||||||
|
let wizard: DataTierApplicationWizard;
|
||||||
describe('Dacfx wizard', function (): void {
|
describe('Dacfx wizard', function (): void {
|
||||||
|
beforeEach(async function (): Promise<void> {
|
||||||
|
wizard = new DataTierApplicationWizard();
|
||||||
|
wizard.model = <DacFxDataModel>{};
|
||||||
|
});
|
||||||
|
|
||||||
it('Should initialize wizard correctly', async () => {
|
it('Should initialize wizard correctly', async () => {
|
||||||
let wizard = new DataTierApplicationWizard();
|
|
||||||
should.notEqual(wizard.wizard, undefined);
|
should.notEqual(wizard.wizard, undefined);
|
||||||
should.equal(wizard.wizard.title, loc.wizardTitle);
|
should.equal(wizard.wizard.title, loc.wizardTitle);
|
||||||
|
|
||||||
@@ -18,8 +24,50 @@ describe('Dacfx wizard', function (): void {
|
|||||||
should.notEqual(wizard.pages, undefined);
|
should.notEqual(wizard.pages, undefined);
|
||||||
should.equal(wizard.pages.size, 7);
|
should.equal(wizard.pages.size, 7);
|
||||||
should.equal(wizard.wizard.pages.length, 4);
|
should.equal(wizard.wizard.pages.length, 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should determine summary page correctly', async () => {
|
||||||
|
// summary page should be 2 for deploy
|
||||||
|
wizard.selectedOperation = Operation.deploy;
|
||||||
|
wizard.model.upgradeExisting = false;
|
||||||
|
should.equal(wizard.isSummaryPage(2), true);
|
||||||
|
|
||||||
|
// summary page should be 3 for deploy - upgrade existing db
|
||||||
|
wizard.selectedOperation = Operation.deploy;
|
||||||
|
wizard.model.upgradeExisting = true;
|
||||||
|
should.equal(wizard.isSummaryPage(3), true);
|
||||||
|
|
||||||
|
// summary page should be 3 for generate deploy script
|
||||||
|
wizard.selectedOperation = Operation.generateDeployScript;
|
||||||
|
should.equal(wizard.isSummaryPage(3), true);
|
||||||
|
|
||||||
|
// summary page should be 2 for import
|
||||||
|
wizard.selectedOperation = Operation.import;
|
||||||
|
should.equal(wizard.isSummaryPage(2), true);
|
||||||
|
|
||||||
|
// summary page should be 2 for export
|
||||||
|
wizard.selectedOperation = Operation.export;
|
||||||
|
should.equal(wizard.isSummaryPage(2), true);
|
||||||
|
|
||||||
|
// summary page should be 2 for extract
|
||||||
|
wizard.selectedOperation = Operation.extract;
|
||||||
|
should.equal(wizard.isSummaryPage(2), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should set Done button and operation correctly', async () => {
|
||||||
wizard.setDoneButton(Operation.deploy);
|
wizard.setDoneButton(Operation.deploy);
|
||||||
should.equal(wizard.selectedOperation, Operation.deploy);
|
should.equal(wizard.selectedOperation, Operation.deploy);
|
||||||
|
|
||||||
|
wizard.setDoneButton(Operation.generateDeployScript);
|
||||||
|
should.equal(wizard.selectedOperation, Operation.generateDeployScript);
|
||||||
|
|
||||||
|
wizard.setDoneButton(Operation.extract);
|
||||||
|
should.equal(wizard.selectedOperation, Operation.extract);
|
||||||
|
|
||||||
|
wizard.setDoneButton(Operation.import);
|
||||||
|
should.equal(wizard.selectedOperation, Operation.import);
|
||||||
|
|
||||||
|
wizard.setDoneButton(Operation.export);
|
||||||
|
should.equal(wizard.selectedOperation, Operation.export);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export enum PageName {
|
|||||||
export class DataTierApplicationWizard {
|
export class DataTierApplicationWizard {
|
||||||
public wizard: azdata.window.Wizard;
|
public wizard: azdata.window.Wizard;
|
||||||
private connection: azdata.connection.ConnectionProfile;
|
private connection: azdata.connection.ConnectionProfile;
|
||||||
private model: DacFxDataModel;
|
public model: DacFxDataModel;
|
||||||
public pages: Map<string, Page> = new Map<string, Page>();
|
public pages: Map<string, Page> = new Map<string, Page>();
|
||||||
public selectedOperation: Operation;
|
public selectedOperation: Operation;
|
||||||
|
|
||||||
@@ -331,7 +331,7 @@ export class DataTierApplicationWizard {
|
|||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
private isSummaryPage(idx: number): boolean {
|
public isSummaryPage(idx: number): boolean {
|
||||||
return this.selectedOperation === Operation.import && idx === ImportOperationPath.summary
|
return this.selectedOperation === Operation.import && idx === ImportOperationPath.summary
|
||||||
|| this.selectedOperation === Operation.export && idx === ExportOperationPath.summary
|
|| this.selectedOperation === Operation.export && idx === ExportOperationPath.summary
|
||||||
|| this.selectedOperation === Operation.extract && idx === ExtractOperationPath.summary
|
|| this.selectedOperation === Operation.extract && idx === ExtractOperationPath.summary
|
||||||
|
|||||||
@@ -409,6 +409,11 @@
|
|||||||
"command": "git.timeline.copyCommitMessage",
|
"command": "git.timeline.copyCommitMessage",
|
||||||
"title": "%command.timelineCopyCommitMessage%",
|
"title": "%command.timelineCopyCommitMessage%",
|
||||||
"category": "Git"
|
"category": "Git"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "git.rebaseAbort",
|
||||||
|
"title": "%command.rebaseAbort%",
|
||||||
|
"category": "Git"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"keybindings": [
|
"keybindings": [
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
"command.showOutput": "Show Git Output",
|
"command.showOutput": "Show Git Output",
|
||||||
"command.ignore": "Add to .gitignore",
|
"command.ignore": "Add to .gitignore",
|
||||||
"command.revealInExplorer": "Reveal in Side Bar",
|
"command.revealInExplorer": "Reveal in Side Bar",
|
||||||
|
"command.rebaseAbort": "Abort Rebase",
|
||||||
"command.stashIncludeUntracked": "Stash (Include Untracked)",
|
"command.stashIncludeUntracked": "Stash (Include Untracked)",
|
||||||
"command.stash": "Stash",
|
"command.stash": "Stash",
|
||||||
"command.stashPop": "Pop Stash...",
|
"command.stashPop": "Pop Stash...",
|
||||||
|
|||||||
@@ -2494,6 +2494,10 @@ export class CommandCenter {
|
|||||||
env.clipboard.writeText(item.message);
|
env.clipboard.writeText(item.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@command('git.rebaseAbort', { repository: true })
|
||||||
|
async rebaseAbort(repository: Repository): Promise<void> {
|
||||||
|
await repository.rebaseAbort();
|
||||||
|
}
|
||||||
|
|
||||||
private createCommand(id: string, key: string, method: Function, options: CommandOptions): (...args: any[]) => any {
|
private createCommand(id: string, key: string, method: Function, options: CommandOptions): (...args: any[]) => any {
|
||||||
const result = (...args: any[]) => {
|
const result = (...args: any[]) => {
|
||||||
|
|||||||
@@ -1333,6 +1333,10 @@ export class Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async rebaseAbort(): Promise<void> {
|
||||||
|
await this.run(['rebase', '--abort']);
|
||||||
|
}
|
||||||
|
|
||||||
async rebaseContinue(): Promise<void> {
|
async rebaseContinue(): Promise<void> {
|
||||||
const args = ['rebase', '--continue'];
|
const args = ['rebase', '--continue'];
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
import { ExtensionContext, workspace, window, Disposable, commands, OutputChannel } from 'vscode'; // {{SQL CARBON EDIT}} - remove unused imports
|
import { ExtensionContext, workspace, window, Disposable, commands, OutputChannel, Uri } from 'vscode';
|
||||||
import { findGit, Git, IGit } from './git';
|
import { findGit, Git, IGit } from './git';
|
||||||
import { Model } from './model';
|
import { Model } from './model';
|
||||||
import { CommandCenter } from './commands';
|
import { CommandCenter } from './commands';
|
||||||
@@ -78,7 +78,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
|
|||||||
new GitTimelineProvider(model)
|
new GitTimelineProvider(model)
|
||||||
);
|
);
|
||||||
|
|
||||||
await checkGitVersion(info);
|
// await checkGitVersion(info); {{SQL CARBON EDIT}} Don't check git version
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
@@ -180,13 +180,8 @@ export async function activate(context: ExtensionContext): Promise<GitExtension>
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkGitVersion(_info: IGit): Promise<void> { // {{SQL CARBON EDIT}} - Rename info to _info to prevent error due to unused variable
|
// @ts-expect-error
|
||||||
return; /* {{SQL CARBON EDIT}} return immediately
|
async function checkGitVersion(info: IGit): Promise<void> {
|
||||||
|
|
||||||
/*const config = workspace.getConfiguration('git');
|
|
||||||
const shouldIgnore = config.get<boolean>('ignoreLegacyWarning') === true;
|
|
||||||
|
|
||||||
|
|
||||||
const config = workspace.getConfiguration('git');
|
const config = workspace.getConfiguration('git');
|
||||||
const shouldIgnore = config.get<boolean>('ignoreLegacyWarning') === true;
|
const shouldIgnore = config.get<boolean>('ignoreLegacyWarning') === true;
|
||||||
|
|
||||||
@@ -211,5 +206,5 @@ async function checkGitVersion(_info: IGit): Promise<void> { // {{SQL CARBON EDI
|
|||||||
commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/'));
|
commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/'));
|
||||||
} else if (choice === neverShowAgain) {
|
} else if (choice === neverShowAgain) {
|
||||||
await config.update('ignoreLegacyWarning', true, true);
|
await config.update('ignoreLegacyWarning', true, true);
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,6 +303,7 @@ export const enum Operation {
|
|||||||
CheckIgnore = 'CheckIgnore',
|
CheckIgnore = 'CheckIgnore',
|
||||||
GetObjectDetails = 'GetObjectDetails',
|
GetObjectDetails = 'GetObjectDetails',
|
||||||
SubmoduleUpdate = 'SubmoduleUpdate',
|
SubmoduleUpdate = 'SubmoduleUpdate',
|
||||||
|
RebaseAbort = 'RebaseAbort',
|
||||||
RebaseContinue = 'RebaseContinue',
|
RebaseContinue = 'RebaseContinue',
|
||||||
FindTrackingBranches = 'GetTracking',
|
FindTrackingBranches = 'GetTracking',
|
||||||
Apply = 'Apply',
|
Apply = 'Apply',
|
||||||
@@ -1331,6 +1332,10 @@ export class Repository implements Disposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async rebaseAbort(): Promise<void> {
|
||||||
|
await this.run(Operation.RebaseAbort, async () => await this.repository.rebaseAbort());
|
||||||
|
}
|
||||||
|
|
||||||
checkIgnore(filePaths: string[]): Promise<Set<string>> {
|
checkIgnore(filePaths: string[]): Promise<Set<string>> {
|
||||||
return this.run(Operation.CheckIgnore, () => {
|
return this.run(Operation.CheckIgnore, () => {
|
||||||
return new Promise<Set<string>>((resolve, reject) => {
|
return new Promise<Set<string>>((resolve, reject) => {
|
||||||
|
|||||||
@@ -38,14 +38,25 @@ export class FlatFileWizard {
|
|||||||
|
|
||||||
let pages: Map<number, ImportPage> = new Map<number, ImportPage>();
|
let pages: Map<number, ImportPage> = new Map<number, ImportPage>();
|
||||||
|
|
||||||
let connectionId = (await azdata.connection.getCurrentConnection())?.connectionId ??
|
let currentConnection = await azdata.connection.getCurrentConnection();
|
||||||
(await azdata.connection.openConnectionDialog())?.connectionId;
|
|
||||||
|
|
||||||
if (!connectionId) {
|
let connectionId: string;
|
||||||
vscode.window.showErrorMessage(localize('import.needConnection', "Please connect to a server before using this wizard."));
|
|
||||||
return;
|
if (!currentConnection) {
|
||||||
|
connectionId = (await azdata.connection.openConnectionDialog(['MSSQL'])).connectionId;
|
||||||
|
if (!connectionId) {
|
||||||
|
vscode.window.showErrorMessage(localize('import.needConnection', "Please connect to a server before using this wizard."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentConnection.providerId !== 'MSSQL') {
|
||||||
|
vscode.window.showErrorMessage(localize('import.needSQLConnection', "SQL Server Import extension does not support this type of connection"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectionId = currentConnection.connectionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
model.serverId = connectionId;
|
model.serverId = connectionId;
|
||||||
|
|
||||||
this.wizard = azdata.window.createWizard(localize('flatFileImport.wizardName', "Import flat file wizard"));
|
this.wizard = azdata.window.createWizard(localize('flatFileImport.wizardName', "Import flat file wizard"));
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ export class FileConfigPage extends ImportPage {
|
|||||||
this.databaseDropdown.onValueChanged(async (db) => {
|
this.databaseDropdown.onValueChanged(async (db) => {
|
||||||
this.model.database = (<azdata.CategoryValue>this.databaseDropdown.value).name;
|
this.model.database = (<azdata.CategoryValue>this.databaseDropdown.value).name;
|
||||||
//this.populateTableNames();
|
//this.populateTableNames();
|
||||||
|
let connectionProvider = azdata.dataprotocol.getProvider<azdata.ConnectionProvider>(this.model.server.providerName, azdata.DataProviderType.ConnectionProvider);
|
||||||
|
let connectionUri = await azdata.connection.getUriForConnection(this.model.server.connectionId);
|
||||||
|
connectionProvider.changeDatabase(connectionUri, this.model.database);
|
||||||
this.populateSchemaDropdown();
|
this.populateSchemaDropdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -146,13 +149,25 @@ export class FileConfigPage extends ImportPage {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let values = await this.getDatabaseValues();
|
let defaultServerDatabase = this.model.server.options.database;
|
||||||
|
|
||||||
this.model.database = values[0].name;
|
let values: any[];
|
||||||
|
try {
|
||||||
|
values = await this.getDatabaseValues();
|
||||||
|
} catch (error) {
|
||||||
|
// This code is used in case of contained databases when the query will return an error.
|
||||||
|
console.log(error);
|
||||||
|
values = [{ displayName: defaultServerDatabase, name: defaultServerDatabase }];
|
||||||
|
this.databaseDropdown.editable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.model.database = defaultServerDatabase;
|
||||||
|
|
||||||
this.databaseDropdown.updateProperties({
|
this.databaseDropdown.updateProperties({
|
||||||
values: values
|
values: values
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.databaseDropdown.value = { displayName: this.model.database, name: this.model.database };
|
||||||
this.databaseLoader.loading = false;
|
this.databaseLoader.loading = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -266,11 +281,11 @@ export class FileConfigPage extends ImportPage {
|
|||||||
|
|
||||||
private async populateSchemaDropdown(): Promise<boolean> {
|
private async populateSchemaDropdown(): Promise<boolean> {
|
||||||
this.schemaLoader.loading = true;
|
this.schemaLoader.loading = true;
|
||||||
|
|
||||||
let connectionUri = await azdata.connection.getUriForConnection(this.model.server.connectionId);
|
let connectionUri = await azdata.connection.getUriForConnection(this.model.server.connectionId);
|
||||||
let queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>(this.model.server.providerName, azdata.DataProviderType.QueryProvider);
|
let queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>(this.model.server.providerName, azdata.DataProviderType.QueryProvider);
|
||||||
|
|
||||||
const escapedQuotedDb = this.databaseDropdown.value ? `[${(<azdata.CategoryValue>this.databaseDropdown.value).name.replace(/]/g, ']]')}].` : '';
|
const query = `SELECT name FROM sys.schemas`;
|
||||||
const query = `SELECT name FROM ${escapedQuotedDb}sys.schemas`;
|
|
||||||
|
|
||||||
let results = await queryProvider.runQueryAndReturn(connectionUri, query);
|
let results = await queryProvider.runQueryAndReturn(connectionUri, query);
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class ProsePreviewPage extends ImportPage {
|
|||||||
this.table = this.view.modelBuilder.table().withProperties<azdata.TableComponentProperties>({
|
this.table = this.view.modelBuilder.table().withProperties<azdata.TableComponentProperties>({
|
||||||
data: undefined,
|
data: undefined,
|
||||||
columns: undefined,
|
columns: undefined,
|
||||||
forceFitColumns: azdata.ColumnSizingMode.AutoFit
|
forceFitColumns: azdata.ColumnSizingMode.DataFit
|
||||||
}).component();
|
}).component();
|
||||||
this.refresh = this.view.modelBuilder.button().withProperties({
|
this.refresh = this.view.modelBuilder.button().withProperties({
|
||||||
label: localize('flatFileImport.refresh', "Refresh"),
|
label: localize('flatFileImport.refresh', "Refresh"),
|
||||||
|
|||||||
@@ -28,13 +28,11 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "3.4.34",
|
"@types/chai": "3.4.34",
|
||||||
"@types/node": "^10.14.8",
|
"@types/node": "^10.14.8",
|
||||||
|
"azure-keyvault": "^3.0.4",
|
||||||
"chai": "3.5.0",
|
"chai": "3.5.0",
|
||||||
"mocha-junit-reporter": "^1.17.0",
|
"mocha-junit-reporter": "^1.17.0",
|
||||||
"mocha-multi-reporters": "^1.1.7",
|
"mocha-multi-reporters": "^1.1.7",
|
||||||
|
"ms-rest-azure": "^2.6.0",
|
||||||
"vscode": "1.1.5"
|
"vscode": "1.1.5"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"azure-keyvault": "^3.0.4",
|
|
||||||
"ms-rest-azure": "^2.6.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ interface JSONSchemaSettings {
|
|||||||
schema?: any;
|
schema?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace SettingIds {
|
||||||
|
export const enableFormatter = 'json.format.enable';
|
||||||
|
export const enableSchemaDownload = 'json.schemaDownload.enable';
|
||||||
|
export const maxItemsComputed = 'json.maxItemsComputed';
|
||||||
|
}
|
||||||
|
|
||||||
let telemetryReporter: TelemetryReporter | undefined;
|
let telemetryReporter: TelemetryReporter | undefined;
|
||||||
|
|
||||||
export function activate(context: ExtensionContext) {
|
export function activate(context: ExtensionContext) {
|
||||||
@@ -107,10 +113,8 @@ export function activate(context: ExtensionContext) {
|
|||||||
id: 'status.json.resolveError',
|
id: 'status.json.resolveError',
|
||||||
name: localize('json.resolveError', "JSON: Schema Resolution Error"),
|
name: localize('json.resolveError', "JSON: Schema Resolution Error"),
|
||||||
alignment: StatusBarAlignment.Right,
|
alignment: StatusBarAlignment.Right,
|
||||||
priority: 0
|
priority: 0,
|
||||||
});
|
});
|
||||||
schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema';
|
|
||||||
schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema.') + ' ' + localize('json.clickToRetry', 'Click to retry.');
|
|
||||||
schemaResolutionErrorStatusBarItem.text = '$(alert)';
|
schemaResolutionErrorStatusBarItem.text = '$(alert)';
|
||||||
toDispose.push(schemaResolutionErrorStatusBarItem);
|
toDispose.push(schemaResolutionErrorStatusBarItem);
|
||||||
|
|
||||||
@@ -200,6 +204,7 @@ export function activate(context: ExtensionContext) {
|
|||||||
toDispose.push(disposable);
|
toDispose.push(disposable);
|
||||||
client.onReady().then(() => {
|
client.onReady().then(() => {
|
||||||
const schemaDocuments: { [uri: string]: boolean } = {};
|
const schemaDocuments: { [uri: string]: boolean } = {};
|
||||||
|
let schemaDownloadEnabled = true;
|
||||||
|
|
||||||
// handle content request
|
// handle content request
|
||||||
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
|
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
|
||||||
@@ -208,12 +213,16 @@ export function activate(context: ExtensionContext) {
|
|||||||
return Promise.reject(new Error(localize('untitled.schema', 'Unable to load {0}', uri.toString())));
|
return Promise.reject(new Error(localize('untitled.schema', 'Unable to load {0}', uri.toString())));
|
||||||
}
|
}
|
||||||
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
|
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
|
||||||
return workspace.openTextDocument(uri).then(doc => {
|
if (schemaDownloadEnabled) {
|
||||||
schemaDocuments[uri.toString()] = true;
|
return workspace.openTextDocument(uri).then(doc => {
|
||||||
return doc.getText();
|
schemaDocuments[uri.toString()] = true;
|
||||||
}, error => {
|
return doc.getText();
|
||||||
return Promise.reject(error);
|
}, error => {
|
||||||
});
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Promise.reject(localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (telemetryReporter && uri.authority === 'schema.management.azure.com') {
|
if (telemetryReporter && uri.authority === 'schema.management.azure.com') {
|
||||||
/* __GDPR__
|
/* __GDPR__
|
||||||
@@ -294,16 +303,61 @@ export function activate(context: ExtensionContext) {
|
|||||||
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context));
|
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context));
|
||||||
});
|
});
|
||||||
|
|
||||||
// manually register / deregister format provider based on the `html.format.enable` setting avoiding issues with late registration. See #71652.
|
// manually register / deregister format provider based on the `json.format.enable` setting avoiding issues with late registration. See #71652.
|
||||||
updateFormatterRegistration();
|
updateFormatterRegistration();
|
||||||
toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() });
|
toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() });
|
||||||
toDispose.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration('html.format.enable') && updateFormatterRegistration()));
|
|
||||||
|
|
||||||
|
updateSchemaDownloadSetting();
|
||||||
|
|
||||||
|
toDispose.push(workspace.onDidChangeConfiguration(e => {
|
||||||
|
if (e.affectsConfiguration(SettingIds.enableFormatter)) {
|
||||||
|
updateFormatterRegistration();
|
||||||
|
} else if (e.affectsConfiguration(SettingIds.enableSchemaDownload)) {
|
||||||
|
updateSchemaDownloadSetting();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
client.onNotification(ResultLimitReachedNotification.type, message => {
|
client.onNotification(ResultLimitReachedNotification.type, message => {
|
||||||
window.showInformationMessage(`${message}\nUse setting 'json.maxItemsComputed' to configure the limit.`);
|
window.showInformationMessage(`${message}\n${localize('configureLimit', 'Use setting \'{0}\' to configure the limit.', SettingIds.maxItemsComputed)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateFormatterRegistration() {
|
||||||
|
const formatEnabled = workspace.getConfiguration().get(SettingIds.enableFormatter);
|
||||||
|
if (!formatEnabled && rangeFormatting) {
|
||||||
|
rangeFormatting.dispose();
|
||||||
|
rangeFormatting = undefined;
|
||||||
|
} else if (formatEnabled && !rangeFormatting) {
|
||||||
|
rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, {
|
||||||
|
provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]> {
|
||||||
|
const params: DocumentRangeFormattingParams = {
|
||||||
|
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
||||||
|
range: client.code2ProtocolConverter.asRange(range),
|
||||||
|
options: client.code2ProtocolConverter.asFormattingOptions(options)
|
||||||
|
};
|
||||||
|
return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then(
|
||||||
|
client.protocol2CodeConverter.asTextEdits,
|
||||||
|
(error) => {
|
||||||
|
client.logFailedRequest(DocumentRangeFormattingRequest.type, error);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSchemaDownloadSetting() {
|
||||||
|
schemaDownloadEnabled = workspace.getConfiguration().get(SettingIds.enableSchemaDownload) !== false;
|
||||||
|
if (schemaDownloadEnabled) {
|
||||||
|
schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema. Click to retry.');
|
||||||
|
schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema';
|
||||||
|
handleRetryResolveSchemaCommand();
|
||||||
|
} else {
|
||||||
|
schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionDisabledMessage', 'Downloading schemas is disabled. Click to configure.');
|
||||||
|
schemaResolutionErrorStatusBarItem.command = { command: 'workbench.action.openSettings', arguments: [SettingIds.enableSchemaDownload], title: '' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const languageConfiguration: LanguageConfiguration = {
|
const languageConfiguration: LanguageConfiguration = {
|
||||||
@@ -316,30 +370,6 @@ export function activate(context: ExtensionContext) {
|
|||||||
languages.setLanguageConfiguration('json', languageConfiguration);
|
languages.setLanguageConfiguration('json', languageConfiguration);
|
||||||
languages.setLanguageConfiguration('jsonc', languageConfiguration);
|
languages.setLanguageConfiguration('jsonc', languageConfiguration);
|
||||||
|
|
||||||
function updateFormatterRegistration() {
|
|
||||||
const formatEnabled = workspace.getConfiguration().get('json.format.enable');
|
|
||||||
if (!formatEnabled && rangeFormatting) {
|
|
||||||
rangeFormatting.dispose();
|
|
||||||
rangeFormatting = undefined;
|
|
||||||
} else if (formatEnabled && !rangeFormatting) {
|
|
||||||
rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, {
|
|
||||||
provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult<TextEdit[]> {
|
|
||||||
const params: DocumentRangeFormattingParams = {
|
|
||||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
|
|
||||||
range: client.code2ProtocolConverter.asRange(range),
|
|
||||||
options: client.code2ProtocolConverter.asFormattingOptions(options)
|
|
||||||
};
|
|
||||||
return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then(
|
|
||||||
client.protocol2CodeConverter.asTextEdits,
|
|
||||||
(error) => {
|
|
||||||
client.logFailedRequest(DocumentRangeFormattingRequest.type, error);
|
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -386,7 +416,7 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[]
|
|||||||
function getSettings(): Settings {
|
function getSettings(): Settings {
|
||||||
const httpSettings = workspace.getConfiguration('http');
|
const httpSettings = workspace.getConfiguration('http');
|
||||||
|
|
||||||
const resultLimit: number = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get('json.maxItemsComputed')))) || 5000;
|
const resultLimit: number = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get(SettingIds.maxItemsComputed)))) || 5000;
|
||||||
|
|
||||||
const settings: Settings = {
|
const settings: Settings = {
|
||||||
http: {
|
http: {
|
||||||
|
|||||||
@@ -95,7 +95,13 @@
|
|||||||
"type": "number",
|
"type": "number",
|
||||||
"default": 5000,
|
"default": 5000,
|
||||||
"description": "%json.maxItemsComputed.desc%"
|
"description": "%json.maxItemsComputed.desc%"
|
||||||
}
|
},
|
||||||
|
"json.schemaDownload.enable": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "%json.enableSchemaDownload.desc%",
|
||||||
|
"tags": ["usesOnlineServices"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"configurationDefaults": {
|
"configurationDefaults": {
|
||||||
|
|||||||
@@ -12,5 +12,6 @@
|
|||||||
"json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.",
|
"json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.",
|
||||||
"json.schemaResolutionErrorMessage": "Unable to resolve schema.",
|
"json.schemaResolutionErrorMessage": "Unable to resolve schema.",
|
||||||
"json.clickToRetry": "Click to retry.",
|
"json.clickToRetry": "Click to retry.",
|
||||||
"json.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons)."
|
"json.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons).",
|
||||||
|
"json.enableSchemaDownload.desc": "When enabled, JSON schemas can be fetched from http and https locations."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,25 @@
|
|||||||
"requiredRPackages": [
|
"requiredRPackages": [
|
||||||
{
|
{
|
||||||
"name": "RODBCext",
|
"name": "RODBCext",
|
||||||
"repository": "https://cran.microsoft.com"
|
"repository": "https://mran.microsoft.com/snapshot/2019-02-01/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sqlmlutils",
|
"name": "sqlmlutils",
|
||||||
"fileName": "sqlmlutils_0.7.1.zip",
|
"fileName": "sqlmlutils_0.7.1.zip",
|
||||||
"downloadUrl": "https://github.com/microsoft/sqlmlutils/blob/master/R/dist/sqlmlutils_0.7.1.zip?raw=true"
|
"downloadUrl": "https://github.com/microsoft/sqlmlutils/blob/master/R/dist/sqlmlutils_0.7.1.zip?raw=true",
|
||||||
|
"platform" : "win32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sqlmlutils",
|
||||||
|
"fileName": "sqlmlutils_0.7.1.tar.gz",
|
||||||
|
"downloadUrl": "https://github.com/microsoft/sqlmlutils/blob/master/R/dist/sqlmlutils_0.7.1.tar.gz?raw=true",
|
||||||
|
"platform" : "darwin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sqlmlutils",
|
||||||
|
"fileName": "sqlmlutils_0.7.1.tar.gz",
|
||||||
|
"downloadUrl": "https://github.com/microsoft/sqlmlutils/blob/master/R/dist/sqlmlutils_0.7.1.tar.gz?raw=true",
|
||||||
|
"platform" : "linux"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rPackagesRepository": "https://cran.r-project.org"
|
"rPackagesRepository": "https://cran.r-project.org"
|
||||||
|
|||||||
@@ -32,4 +32,9 @@ export interface PackageConfigModel {
|
|||||||
* Package file name if package has download url
|
* Package file name if package has download url
|
||||||
*/
|
*/
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package platform (Windows, Mac, Linux)
|
||||||
|
*/
|
||||||
|
platform?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ export class PackageManager {
|
|||||||
*/
|
*/
|
||||||
public async managePackages(): Promise<void> {
|
public async managePackages(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await this.enableExternalScript();
|
|
||||||
|
|
||||||
// Only execute the command if there's a valid connection with ml configuration enabled
|
// Only execute the command if there's a valid connection with ml configuration enabled
|
||||||
//
|
//
|
||||||
@@ -129,7 +128,8 @@ export class PackageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await utils.createFolder(utils.getRPackagesFolderPath(this._rootFolder));
|
await utils.createFolder(utils.getRPackagesFolderPath(this._rootFolder));
|
||||||
await Promise.all(this._config.requiredSqlRPackages.map(x => this.installRPackage(x)));
|
const packages = this._config.requiredSqlRPackages.filter(p => !p.platform || p.platform === process.platform);
|
||||||
|
await Promise.all(packages.map(x => this.installRPackage(x)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export class SqlRPackageManageProvider extends SqlPackageManageProviderBase impl
|
|||||||
let scripts: string[] = [
|
let scripts: string[] = [
|
||||||
'formals(quit)$save <- formals(q)$save <- "no"',
|
'formals(quit)$save <- formals(q)$save <- "no"',
|
||||||
'library(sqlmlutils)',
|
'library(sqlmlutils)',
|
||||||
`connection <- connectionInfo(${connectionParts})`,
|
`connection <- connectionInfo(driver= "ODBC Driver 17 for SQL Server", ${connectionParts})`,
|
||||||
`r = getOption("repos")`,
|
`r = getOption("repos")`,
|
||||||
`r["CRAN"] = "${this._config.rPackagesRepository}"`,
|
`r["CRAN"] = "${this._config.rPackagesRepository}"`,
|
||||||
`options(repos = r)`,
|
`options(repos = r)`,
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ function createContext(): TestContext {
|
|||||||
storagePath: '',
|
storagePath: '',
|
||||||
globalStoragePath: '',
|
globalStoragePath: '',
|
||||||
logPath: '',
|
logPath: '',
|
||||||
extensionUri: vscode.Uri.parse('')
|
extensionUri: vscode.Uri.parse(''),
|
||||||
|
environmentVariableCollection: { } as any
|
||||||
},
|
},
|
||||||
outputChannel: {
|
outputChannel: {
|
||||||
name: '',
|
name: '',
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ export function createViewContext(): ViewTestContext {
|
|||||||
removeItem: () => true,
|
removeItem: () => true,
|
||||||
insertItem: () => { },
|
insertItem: () => { },
|
||||||
items: [],
|
items: [],
|
||||||
setLayout: () => { }
|
setLayout: () => { },
|
||||||
|
setItemLayout: () => { }
|
||||||
};
|
};
|
||||||
let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
|
let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -133,7 +133,6 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
|
|||||||
webview: vscode.WebviewPanel,
|
webview: vscode.WebviewPanel,
|
||||||
state: any
|
state: any
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log(state);
|
|
||||||
const resource = vscode.Uri.parse(state.resource);
|
const resource = vscode.Uri.parse(state.resource);
|
||||||
const locked = state.locked;
|
const locked = state.locked;
|
||||||
const line = state.line;
|
const line = state.line;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||||
"version": "2.0.0-release.61",
|
"version": "2.0.0-release.64",
|
||||||
"downloadFileNames": {
|
"downloadFileNames": {
|
||||||
"Windows_86": "win-x86-netcoreapp2.2.zip",
|
"Windows_86": "win-x86-netcoreapp2.2.zip",
|
||||||
"Windows_64": "win-x64-netcoreapp2.2.zip",
|
"Windows_64": "win-x64-netcoreapp2.2.zip",
|
||||||
|
|||||||
@@ -366,6 +366,28 @@
|
|||||||
"displayName": "%onprem.serverProperties.osVersion%",
|
"displayName": "%onprem.serverProperties.osVersion%",
|
||||||
"value": "osVersion"
|
"value": "osVersion"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"databasesListProperties": [
|
||||||
|
{
|
||||||
|
"displayName": "%databasesListProperties.name%",
|
||||||
|
"value": "name",
|
||||||
|
"widthWeight": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "%databasesListProperties.status%",
|
||||||
|
"value": "state",
|
||||||
|
"widthWeight": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "%databasesListProperties.size%",
|
||||||
|
"value": "sizeInMB",
|
||||||
|
"widthWeight": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "%databasesListProperties.lastBackup%",
|
||||||
|
"value": "lastBackup",
|
||||||
|
"widthWeight": 20
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -404,6 +426,23 @@
|
|||||||
"displayName": "%cloud.serverProperties.serverEdition%",
|
"displayName": "%cloud.serverProperties.serverEdition%",
|
||||||
"value": "serverEdition"
|
"value": "serverEdition"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"databasesListProperties": [
|
||||||
|
{
|
||||||
|
"displayName": "%databasesListProperties.name%",
|
||||||
|
"value": "name",
|
||||||
|
"widthWeight": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "%databasesListProperties.status%",
|
||||||
|
"value": "state",
|
||||||
|
"widthWeight": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "%databasesListProperties.size%",
|
||||||
|
"value": "sizeInMB",
|
||||||
|
"widthWeight": 20
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -434,6 +473,13 @@
|
|||||||
"displayName": "%cloud.serverProperties.serverEdition%",
|
"displayName": "%cloud.serverProperties.serverEdition%",
|
||||||
"value": "serverEdition"
|
"value": "serverEdition"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"databasesListProperties": [
|
||||||
|
{
|
||||||
|
"displayName": "%databasesListProperties.name%",
|
||||||
|
"value": "name",
|
||||||
|
"widthWeight": 100
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1043,7 +1089,7 @@
|
|||||||
"figures": "^2.0.0",
|
"figures": "^2.0.0",
|
||||||
"find-remove": "1.2.1",
|
"find-remove": "1.2.1",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"request-promise": "^4.2.2",
|
"request-light": "^0.3.0",
|
||||||
"service-downloader": "0.2.1",
|
"service-downloader": "0.2.1",
|
||||||
"stream-meter": "^1.0.4",
|
"stream-meter": "^1.0.4",
|
||||||
"through2": "^3.0.1",
|
"through2": "^3.0.1",
|
||||||
@@ -1057,7 +1103,6 @@
|
|||||||
"@types/chai": "^4.2.11",
|
"@types/chai": "^4.2.11",
|
||||||
"@types/mocha": "^7.0.2",
|
"@types/mocha": "^7.0.2",
|
||||||
"@types/request": "^2.48.2",
|
"@types/request": "^2.48.2",
|
||||||
"@types/request-promise": "^4.1.44",
|
|
||||||
"@types/stream-meter": "^0.0.22",
|
"@types/stream-meter": "^0.0.22",
|
||||||
"@types/through2": "^2.0.34",
|
"@types/through2": "^2.0.34",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
|
|||||||
@@ -140,5 +140,10 @@
|
|||||||
"mssql.connectionOptions.packetSize.displayName": "Packet size",
|
"mssql.connectionOptions.packetSize.displayName": "Packet size",
|
||||||
"mssql.connectionOptions.packetSize.description": "Size in bytes of the network packets used to communicate with an instance of SQL Server",
|
"mssql.connectionOptions.packetSize.description": "Size in bytes of the network packets used to communicate with an instance of SQL Server",
|
||||||
"mssql.connectionOptions.typeSystemVersion.displayName": "Type system version",
|
"mssql.connectionOptions.typeSystemVersion.displayName": "Type system version",
|
||||||
"mssql.connectionOptions.typeSystemVersion.description": "Indicates which server type system the provider will expose through the DataReader"
|
"mssql.connectionOptions.typeSystemVersion.description": "Indicates which server type system the provider will expose through the DataReader",
|
||||||
|
"databasesListProperties.name": "Name",
|
||||||
|
"databasesListProperties.status": "Status",
|
||||||
|
"databasesListProperties.size": "Size (MB)",
|
||||||
|
"databasesListProperties.lastBackup": "Last backup",
|
||||||
|
"objectsListProperties.name": "Name"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export const SchemaCompareService = 'schemaCompareService';
|
|||||||
export const LanguageExtensionService = 'languageExtensionService';
|
export const LanguageExtensionService = 'languageExtensionService';
|
||||||
export const objectExplorerPrefix: string = 'objectexplorer://';
|
export const objectExplorerPrefix: string = 'objectexplorer://';
|
||||||
export const ViewType = 'view';
|
export const ViewType = 'view';
|
||||||
|
export const SqlAssessmentService = 'sqlAssessmentService';
|
||||||
|
|
||||||
export enum BuiltInCommands {
|
export enum BuiltInCommands {
|
||||||
SetContext = 'setContext'
|
SetContext = 'setContext'
|
||||||
|
|||||||
@@ -674,6 +674,35 @@ export namespace SchemaCompareCancellationRequest {
|
|||||||
|
|
||||||
// ------------------------------- <Schema Compare> -----------------------------
|
// ------------------------------- <Schema Compare> -----------------------------
|
||||||
|
|
||||||
|
// ------------------------------- <Sql Assessment> -----------------------------
|
||||||
|
|
||||||
|
export interface SqlAssessmentParams {
|
||||||
|
ownerUri: string;
|
||||||
|
targetType: mssql.SqlAssessmentTargetType
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenerateSqlAssessmentScriptParams {
|
||||||
|
items: mssql.SqlAssessmentResultItem[];
|
||||||
|
taskExecutionMode: azdata.TaskExecutionMode;
|
||||||
|
targetServerName: string;
|
||||||
|
targetDatabaseName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace SqlAssessmentInvokeRequest {
|
||||||
|
export const type = new RequestType<SqlAssessmentParams, mssql.SqlAssessmentResult, void, void>('assessment/invoke');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace GetSqlAssessmentItemsRequest {
|
||||||
|
export const type = new RequestType<SqlAssessmentParams, mssql.SqlAssessmentResult, void, void>('assessment/getAssessmentItems');
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace GenerateSqlAssessmentScriptRequest {
|
||||||
|
export const type = new RequestType<GenerateSqlAssessmentScriptParams, azdata.ResultStatus, void, void>('assessment/generateScript');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------- <Sql Assessment> -----------------------------
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------- <Serialization> -----------------------------
|
// ------------------------------- <Serialization> -----------------------------
|
||||||
export namespace SerializeDataStartRequest {
|
export namespace SerializeDataStartRequest {
|
||||||
export const type = new RequestType<azdata.SerializeDataStartRequestParams, azdata.SerializeDataResult, void, void>('serialize/start');
|
export const type = new RequestType<azdata.SerializeDataStartRequestParams, azdata.SerializeDataResult, void, void>('serialize/start');
|
||||||
|
|||||||
46
extensions/mssql/src/mssql.d.ts
vendored
46
extensions/mssql/src/mssql.d.ts
vendored
@@ -41,6 +41,8 @@ export interface IExtension {
|
|||||||
readonly languageExtension: ILanguageExtensionService;
|
readonly languageExtension: ILanguageExtensionService;
|
||||||
|
|
||||||
readonly dacFx: IDacFxService;
|
readonly dacFx: IDacFxService;
|
||||||
|
|
||||||
|
readonly sqlAssessment: ISqlAssessmentService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -463,3 +465,47 @@ export interface ListRegisteredServersResult {
|
|||||||
registeredServerGroups: Array<RegisteredServerGroup>;
|
registeredServerGroups: Array<RegisteredServerGroup>;
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sql Assessment
|
||||||
|
*/
|
||||||
|
|
||||||
|
// SqlAssessment interfaces -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
export const enum SqlAssessmentTargetType {
|
||||||
|
Server = 1,
|
||||||
|
Database = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum SqlAssessmentResultItemKind {
|
||||||
|
RealResult = 0,
|
||||||
|
Warning = 1,
|
||||||
|
Error = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SqlAssessmentResultItem {
|
||||||
|
rulesetVersion: string;
|
||||||
|
rulesetName: string;
|
||||||
|
targetType: SqlAssessmentTargetType;
|
||||||
|
targetName: string;
|
||||||
|
checkId: string;
|
||||||
|
tags: string[];
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
message: string;
|
||||||
|
helpLink: string;
|
||||||
|
level: string;
|
||||||
|
timestamp: string;
|
||||||
|
kind: SqlAssessmentResultItemKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SqlAssessmentResult extends azdata.ResultStatus {
|
||||||
|
items: SqlAssessmentResultItem[];
|
||||||
|
apiVersion: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISqlAssessmentService {
|
||||||
|
assessmentInvoke(ownerUri: string, targetType: SqlAssessmentTargetType): Promise<SqlAssessmentResult>;
|
||||||
|
getAssessmentItems(ownerUri: string, targetType: SqlAssessmentTargetType): Promise<SqlAssessmentResult>;
|
||||||
|
generateAssessmentScript(items: SqlAssessmentResultItem[], targetServerName: string, targetDatabaseName: string, taskExecutionMode: azdata.TaskExecutionMode): Promise<azdata.ResultStatus>;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { AppContext } from './appContext';
|
import { AppContext } from './appContext';
|
||||||
import { IExtension, ICmsService, IDacFxService, ISchemaCompareService, MssqlObjectExplorerBrowser, ILanguageExtensionService } from './mssql';
|
import { IExtension, ICmsService, IDacFxService, ISchemaCompareService, MssqlObjectExplorerBrowser, ILanguageExtensionService, ISqlAssessmentService } from './mssql';
|
||||||
import * as constants from './constants';
|
import * as constants from './constants';
|
||||||
import { MssqlObjectExplorerNodeProvider } from './objectExplorerNodeProvider/objectExplorerNodeProvider';
|
import { MssqlObjectExplorerNodeProvider } from './objectExplorerNodeProvider/objectExplorerNodeProvider';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
@@ -30,6 +30,9 @@ export function createMssqlApi(context: AppContext): IExtension {
|
|||||||
return <any>oeProvider.findSqlClusterNodeByContext(explorerContext);
|
return <any>oeProvider.findSqlClusterNodeByContext(explorerContext);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
get sqlAssessment() {
|
||||||
|
return context.getService<ISqlAssessmentService>(constants.SqlAssessmentService);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,15 +37,14 @@ export class SparkJobSubmissionModel {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly _sqlClusterConnection: SqlClusterConnection,
|
private readonly _sqlClusterConnection: SqlClusterConnection,
|
||||||
private readonly _dialog: azdata.window.Dialog,
|
private readonly _dialog: azdata.window.Dialog,
|
||||||
private readonly _appContext: AppContext,
|
private readonly _appContext: AppContext) {
|
||||||
requestService?: typeof import('request-promise')) {
|
|
||||||
|
|
||||||
if (!this._sqlClusterConnection || !this._dialog || !this._appContext) {
|
if (!this._sqlClusterConnection || !this._dialog || !this._appContext) {
|
||||||
throw new Error(localize('sparkJobSubmission.SparkJobSubmissionModelInitializeError',
|
throw new Error(localize('sparkJobSubmission.SparkJobSubmissionModelInitializeError',
|
||||||
"Parameters for SparkJobSubmissionModel is illegal"));
|
"Parameters for SparkJobSubmissionModel is illegal"));
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dialogService = new SparkJobSubmissionService(requestService);
|
this._dialogService = new SparkJobSubmissionService();
|
||||||
this._guidForClusterFolder = utils.generateGuid();
|
this._guidForClusterFolder = utils.generateGuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,21 +10,9 @@ import * as constants from '../../../constants';
|
|||||||
import { SqlClusterConnection } from '../../../objectExplorerNodeProvider/connection';
|
import { SqlClusterConnection } from '../../../objectExplorerNodeProvider/connection';
|
||||||
import * as utils from '../../../utils';
|
import * as utils from '../../../utils';
|
||||||
import * as auth from '../../../util/auth';
|
import * as auth from '../../../util/auth';
|
||||||
import { Options } from 'request-promise';
|
import * as request from 'request-light';
|
||||||
|
|
||||||
export class SparkJobSubmissionService {
|
export class SparkJobSubmissionService {
|
||||||
private _requestPromise: typeof import('request-promise');
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
requestService?: typeof import('request-promise')) {
|
|
||||||
if (requestService) {
|
|
||||||
// this is to fake the request service for test.
|
|
||||||
this._requestPromise = requestService;
|
|
||||||
} else {
|
|
||||||
this._requestPromise = require('request-promise');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async submitBatchJob(submissionArgs: SparkJobSubmissionInput): Promise<string> {
|
public async submitBatchJob(submissionArgs: SparkJobSubmissionInput): Promise<string> {
|
||||||
try {
|
try {
|
||||||
let livyUrl: string = `https://${submissionArgs.host}:${submissionArgs.port}${submissionArgs.livyPath}/`;
|
let livyUrl: string = `https://${submissionArgs.host}:${submissionArgs.port}${submissionArgs.livyPath}/`;
|
||||||
@@ -32,12 +20,11 @@ export class SparkJobSubmissionService {
|
|||||||
// Get correct authentication headers
|
// Get correct authentication headers
|
||||||
let headers = await this.getAuthenticationHeaders(submissionArgs);
|
let headers = await this.getAuthenticationHeaders(submissionArgs);
|
||||||
|
|
||||||
let options: Options = {
|
let options: request.XHROptions = {
|
||||||
uri: livyUrl,
|
url: livyUrl,
|
||||||
method: 'POST',
|
type: 'POST',
|
||||||
json: true,
|
strictSSL: !auth.getIgnoreSslVerificationConfigSetting(),
|
||||||
rejectUnauthorized: !auth.getIgnoreSslVerificationConfigSetting(),
|
data: {
|
||||||
body: {
|
|
||||||
file: submissionArgs.sparkFile,
|
file: submissionArgs.sparkFile,
|
||||||
proxyUser: submissionArgs.user,
|
proxyUser: submissionArgs.user,
|
||||||
className: submissionArgs.mainClass,
|
className: submissionArgs.mainClass,
|
||||||
@@ -51,7 +38,7 @@ export class SparkJobSubmissionService {
|
|||||||
if (submissionArgs.jobArguments && submissionArgs.jobArguments.trim()) {
|
if (submissionArgs.jobArguments && submissionArgs.jobArguments.trim()) {
|
||||||
let argsList = submissionArgs.jobArguments.split(' ');
|
let argsList = submissionArgs.jobArguments.split(' ');
|
||||||
if (argsList.length > 0) {
|
if (argsList.length > 0) {
|
||||||
options.body['args'] = argsList;
|
options.data['args'] = argsList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +46,7 @@ export class SparkJobSubmissionService {
|
|||||||
if (submissionArgs.jarFileList && submissionArgs.jarFileList.trim()) {
|
if (submissionArgs.jarFileList && submissionArgs.jarFileList.trim()) {
|
||||||
let jarList = submissionArgs.jarFileList.split(';');
|
let jarList = submissionArgs.jarFileList.split(';');
|
||||||
if (jarList.length > 0) {
|
if (jarList.length > 0) {
|
||||||
options.body['jars'] = jarList;
|
options.data['jars'] = jarList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +54,7 @@ export class SparkJobSubmissionService {
|
|||||||
if (submissionArgs.pyFileList && submissionArgs.pyFileList.trim()) {
|
if (submissionArgs.pyFileList && submissionArgs.pyFileList.trim()) {
|
||||||
let pyList = submissionArgs.pyFileList.split(';');
|
let pyList = submissionArgs.pyFileList.split(';');
|
||||||
if (pyList.length > 0) {
|
if (pyList.length > 0) {
|
||||||
options.body['pyFiles'] = pyList;
|
options.data['pyFiles'] = pyList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,11 +62,17 @@ export class SparkJobSubmissionService {
|
|||||||
if (submissionArgs.otherFileList && submissionArgs.otherFileList.trim()) {
|
if (submissionArgs.otherFileList && submissionArgs.otherFileList.trim()) {
|
||||||
let otherList = submissionArgs.otherFileList.split(';');
|
let otherList = submissionArgs.otherFileList.split(';');
|
||||||
if (otherList.length > 0) {
|
if (otherList.length > 0) {
|
||||||
options.body['files'] = otherList;
|
options.data['files'] = otherList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this._requestPromise(options);
|
options.data = JSON.stringify(options.data);
|
||||||
|
|
||||||
|
// Note this is currently required to be called each time since request-light is overwriting
|
||||||
|
// the setting passed in through the options. If/when that gets fixed this can be removed
|
||||||
|
request.configure(null, !auth.getIgnoreSslVerificationConfigSetting());
|
||||||
|
|
||||||
|
const response = JSON.parse((await request.xhr(options)).responseText);
|
||||||
if (response && utils.isValidNumber(response.id)) {
|
if (response && utils.isValidNumber(response.id)) {
|
||||||
return response.id;
|
return response.id;
|
||||||
}
|
}
|
||||||
@@ -108,16 +101,19 @@ export class SparkJobSubmissionService {
|
|||||||
let livyUrl = `https://${submissionArgs.host}:${submissionArgs.port}${submissionArgs.livyPath}/${livyBatchId}/log`;
|
let livyUrl = `https://${submissionArgs.host}:${submissionArgs.port}${submissionArgs.livyPath}/${livyBatchId}/log`;
|
||||||
let headers = await this.getAuthenticationHeaders(submissionArgs);
|
let headers = await this.getAuthenticationHeaders(submissionArgs);
|
||||||
|
|
||||||
let options = {
|
let options: request.XHROptions = {
|
||||||
uri: livyUrl,
|
url: livyUrl,
|
||||||
method: 'GET',
|
type: 'GET',
|
||||||
json: true,
|
strictSSL: !auth.getIgnoreSslVerificationConfigSetting(),
|
||||||
rejectUnauthorized: !auth.getIgnoreSslVerificationConfigSetting(),
|
|
||||||
// authentication headers
|
// authentication headers
|
||||||
headers: headers
|
headers: headers
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await this._requestPromise(options);
|
// Note this is currently required to be called each time since request-light is overwriting
|
||||||
|
// the setting passed in through the options. If/when that gets fixed this can be removed
|
||||||
|
request.configure(null, !auth.getIgnoreSslVerificationConfigSetting());
|
||||||
|
|
||||||
|
const response = JSON.parse((await request.xhr(options)).responseText);
|
||||||
if (response && response.log) {
|
if (response && response.log) {
|
||||||
return this.extractYarnAppIdFromLog(response.log);
|
return this.extractYarnAppIdFromLog(response.log);
|
||||||
}
|
}
|
||||||
|
|||||||
67
extensions/mssql/src/sqlAssessment/sqlAssessmentService.ts
Normal file
67
extensions/mssql/src/sqlAssessment/sqlAssessmentService.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as mssql from '../mssql';
|
||||||
|
import { AppContext } from '../appContext';
|
||||||
|
import { SqlOpsDataClient, ISqlOpsFeature } from 'dataprotocol-client';
|
||||||
|
import { ClientCapabilities } from 'vscode-languageclient';
|
||||||
|
import * as constants from '../constants';
|
||||||
|
import * as azdata from 'azdata';
|
||||||
|
import * as contracts from '../contracts';
|
||||||
|
|
||||||
|
|
||||||
|
export class SqlAssessmentService implements mssql.ISqlAssessmentService {
|
||||||
|
public static asFeature(context: AppContext): ISqlOpsFeature {
|
||||||
|
return class extends SqlAssessmentService {
|
||||||
|
constructor(client: SqlOpsDataClient) {
|
||||||
|
super(context, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
fillClientCapabilities(capabilities: ClientCapabilities): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize(): void {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(context: AppContext, protected readonly client: SqlOpsDataClient) {
|
||||||
|
context.registerService(constants.SqlAssessmentService, this);
|
||||||
|
}
|
||||||
|
async assessmentInvoke(ownerUri: string, targetType: mssql.SqlAssessmentTargetType): Promise<mssql.SqlAssessmentResult | undefined> {
|
||||||
|
let params: contracts.SqlAssessmentParams = { ownerUri: ownerUri, targetType: targetType };
|
||||||
|
try {
|
||||||
|
return this.client.sendRequest(contracts.SqlAssessmentInvokeRequest.type, params);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.client.logFailedRequest(contracts.SqlAssessmentInvokeRequest.type, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
async getAssessmentItems(ownerUri: string, targetType: mssql.SqlAssessmentTargetType): Promise<mssql.SqlAssessmentResult | undefined> {
|
||||||
|
let params: contracts.SqlAssessmentParams = { ownerUri: ownerUri, targetType: targetType };
|
||||||
|
try {
|
||||||
|
return this.client.sendRequest(contracts.GetSqlAssessmentItemsRequest.type, params);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.client.logFailedRequest(contracts.GetSqlAssessmentItemsRequest.type, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
async generateAssessmentScript(items: mssql.SqlAssessmentResultItem[], targetServerName: string, targetDatabaseName: string, taskExecutionMode: azdata.TaskExecutionMode): Promise<azdata.ResultStatus | undefined> {
|
||||||
|
let params: contracts.GenerateSqlAssessmentScriptParams = { items: items, targetServerName: targetServerName, targetDatabaseName: targetDatabaseName, taskExecutionMode: taskExecutionMode };
|
||||||
|
try {
|
||||||
|
return this.client.sendRequest(contracts.GenerateSqlAssessmentScriptRequest.type, params);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.client.logFailedRequest(contracts.GenerateSqlAssessmentScriptRequest.type, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import { CompletionExtensionParams, CompletionExtLoadRequest } from './contracts
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { LanguageExtensionService } from './languageExtension/languageExtensionService';
|
import { LanguageExtensionService } from './languageExtension/languageExtensionService';
|
||||||
|
import { SqlAssessmentService } from './sqlAssessment/sqlAssessmentService';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
const outputChannel = vscode.window.createOutputChannel(Constants.serviceName);
|
const outputChannel = vscode.window.createOutputChannel(Constants.serviceName);
|
||||||
@@ -157,7 +158,8 @@ function getClientOptions(context: AppContext): ClientOptions {
|
|||||||
SchemaCompareService.asFeature(context),
|
SchemaCompareService.asFeature(context),
|
||||||
LanguageExtensionService.asFeature(context),
|
LanguageExtensionService.asFeature(context),
|
||||||
DacFxService.asFeature(context),
|
DacFxService.asFeature(context),
|
||||||
CmsService.asFeature(context)
|
CmsService.asFeature(context),
|
||||||
|
SqlAssessmentService.asFeature(context)
|
||||||
],
|
],
|
||||||
outputChannel: new CustomOutputChannel()
|
outputChannel: new CustomOutputChannel()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
"@types/bluebird@*":
|
|
||||||
version "3.5.30"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.30.tgz#ee034a0eeea8b84ed868b1aa60d690b08a6cfbc5"
|
|
||||||
integrity sha512-8LhzvcjIoqoi1TghEkRMkbbmM+jhHnBokPGkJWjclMK+Ks0MxEBow3/p2/iFTZ+OIbJHQDSfpgdZEb+af3gfVw==
|
|
||||||
|
|
||||||
"@types/bytes@^3.0.0":
|
"@types/bytes@^3.0.0":
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/bytes/-/bytes-3.1.0.tgz#835a3e4aea3b4d7604aca216a78de372bff3ecc3"
|
resolved "https://registry.yarnpkg.com/@types/bytes/-/bytes-3.1.0.tgz#835a3e4aea3b4d7604aca216a78de372bff3ecc3"
|
||||||
@@ -32,15 +27,7 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.1.tgz#49a2a83df9d26daacead30d0ccc8762b128d53c7"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.1.tgz#49a2a83df9d26daacead30d0ccc8762b128d53c7"
|
||||||
integrity sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==
|
integrity sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==
|
||||||
|
|
||||||
"@types/request-promise@^4.1.44":
|
"@types/request@^2.48.2":
|
||||||
version "4.1.46"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/request-promise/-/request-promise-4.1.46.tgz#37df6efae984316dfbfbbe8fcda37f3ba52822f2"
|
|
||||||
integrity sha512-3Thpj2Va5m0ji3spaCk8YKrjkZyZc6RqUVOphA0n/Xet66AW/AiOAs5vfXhQIL5NmkaO7Jnun7Nl9NEjJ2zBaw==
|
|
||||||
dependencies:
|
|
||||||
"@types/bluebird" "*"
|
|
||||||
"@types/request" "*"
|
|
||||||
|
|
||||||
"@types/request@*", "@types/request@^2.48.2":
|
|
||||||
version "2.48.4"
|
version "2.48.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.4.tgz#df3d43d7b9ed3550feaa1286c6eabf0738e6cf7e"
|
resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.4.tgz#df3d43d7b9ed3550feaa1286c6eabf0738e6cf7e"
|
||||||
integrity sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw==
|
integrity sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw==
|
||||||
@@ -257,11 +244,6 @@ binary-extensions@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
|
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
|
||||||
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
|
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
|
||||||
|
|
||||||
bluebird@^3.5.0:
|
|
||||||
version "3.7.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
|
||||||
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
|
||||||
|
|
||||||
brace-expansion@^1.1.7:
|
brace-expansion@^1.1.7:
|
||||||
version "1.1.11"
|
version "1.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||||
@@ -893,7 +875,7 @@ http-signature@~1.2.0:
|
|||||||
jsprim "^1.2.2"
|
jsprim "^1.2.2"
|
||||||
sshpk "^1.7.0"
|
sshpk "^1.7.0"
|
||||||
|
|
||||||
https-proxy-agent@^2.2.3:
|
https-proxy-agent@^2.2.3, https-proxy-agent@^2.2.4:
|
||||||
version "2.2.4"
|
version "2.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
||||||
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
|
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
|
||||||
@@ -1493,22 +1475,14 @@ remap-istanbul@^0.11.1:
|
|||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
through2 "2.0.1"
|
through2 "2.0.1"
|
||||||
|
|
||||||
request-promise-core@1.1.3:
|
request-light@^0.3.0:
|
||||||
version "1.1.3"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9"
|
resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.3.0.tgz#04daa783e7f0a70392328dda4b546f3e27845f2d"
|
||||||
integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==
|
integrity sha512-xlVlZVT0ZvCT+c3zm3SjeFCzchoQxsUUmx5fkal0I6RIDJK+lmb1UYyKJ7WM4dTfnzHP4ElWwAf8Dli8c0/tVA==
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash "^4.17.15"
|
http-proxy-agent "^2.1.0"
|
||||||
|
https-proxy-agent "^2.2.4"
|
||||||
request-promise@^4.2.2:
|
vscode-nls "^4.1.1"
|
||||||
version "4.2.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.5.tgz#186222c59ae512f3497dfe4d75a9c8461bd0053c"
|
|
||||||
integrity sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg==
|
|
||||||
dependencies:
|
|
||||||
bluebird "^3.5.0"
|
|
||||||
request-promise-core "1.1.3"
|
|
||||||
stealthy-require "^1.1.1"
|
|
||||||
tough-cookie "^2.3.3"
|
|
||||||
|
|
||||||
request@^2.88.0:
|
request@^2.88.0:
|
||||||
version "2.88.2"
|
version "2.88.2"
|
||||||
@@ -1642,11 +1616,6 @@ sshpk@^1.7.0:
|
|||||||
safer-buffer "^2.0.2"
|
safer-buffer "^2.0.2"
|
||||||
tweetnacl "~0.14.0"
|
tweetnacl "~0.14.0"
|
||||||
|
|
||||||
stealthy-require@^1.1.1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
|
|
||||||
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
|
|
||||||
|
|
||||||
stream-meter@^1.0.4:
|
stream-meter@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/stream-meter/-/stream-meter-1.0.4.tgz#52af95aa5ea760a2491716704dbff90f73afdd1d"
|
resolved "https://registry.yarnpkg.com/stream-meter/-/stream-meter-1.0.4.tgz#52af95aa5ea760a2491716704dbff90f73afdd1d"
|
||||||
@@ -1812,14 +1781,6 @@ to-regex-range@^5.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-number "^7.0.0"
|
is-number "^7.0.0"
|
||||||
|
|
||||||
tough-cookie@^2.3.3, tough-cookie@~2.5.0:
|
|
||||||
version "2.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
|
||||||
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
|
|
||||||
dependencies:
|
|
||||||
psl "^1.1.28"
|
|
||||||
punycode "^2.1.1"
|
|
||||||
|
|
||||||
tough-cookie@^3.0.1:
|
tough-cookie@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2"
|
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2"
|
||||||
@@ -1829,6 +1790,14 @@ tough-cookie@^3.0.1:
|
|||||||
psl "^1.1.28"
|
psl "^1.1.28"
|
||||||
punycode "^2.1.1"
|
punycode "^2.1.1"
|
||||||
|
|
||||||
|
tough-cookie@~2.5.0:
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
||||||
|
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
|
||||||
|
dependencies:
|
||||||
|
psl "^1.1.28"
|
||||||
|
punycode "^2.1.1"
|
||||||
|
|
||||||
tunnel-agent@^0.6.0:
|
tunnel-agent@^0.6.0:
|
||||||
version "0.6.0"
|
version "0.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
|
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
|
||||||
@@ -1928,7 +1897,7 @@ vscode-languageserver-types@3.14.0:
|
|||||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743"
|
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743"
|
||||||
integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==
|
integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==
|
||||||
|
|
||||||
vscode-nls@^4.0.0:
|
vscode-nls@^4.0.0, vscode-nls@^4.1.1:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167"
|
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167"
|
||||||
integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==
|
integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==
|
||||||
|
|||||||
@@ -110,12 +110,11 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
|||||||
this.currentBook = existingBook;
|
this.currentBook = existingBook;
|
||||||
} else {
|
} else {
|
||||||
await this.createAndAddBookModel(bookPath, !!isNotebook);
|
await this.createAndAddBookModel(bookPath, !!isNotebook);
|
||||||
let bookViewer = vscode.window.createTreeView(this.viewId, { showCollapseAll: true, treeDataProvider: this });
|
|
||||||
this.currentBook = this.books.find(book => book.bookPath === bookPath);
|
this.currentBook = this.books.find(book => book.bookPath === bookPath);
|
||||||
bookViewer.reveal(this.currentBook.bookItems[0], { expand: vscode.TreeItemCollapsibleState.Expanded, focus: true, select: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showPreview) {
|
if (showPreview) {
|
||||||
|
this._bookViewer.reveal(this.currentBook.bookItems[0], { expand: vscode.TreeItemCollapsibleState.Expanded, focus: true, select: true });
|
||||||
await this.showPreviewFile(urlToOpen);
|
await this.showPreviewFile(urlToOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,9 +222,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
|||||||
openDocumentListenerUnsubscriber.dispose();
|
openDocumentListenerUnsubscriber.dispose();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
azdata.nb.showNotebookDocument(vscode.Uri.file(resource));
|
||||||
let doc = await vscode.workspace.openTextDocument(resource);
|
|
||||||
vscode.window.showTextDocument(doc);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
vscode.window.showErrorMessage(loc.openNotebookError(resource, e instanceof Error ? e.message : e));
|
vscode.window.showErrorMessage(loc.openNotebookError(resource, e instanceof Error ? e.message : e));
|
||||||
|
|||||||
@@ -34,6 +34,13 @@ export const localhostName = 'localhost';
|
|||||||
export const localhostTitle = localize('managePackages.localhost', "localhost");
|
export const localhostTitle = localize('managePackages.localhost', "localhost");
|
||||||
export const PackageNotFoundError = localize('managePackages.packageNotFound', "Could not find the specified package");
|
export const PackageNotFoundError = localize('managePackages.packageNotFound', "Could not find the specified package");
|
||||||
|
|
||||||
|
export const python3DisplayName = 'Python 3';
|
||||||
|
export const pysparkDisplayName = 'PySpark';
|
||||||
|
export const sparkScalaDisplayName = 'Spark | Scala';
|
||||||
|
export const sparkRDisplayName = 'Spark | R';
|
||||||
|
export const powershellDisplayName = 'PowerShell';
|
||||||
|
export const allKernelsName = 'All Kernels';
|
||||||
|
|
||||||
export const visitedNotebooksMementoKey = 'notebooks.visited';
|
export const visitedNotebooksMementoKey = 'notebooks.visited';
|
||||||
|
|
||||||
export enum BuiltInCommands {
|
export enum BuiltInCommands {
|
||||||
|
|||||||
@@ -294,3 +294,7 @@ function decorate(decorator: (fn: Function, key: string) => Function): Function
|
|||||||
descriptor[fnKey] = decorator(fn, key);
|
descriptor[fnKey] = decorator(fn, key);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDropdownValue(dropdown: azdata.DropDownComponent): string {
|
||||||
|
return (typeof dropdown.value === 'string') ? dropdown.value : dropdown.value.name;
|
||||||
|
}
|
||||||
|
|||||||
33
extensions/notebook/src/dialog/configurePython/basePage.ts
Normal file
33
extensions/notebook/src/dialog/configurePython/basePage.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as azdata from 'azdata';
|
||||||
|
import { ConfigurePythonModel, ConfigurePythonWizard } from './configurePythonWizard';
|
||||||
|
import { ApiWrapper } from '../../common/apiWrapper';
|
||||||
|
|
||||||
|
export abstract class BasePage {
|
||||||
|
|
||||||
|
constructor(protected readonly apiWrapper: ApiWrapper,
|
||||||
|
protected readonly instance: ConfigurePythonWizard,
|
||||||
|
protected readonly wizardPage: azdata.window.WizardPage,
|
||||||
|
protected readonly model: ConfigurePythonModel,
|
||||||
|
protected readonly view: azdata.ModelView) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method constructs all the elements of the page.
|
||||||
|
*/
|
||||||
|
public async abstract initialize(): Promise<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when the user is entering the page.
|
||||||
|
*/
|
||||||
|
public async abstract onPageEnter(): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when the user is leaving the page.
|
||||||
|
*/
|
||||||
|
public async abstract onPageLeave(): Promise<boolean>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { BasePage } from './basePage';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import { JupyterServerInstallation } from '../../jupyter/jupyterServerInstallation';
|
||||||
|
import { PythonPathInfo } from '../pythonPathLookup';
|
||||||
|
import * as utils from '../../common/utils';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class ConfigurePathPage extends BasePage {
|
||||||
|
private readonly BrowseButtonText = localize('configurePython.browseButtonText', "Browse");
|
||||||
|
private readonly LocationTextBoxTitle = localize('configurePython.locationTextBoxText', "Python Install Location");
|
||||||
|
private readonly SelectFileLabel = localize('configurePython.selectFileLabel', "Select");
|
||||||
|
|
||||||
|
private pythonLocationDropdown: azdata.DropDownComponent;
|
||||||
|
private pythonDropdownLoader: azdata.LoadingComponent;
|
||||||
|
private browseButton: azdata.ButtonComponent;
|
||||||
|
private newInstallButton: azdata.RadioButtonComponent;
|
||||||
|
private existingInstallButton: azdata.RadioButtonComponent;
|
||||||
|
|
||||||
|
private usingCustomPath: boolean = false;
|
||||||
|
|
||||||
|
public async initialize(): Promise<boolean> {
|
||||||
|
this.pythonLocationDropdown = this.view.modelBuilder.dropDown()
|
||||||
|
.withProperties<azdata.DropDownProperties>({
|
||||||
|
value: undefined,
|
||||||
|
values: [],
|
||||||
|
width: '100%'
|
||||||
|
}).component();
|
||||||
|
this.pythonDropdownLoader = this.view.modelBuilder.loadingComponent()
|
||||||
|
.withItem(this.pythonLocationDropdown)
|
||||||
|
.withProperties<azdata.LoadingComponentProperties>({
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
.component();
|
||||||
|
|
||||||
|
this.browseButton = this.view.modelBuilder.button()
|
||||||
|
.withProperties<azdata.ButtonProperties>({
|
||||||
|
label: this.BrowseButtonText,
|
||||||
|
width: '70px'
|
||||||
|
}).component();
|
||||||
|
this.browseButton.onDidClick(() => this.handleBrowse());
|
||||||
|
|
||||||
|
this.createInstallRadioButtons(this.view.modelBuilder, this.model.useExistingPython);
|
||||||
|
|
||||||
|
let formModel = this.view.modelBuilder.formContainer()
|
||||||
|
.withFormItems([{
|
||||||
|
component: this.newInstallButton,
|
||||||
|
title: localize('configurePython.installationType', "Installation Type")
|
||||||
|
}, {
|
||||||
|
component: this.existingInstallButton,
|
||||||
|
title: ''
|
||||||
|
}, {
|
||||||
|
component: this.pythonDropdownLoader,
|
||||||
|
title: this.LocationTextBoxTitle
|
||||||
|
}, {
|
||||||
|
component: this.browseButton,
|
||||||
|
title: ''
|
||||||
|
}]).component();
|
||||||
|
|
||||||
|
await this.view.initializeModel(formModel);
|
||||||
|
|
||||||
|
await this.updatePythonPathsDropdown(this.model.useExistingPython);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onPageEnter(): Promise<void> {
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onPageLeave(): Promise<boolean> {
|
||||||
|
let pythonLocation = utils.getDropdownValue(this.pythonLocationDropdown);
|
||||||
|
if (!pythonLocation || pythonLocation.length === 0) {
|
||||||
|
this.instance.showErrorMessage(this.instance.InvalidLocationMsg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.model.pythonLocation = pythonLocation;
|
||||||
|
this.model.useExistingPython = !!this.existingInstallButton.checked;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updatePythonPathsDropdown(useExistingPython: boolean): Promise<void> {
|
||||||
|
this.pythonDropdownLoader.loading = true;
|
||||||
|
try {
|
||||||
|
let pythonPaths: PythonPathInfo[];
|
||||||
|
let dropdownValues: azdata.CategoryValue[];
|
||||||
|
if (useExistingPython) {
|
||||||
|
pythonPaths = await this.model.pythonPathsPromise;
|
||||||
|
if (pythonPaths && pythonPaths.length > 0) {
|
||||||
|
dropdownValues = pythonPaths.map(path => {
|
||||||
|
return {
|
||||||
|
displayName: localize('configurePythyon.dropdownPathLabel', "{0} (Python {1})", path.installDir, path.version),
|
||||||
|
name: path.installDir
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dropdownValues = [{
|
||||||
|
displayName: localize('configurePythyon.noVersionsFound', "No supported Python versions found."),
|
||||||
|
name: ''
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let defaultPath = JupyterServerInstallation.DefaultPythonLocation;
|
||||||
|
dropdownValues = [{
|
||||||
|
displayName: localize('configurePythyon.defaultPathLabel', "{0} (Default)", defaultPath),
|
||||||
|
name: defaultPath
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.usingCustomPath = false;
|
||||||
|
await this.pythonLocationDropdown.updateProperties({
|
||||||
|
value: dropdownValues[0],
|
||||||
|
values: dropdownValues
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.pythonDropdownLoader.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createInstallRadioButtons(modelBuilder: azdata.ModelBuilder, useExistingPython: boolean): void {
|
||||||
|
let buttonGroup = 'installationType';
|
||||||
|
this.newInstallButton = modelBuilder.radioButton()
|
||||||
|
.withProperties<azdata.RadioButtonProperties>({
|
||||||
|
name: buttonGroup,
|
||||||
|
label: localize('configurePython.newInstall', "New Python installation"),
|
||||||
|
checked: !useExistingPython
|
||||||
|
}).component();
|
||||||
|
this.newInstallButton.onDidClick(() => {
|
||||||
|
this.existingInstallButton.checked = false;
|
||||||
|
this.updatePythonPathsDropdown(false)
|
||||||
|
.catch(err => {
|
||||||
|
this.instance.showErrorMessage(utils.getErrorMessage(err));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.existingInstallButton = modelBuilder.radioButton()
|
||||||
|
.withProperties<azdata.RadioButtonProperties>({
|
||||||
|
name: buttonGroup,
|
||||||
|
label: localize('configurePython.existingInstall', "Use existing Python installation"),
|
||||||
|
checked: useExistingPython
|
||||||
|
}).component();
|
||||||
|
this.existingInstallButton.onDidClick(() => {
|
||||||
|
this.newInstallButton.checked = false;
|
||||||
|
this.updatePythonPathsDropdown(true)
|
||||||
|
.catch(err => {
|
||||||
|
this.instance.showErrorMessage(utils.getErrorMessage(err));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleBrowse(): Promise<void> {
|
||||||
|
let options: vscode.OpenDialogOptions = {
|
||||||
|
defaultUri: vscode.Uri.file(utils.getUserHome()),
|
||||||
|
canSelectFiles: false,
|
||||||
|
canSelectFolders: true,
|
||||||
|
canSelectMany: false,
|
||||||
|
openLabel: this.SelectFileLabel
|
||||||
|
};
|
||||||
|
|
||||||
|
let fileUris: vscode.Uri[] = await this.apiWrapper.showOpenDialog(options);
|
||||||
|
if (fileUris?.length > 0 && fileUris[0]) {
|
||||||
|
let existingValues = <azdata.CategoryValue[]>this.pythonLocationDropdown.values;
|
||||||
|
let filePath = fileUris[0].fsPath;
|
||||||
|
let newValue = {
|
||||||
|
displayName: localize('configurePythyon.customPathLabel', "{0} (Custom)", filePath),
|
||||||
|
name: filePath
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.usingCustomPath) {
|
||||||
|
existingValues[0] = newValue;
|
||||||
|
} else {
|
||||||
|
existingValues.unshift(newValue);
|
||||||
|
this.usingCustomPath = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.pythonLocationDropdown.updateProperties({
|
||||||
|
value: existingValues[0],
|
||||||
|
values: existingValues
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,12 +7,12 @@ import * as vscode from 'vscode';
|
|||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import * as utils from '../common/utils';
|
import * as utils from '../../common/utils';
|
||||||
|
|
||||||
import { JupyterServerInstallation } from '../jupyter/jupyterServerInstallation';
|
import { JupyterServerInstallation } from '../../jupyter/jupyterServerInstallation';
|
||||||
import { ApiWrapper } from '../common/apiWrapper';
|
import { ApiWrapper } from '../../common/apiWrapper';
|
||||||
import { Deferred } from '../common/promise';
|
import { Deferred } from '../../common/promise';
|
||||||
import { PythonPathLookup, PythonPathInfo } from './pythonPathLookup';
|
import { PythonPathLookup, PythonPathInfo } from '../pythonPathLookup';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 azdata from 'azdata';
|
||||||
|
import { BasePage } from './basePage';
|
||||||
|
import { ConfigurePathPage } from './configurePathPage';
|
||||||
|
import { PickPackagesPage } from './pickPackagesPage';
|
||||||
|
import { JupyterServerInstallation, PythonPkgDetails, PythonInstallSettings } from '../../jupyter/jupyterServerInstallation';
|
||||||
|
import * as utils from '../../common/utils';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import { Deferred } from '../../common/promise';
|
||||||
|
import { PythonPathInfo, PythonPathLookup } from '../pythonPathLookup';
|
||||||
|
import { ApiWrapper } from '../../common/apiWrapper';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export interface ConfigurePythonModel {
|
||||||
|
kernelName: string;
|
||||||
|
pythonLocation: string;
|
||||||
|
useExistingPython: boolean;
|
||||||
|
pythonPathsPromise: Promise<PythonPathInfo[]>;
|
||||||
|
packagesToInstall: PythonPkgDetails[];
|
||||||
|
installation: JupyterServerInstallation;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ConfigurePythonWizard {
|
||||||
|
private readonly InstallButtonText = localize('configurePython.okButtonText', "Install");
|
||||||
|
public readonly InvalidLocationMsg = localize('configurePython.invalidLocationMsg', "The specified install location is invalid.");
|
||||||
|
private readonly PythonNotFoundMsg = localize('configurePython.pythonNotFoundMsg', "No Python installation was found at the specified location.");
|
||||||
|
|
||||||
|
private _wizard: azdata.window.Wizard;
|
||||||
|
private model: ConfigurePythonModel;
|
||||||
|
|
||||||
|
private _setupComplete: Deferred<void>;
|
||||||
|
private pythonPathsPromise: Promise<PythonPathInfo[]>;
|
||||||
|
|
||||||
|
constructor(private apiWrapper: ApiWrapper, private jupyterInstallation: JupyterServerInstallation) {
|
||||||
|
this._setupComplete = new Deferred<void>();
|
||||||
|
this.pythonPathsPromise = (new PythonPathLookup()).getSuggestions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get wizard(): azdata.window.Wizard {
|
||||||
|
return this._wizard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get setupComplete(): Promise<void> {
|
||||||
|
return this._setupComplete.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async start(kernelName?: string, rejectOnCancel?: boolean, ...args: any[]): Promise<void> {
|
||||||
|
this.model = <ConfigurePythonModel>{
|
||||||
|
kernelName: kernelName,
|
||||||
|
pythonPathsPromise: this.pythonPathsPromise,
|
||||||
|
installation: this.jupyterInstallation,
|
||||||
|
useExistingPython: JupyterServerInstallation.getExistingPythonSetting(this.apiWrapper)
|
||||||
|
};
|
||||||
|
|
||||||
|
let pages: Map<number, BasePage> = new Map<number, BasePage>();
|
||||||
|
|
||||||
|
let wizardTitle: string;
|
||||||
|
if (kernelName) {
|
||||||
|
wizardTitle = localize('configurePython.wizardNameWithKernel', 'Configure Python to run {0} kernel', kernelName);
|
||||||
|
} else {
|
||||||
|
wizardTitle = localize('configurePython.wizardNameWithoutKernel', 'Configure Python to run kernels');
|
||||||
|
}
|
||||||
|
this._wizard = azdata.window.createWizard(wizardTitle);
|
||||||
|
let page0 = azdata.window.createWizardPage(localize('configurePython.page0Name', 'Configure Python Runtime'));
|
||||||
|
let page1 = azdata.window.createWizardPage(localize('configurePython.page1Name', 'Install Dependencies'));
|
||||||
|
|
||||||
|
page0.registerContent(async (view) => {
|
||||||
|
let configurePathPage = new ConfigurePathPage(this.apiWrapper, this, page0, this.model, view);
|
||||||
|
pages.set(0, configurePathPage);
|
||||||
|
await configurePathPage.initialize();
|
||||||
|
await configurePathPage.onPageEnter();
|
||||||
|
});
|
||||||
|
|
||||||
|
page1.registerContent(async (view) => {
|
||||||
|
let pickPackagesPage = new PickPackagesPage(this.apiWrapper, this, page1, this.model, view);
|
||||||
|
pages.set(1, pickPackagesPage);
|
||||||
|
await pickPackagesPage.initialize();
|
||||||
|
});
|
||||||
|
|
||||||
|
this._wizard.doneButton.label = this.InstallButtonText;
|
||||||
|
this._wizard.cancelButton.onClick(() => {
|
||||||
|
if (rejectOnCancel) {
|
||||||
|
this._setupComplete.reject(localize('configurePython.pythonInstallDeclined', "Python installation was declined."));
|
||||||
|
} else {
|
||||||
|
this._setupComplete.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._wizard.onPageChanged(async info => {
|
||||||
|
let newPage = pages.get(info.newPage);
|
||||||
|
if (newPage) {
|
||||||
|
await newPage.onPageEnter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._wizard.registerNavigationValidator(async (info) => {
|
||||||
|
let lastPage = pages.get(info.lastPage);
|
||||||
|
let newPage = pages.get(info.newPage);
|
||||||
|
|
||||||
|
// Hit "next" on last page, so handle submit
|
||||||
|
let nextOnLastPage = !newPage && lastPage instanceof PickPackagesPage;
|
||||||
|
if (nextOnLastPage) {
|
||||||
|
return await this.handlePackageInstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastPage) {
|
||||||
|
let pageValid = await lastPage.onPageLeave();
|
||||||
|
if (!pageValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clearStatusMessage();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
this._wizard.generateScriptButton.hidden = true;
|
||||||
|
this._wizard.pages = [page0, page1];
|
||||||
|
this._wizard.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async close(): Promise<void> {
|
||||||
|
await this._wizard.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public showErrorMessage(errorMsg: string) {
|
||||||
|
this._wizard.message = <azdata.window.DialogMessage>{
|
||||||
|
text: errorMsg,
|
||||||
|
level: azdata.window.MessageLevel.Error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public clearStatusMessage() {
|
||||||
|
this._wizard.message = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handlePackageInstall(): Promise<boolean> {
|
||||||
|
let pythonLocation = this.model.pythonLocation;
|
||||||
|
let useExistingPython = this.model.useExistingPython;
|
||||||
|
try {
|
||||||
|
let isValid = await this.isFileValid(pythonLocation);
|
||||||
|
if (!isValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useExistingPython) {
|
||||||
|
let exePath = JupyterServerInstallation.getPythonExePath(pythonLocation, true);
|
||||||
|
let pythonExists = await utils.exists(exePath);
|
||||||
|
if (!pythonExists) {
|
||||||
|
this.showErrorMessage(this.PythonNotFoundMsg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.showErrorMessage(utils.getErrorMessage(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't wait on installation, since there's currently no Cancel functionality
|
||||||
|
let installSettings: PythonInstallSettings = {
|
||||||
|
installPath: pythonLocation,
|
||||||
|
existingPython: useExistingPython,
|
||||||
|
specificPackages: this.model.packagesToInstall
|
||||||
|
};
|
||||||
|
this.jupyterInstallation.startInstallProcess(false, installSettings)
|
||||||
|
.then(() => {
|
||||||
|
this._setupComplete.resolve();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
this._setupComplete.reject(utils.getErrorMessage(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async isFileValid(pythonLocation: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const stats = await fs.stat(pythonLocation);
|
||||||
|
if (stats.isFile()) {
|
||||||
|
this.showErrorMessage(this.InvalidLocationMsg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore error if folder doesn't exist, since it will be
|
||||||
|
// created during installation
|
||||||
|
if (err.code !== 'ENOENT') {
|
||||||
|
this.showErrorMessage(err.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as azdata from 'azdata';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import { BasePage } from './basePage';
|
||||||
|
import { JupyterServerInstallation, PythonPkgDetails } from '../../jupyter/jupyterServerInstallation';
|
||||||
|
import { python3DisplayName, pysparkDisplayName, sparkScalaDisplayName, sparkRDisplayName, powershellDisplayName, allKernelsName } from '../../common/constants';
|
||||||
|
import { getDropdownValue } from '../../common/utils';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class PickPackagesPage extends BasePage {
|
||||||
|
private kernelLabel: azdata.TextComponent | undefined;
|
||||||
|
private kernelDropdown: azdata.DropDownComponent | undefined;
|
||||||
|
private requiredPackagesTable: azdata.DeclarativeTableComponent;
|
||||||
|
private packageTableSpinner: azdata.LoadingComponent;
|
||||||
|
|
||||||
|
private installedPackagesPromise: Promise<PythonPkgDetails[]>;
|
||||||
|
private installedPackages: PythonPkgDetails[];
|
||||||
|
|
||||||
|
public async initialize(): Promise<boolean> {
|
||||||
|
if (this.model.kernelName) {
|
||||||
|
// Wizard was started for a specific kernel, so don't populate any other options
|
||||||
|
this.kernelLabel = this.view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
||||||
|
value: this.model.kernelName
|
||||||
|
}).component();
|
||||||
|
} else {
|
||||||
|
let dropdownValues = [python3DisplayName, pysparkDisplayName, sparkScalaDisplayName, sparkRDisplayName, powershellDisplayName, allKernelsName];
|
||||||
|
this.kernelDropdown = this.view.modelBuilder.dropDown().withProperties<azdata.DropDownProperties>({
|
||||||
|
value: dropdownValues[0],
|
||||||
|
values: dropdownValues,
|
||||||
|
width: '300px'
|
||||||
|
}).component();
|
||||||
|
this.kernelDropdown.onValueChanged(async value => {
|
||||||
|
await this.updateRequiredPackages(value.selected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requiredPackagesTable = this.view.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
||||||
|
columns: [{
|
||||||
|
displayName: localize('configurePython.pkgNameColumn', "Name"),
|
||||||
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
|
isReadOnly: true,
|
||||||
|
width: '200px'
|
||||||
|
}, {
|
||||||
|
displayName: localize('configurePython.existingVersionColumn', "Existing Version"),
|
||||||
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
|
isReadOnly: true,
|
||||||
|
width: '200px'
|
||||||
|
}, {
|
||||||
|
displayName: localize('configurePython.requiredVersionColumn', "Required Version"),
|
||||||
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
|
isReadOnly: true,
|
||||||
|
width: '200px'
|
||||||
|
}],
|
||||||
|
data: [[]]
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.packageTableSpinner = this.view.modelBuilder.loadingComponent().withItem(this.requiredPackagesTable).component();
|
||||||
|
|
||||||
|
let formModel = this.view.modelBuilder.formContainer()
|
||||||
|
.withFormItems([{
|
||||||
|
component: this.kernelDropdown ?? this.kernelLabel,
|
||||||
|
title: localize('configurePython.kernelLabel', "Kernel")
|
||||||
|
}, {
|
||||||
|
component: this.packageTableSpinner,
|
||||||
|
title: localize('configurePython.requiredDependencies', "Install required kernel dependencies")
|
||||||
|
}]).component();
|
||||||
|
await this.view.initializeModel(formModel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onPageEnter(): Promise<void> {
|
||||||
|
let pythonExe = JupyterServerInstallation.getPythonExePath(this.model.pythonLocation, this.model.useExistingPython);
|
||||||
|
this.installedPackagesPromise = this.model.installation.getInstalledPipPackages(pythonExe);
|
||||||
|
this.installedPackages = undefined;
|
||||||
|
|
||||||
|
if (this.kernelDropdown) {
|
||||||
|
if (this.model.kernelName) {
|
||||||
|
this.kernelDropdown.value = this.model.kernelName;
|
||||||
|
} else {
|
||||||
|
this.model.kernelName = getDropdownValue(this.kernelDropdown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.updateRequiredPackages(this.model.kernelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onPageLeave(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateRequiredPackages(kernelName: string): Promise<void> {
|
||||||
|
this.packageTableSpinner.loading = true;
|
||||||
|
try {
|
||||||
|
let pkgVersionMap = new Map<string, { currentVersion: string, newVersion: string }>();
|
||||||
|
|
||||||
|
// Fetch list of required packages for the specified kernel
|
||||||
|
let requiredPackages = JupyterServerInstallation.getRequiredPackagesForKernel(kernelName);
|
||||||
|
requiredPackages.forEach(pkg => {
|
||||||
|
pkgVersionMap.set(pkg.name, { currentVersion: undefined, newVersion: pkg.version });
|
||||||
|
});
|
||||||
|
|
||||||
|
// For each required package, check if there is another version of that package already installed
|
||||||
|
if (!this.installedPackages) {
|
||||||
|
this.installedPackages = await this.installedPackagesPromise;
|
||||||
|
}
|
||||||
|
this.installedPackages.forEach(pkg => {
|
||||||
|
let info = pkgVersionMap.get(pkg.name);
|
||||||
|
if (info) {
|
||||||
|
info.currentVersion = pkg.version;
|
||||||
|
pkgVersionMap.set(pkg.name, info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pkgVersionMap.size > 0) {
|
||||||
|
let packageData = [];
|
||||||
|
for (let [key, value] of pkgVersionMap.entries()) {
|
||||||
|
packageData.push([key, value.currentVersion ?? '-', value.newVersion]);
|
||||||
|
}
|
||||||
|
this.requiredPackagesTable.data = packageData;
|
||||||
|
this.model.packagesToInstall = requiredPackages;
|
||||||
|
} else {
|
||||||
|
this.instance.showErrorMessage(localize('msgUnsupportedKernel', "Could not retrieve packages for unsupported kernel {0}", kernelName));
|
||||||
|
this.requiredPackagesTable.data = [['-', '-', '-']];
|
||||||
|
this.model.packagesToInstall = undefined;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.packageTableSpinner.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -133,7 +133,7 @@ export class ManagePackagesDialogModel {
|
|||||||
* Returns the default location
|
* Returns the default location
|
||||||
*/
|
*/
|
||||||
public get defaultLocation(): string | undefined {
|
public get defaultLocation(): string | undefined {
|
||||||
return this.options.defaultLocation || this.targetLocationTypes.length > 0 ? this.targetLocationTypes[0] : undefined;
|
return this.options.defaultLocation || (this.targetLocationTypes.length > 0 ? this.targetLocationTypes[0] : undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { ApiWrapper } from '../common/apiWrapper';
|
|||||||
import { LocalJupyterServerManager, ServerInstanceFactory } from './jupyterServerManager';
|
import { LocalJupyterServerManager, ServerInstanceFactory } from './jupyterServerManager';
|
||||||
import { NotebookCompletionItemProvider } from '../intellisense/completionItemProvider';
|
import { NotebookCompletionItemProvider } from '../intellisense/completionItemProvider';
|
||||||
import { JupyterNotebookProvider } from './jupyterNotebookProvider';
|
import { JupyterNotebookProvider } from './jupyterNotebookProvider';
|
||||||
import { ConfigurePythonDialog } from '../dialog/configurePythonDialog';
|
import { ConfigurePythonWizard } from '../dialog/configurePython/configurePythonWizard';
|
||||||
import CodeAdapter from '../prompts/adapter';
|
import CodeAdapter from '../prompts/adapter';
|
||||||
import { ManagePackagesDialog } from '../dialog/managePackages/managePackagesDialog';
|
import { ManagePackagesDialog } from '../dialog/managePackages/managePackagesDialog';
|
||||||
import { IPackageManageProvider } from '../types';
|
import { IPackageManageProvider } from '../types';
|
||||||
@@ -30,6 +30,7 @@ import { LocalPipPackageManageProvider } from './localPipPackageManageProvider';
|
|||||||
import { LocalCondaPackageManageProvider } from './localCondaPackageManageProvider';
|
import { LocalCondaPackageManageProvider } from './localCondaPackageManageProvider';
|
||||||
import { ManagePackagesDialogModel, ManagePackageDialogOptions } from '../dialog/managePackages/managePackagesDialogModel';
|
import { ManagePackagesDialogModel, ManagePackageDialogOptions } from '../dialog/managePackages/managePackagesDialogModel';
|
||||||
import { PiPyClient } from './pipyClient';
|
import { PiPyClient } from './pipyClient';
|
||||||
|
import { ConfigurePythonDialog } from '../dialog/configurePython/configurePythonDialog';
|
||||||
|
|
||||||
let untitledCounter = 0;
|
let untitledCounter = 0;
|
||||||
|
|
||||||
@@ -250,10 +251,21 @@ export class JupyterController implements vscode.Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public doConfigurePython(jupyterInstaller: JupyterServerInstallation): void {
|
public doConfigurePython(jupyterInstaller: JupyterServerInstallation): void {
|
||||||
let pythonDialog = new ConfigurePythonDialog(this.apiWrapper, jupyterInstaller);
|
let enablePreviewFeatures = this.apiWrapper.getConfiguration('workbench').get('enablePreviewFeatures');
|
||||||
pythonDialog.showDialog().catch((err: any) => {
|
if (enablePreviewFeatures) {
|
||||||
this.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
|
let pythonWizard = new ConfigurePythonWizard(this.apiWrapper, jupyterInstaller);
|
||||||
});
|
pythonWizard.start().catch((err: any) => {
|
||||||
|
this.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
|
||||||
|
});
|
||||||
|
pythonWizard.setupComplete.catch((err: any) => {
|
||||||
|
this.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let pythonDialog = new ConfigurePythonDialog(this.apiWrapper, jupyterInstaller);
|
||||||
|
pythonDialog.showDialog().catch((err: any) => {
|
||||||
|
this.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get jupyterInstallation() {
|
public get jupyterInstallation() {
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export class JupyterNotebookManager implements nb.NotebookManager, vscode.Dispos
|
|||||||
this._sessionManager = sessionManager || new JupyterSessionManager(pythonEnvVarPath);
|
this._sessionManager = sessionManager || new JupyterSessionManager(pythonEnvVarPath);
|
||||||
this._serverManager.onServerStarted(() => {
|
this._serverManager.onServerStarted(() => {
|
||||||
this.setServerSettings(this._serverManager.serverSettings);
|
this.setServerSettings(this._serverManager.serverSettings);
|
||||||
|
this._sessionManager.installation = this._serverManager.instanceOptions.install;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
public get contentManager(): nb.ContentManager {
|
public get contentManager(): nb.ContentManager {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ import * as constants from '../common/constants';
|
|||||||
import * as utils from '../common/utils';
|
import * as utils from '../common/utils';
|
||||||
import { OutputChannel, ConfigurationTarget, window } from 'vscode';
|
import { OutputChannel, ConfigurationTarget, window } from 'vscode';
|
||||||
import { Deferred } from '../common/promise';
|
import { Deferred } from '../common/promise';
|
||||||
import { ConfigurePythonDialog } from '../dialog/configurePythonDialog';
|
import { ConfigurePythonWizard } from '../dialog/configurePython/configurePythonWizard';
|
||||||
import { IPrompter, IQuestion, QuestionTypes } from '../prompts/question';
|
import { IPrompter, IQuestion, QuestionTypes } from '../prompts/question';
|
||||||
import CodeAdapter from '../prompts/adapter';
|
import CodeAdapter from '../prompts/adapter';
|
||||||
|
import { ConfigurePythonDialog } from '../dialog/configurePython/configurePythonDialog';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
const msgInstallPkgProgress = localize('msgInstallPkgProgress', "Notebook dependencies installation is in progress");
|
const msgInstallPkgProgress = localize('msgInstallPkgProgress', "Notebook dependencies installation is in progress");
|
||||||
@@ -39,10 +40,15 @@ function msgDependenciesInstallationFailed(errorMessage: string): string { retur
|
|||||||
function msgDownloadPython(platform: string, pythonDownloadUrl: string): string { return localize('msgDownloadPython', "Downloading local python for platform: {0} to {1}", platform, pythonDownloadUrl); }
|
function msgDownloadPython(platform: string, pythonDownloadUrl: string): string { return localize('msgDownloadPython', "Downloading local python for platform: {0} to {1}", platform, pythonDownloadUrl); }
|
||||||
function msgPackageRetrievalFailed(errorMessage: string): string { return localize('msgPackageRetrievalFailed', "Encountered an error when trying to retrieve list of installed packages: {0}", errorMessage); }
|
function msgPackageRetrievalFailed(errorMessage: string): string { return localize('msgPackageRetrievalFailed', "Encountered an error when trying to retrieve list of installed packages: {0}", errorMessage); }
|
||||||
|
|
||||||
|
export interface PythonInstallSettings {
|
||||||
|
installPath: string;
|
||||||
|
existingPython: boolean;
|
||||||
|
specificPackages?: PythonPkgDetails[];
|
||||||
|
}
|
||||||
export interface IJupyterServerInstallation {
|
export interface IJupyterServerInstallation {
|
||||||
installCondaPackages(packages: PythonPkgDetails[], useMinVersion: boolean): Promise<void>;
|
installCondaPackages(packages: PythonPkgDetails[], useMinVersion: boolean): Promise<void>;
|
||||||
configurePackagePaths(): Promise<void>;
|
configurePackagePaths(): Promise<void>;
|
||||||
startInstallProcess(forceInstall: boolean, installSettings?: { installPath: string, existingPython: boolean }): Promise<void>;
|
startInstallProcess(forceInstall: boolean, installSettings?: PythonInstallSettings): Promise<void>;
|
||||||
getInstalledPipPackages(): Promise<PythonPkgDetails[]>;
|
getInstalledPipPackages(): Promise<PythonPkgDetails[]>;
|
||||||
getInstalledCondaPackages(): Promise<PythonPkgDetails[]>;
|
getInstalledCondaPackages(): Promise<PythonPkgDetails[]>;
|
||||||
uninstallCondaPackages(packages: PythonPkgDetails[]): Promise<void>;
|
uninstallCondaPackages(packages: PythonPkgDetails[]): Promise<void>;
|
||||||
@@ -66,7 +72,6 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
|
|
||||||
private _pythonInstallationPath: string;
|
private _pythonInstallationPath: string;
|
||||||
private _pythonExecutable: string;
|
private _pythonExecutable: string;
|
||||||
private _pythonPackageDir: string;
|
|
||||||
private _usingExistingPython: boolean;
|
private _usingExistingPython: boolean;
|
||||||
private _usingConda: boolean;
|
private _usingConda: boolean;
|
||||||
|
|
||||||
@@ -104,6 +109,8 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
private readonly _expectedCondaPipPackages = this._commonPipPackages;
|
private readonly _expectedCondaPipPackages = this._commonPipPackages;
|
||||||
private readonly _expectedCondaPackages: PythonPkgDetails[];
|
private readonly _expectedCondaPackages: PythonPkgDetails[];
|
||||||
|
|
||||||
|
private _kernelSetupCache: Map<string, boolean>;
|
||||||
|
|
||||||
constructor(extensionPath: string, outputChannel: OutputChannel, apiWrapper: ApiWrapper, pythonInstallationPath?: string) {
|
constructor(extensionPath: string, outputChannel: OutputChannel, apiWrapper: ApiWrapper, pythonInstallationPath?: string) {
|
||||||
this.extensionPath = extensionPath;
|
this.extensionPath = extensionPath;
|
||||||
this.outputChannel = outputChannel;
|
this.outputChannel = outputChannel;
|
||||||
@@ -120,9 +127,11 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
} else {
|
} else {
|
||||||
this._expectedCondaPackages = this._commonPackages;
|
this._expectedCondaPackages = this._commonPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._kernelSetupCache = new Map<string, boolean>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async installDependencies(backgroundOperation: azdata.BackgroundOperation, forceInstall: boolean): Promise<void> {
|
private async installDependencies(backgroundOperation: azdata.BackgroundOperation, forceInstall: boolean, specificPackages?: PythonPkgDetails[]): Promise<void> {
|
||||||
if (!(await utils.exists(this._pythonExecutable)) || forceInstall || this._usingExistingPython) {
|
if (!(await utils.exists(this._pythonExecutable)) || forceInstall || this._usingExistingPython) {
|
||||||
window.showInformationMessage(msgInstallPkgStart);
|
window.showInformationMessage(msgInstallPkgStart);
|
||||||
|
|
||||||
@@ -132,12 +141,7 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.installPythonPackage(backgroundOperation, this._usingExistingPython, this._pythonInstallationPath, this.outputChannel);
|
await this.installPythonPackage(backgroundOperation, this._usingExistingPython, this._pythonInstallationPath, this.outputChannel);
|
||||||
|
await this.upgradePythonPackages(false, forceInstall, specificPackages);
|
||||||
if (this._usingExistingPython) {
|
|
||||||
await this.upgradePythonPackages(false, forceInstall);
|
|
||||||
} else {
|
|
||||||
await this.installOfflinePipDependencies();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.outputChannel.appendLine(msgDependenciesInstallationFailed(utils.getErrorMessage(err)));
|
this.outputChannel.appendLine(msgDependenciesInstallationFailed(utils.getErrorMessage(err)));
|
||||||
throw err;
|
throw err;
|
||||||
@@ -282,19 +286,13 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
? this._pythonInstallationPath
|
? this._pythonInstallationPath
|
||||||
: path.join(this._pythonInstallationPath, constants.pythonBundleVersion);
|
: path.join(this._pythonInstallationPath, constants.pythonBundleVersion);
|
||||||
|
|
||||||
if (this._usingExistingPython) {
|
|
||||||
this._pythonPackageDir = undefined;
|
|
||||||
} else {
|
|
||||||
this._pythonPackageDir = path.join(pythonSourcePath, 'offlinePackages');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update python paths and properties to reference user's local python.
|
// Update python paths and properties to reference user's local python.
|
||||||
let pythonBinPathSuffix = process.platform === constants.winPlatform ? '' : 'bin';
|
let pythonBinPathSuffix = process.platform === constants.winPlatform ? '' : 'bin';
|
||||||
|
|
||||||
this._pythonExecutable = JupyterServerInstallation.getPythonExePath(this._pythonInstallationPath, this._usingExistingPython);
|
this._pythonExecutable = JupyterServerInstallation.getPythonExePath(this._pythonInstallationPath, this._usingExistingPython);
|
||||||
this.pythonBinPath = path.join(pythonSourcePath, pythonBinPathSuffix);
|
this.pythonBinPath = path.join(pythonSourcePath, pythonBinPathSuffix);
|
||||||
|
|
||||||
this._usingConda = this.checkCondaExists();
|
this._usingConda = this.isCondaInstalled();
|
||||||
|
|
||||||
// Store paths to python libraries required to run jupyter.
|
// Store paths to python libraries required to run jupyter.
|
||||||
this.pythonEnvVarPath = process.env['PATH'];
|
this.pythonEnvVarPath = process.env['PATH'];
|
||||||
@@ -333,11 +331,6 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
delete env['Path']; // Delete extra 'Path' variable for Windows, just in case.
|
delete env['Path']; // Delete extra 'Path' variable for Windows, just in case.
|
||||||
env['PATH'] = this.pythonEnvVarPath;
|
env['PATH'] = this.pythonEnvVarPath;
|
||||||
|
|
||||||
// We don't want Jupyter to know about DOTNET_ROOT, as it's been modified by the liveshare experience
|
|
||||||
// Without this, won't be able to find the ASP.NET bits
|
|
||||||
if (process.env['DOTNET_ROOT']) {
|
|
||||||
delete env['DOTNET_ROOT'];
|
|
||||||
}
|
|
||||||
this.execOptions = {
|
this.execOptions = {
|
||||||
env: env
|
env: env
|
||||||
};
|
};
|
||||||
@@ -365,7 +358,7 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
* @param installSettings Optional parameter that specifies where to install python, and whether the install targets an existing python install.
|
* @param installSettings Optional parameter that specifies where to install python, and whether the install targets an existing python install.
|
||||||
* The previous python path (or the default) is used if a new path is not specified.
|
* The previous python path (or the default) is used if a new path is not specified.
|
||||||
*/
|
*/
|
||||||
public async startInstallProcess(forceInstall: boolean, installSettings?: { installPath: string, existingPython: boolean }): Promise<void> {
|
public async startInstallProcess(forceInstall: boolean, installSettings?: PythonInstallSettings): Promise<void> {
|
||||||
let isPythonRunning: boolean;
|
let isPythonRunning: boolean;
|
||||||
if (installSettings) {
|
if (installSettings) {
|
||||||
isPythonRunning = await this.isPythonRunning(installSettings.installPath, installSettings.existingPython);
|
isPythonRunning = await this.isPythonRunning(installSettings.installPath, installSettings.existingPython);
|
||||||
@@ -404,7 +397,7 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
description: msgTaskName,
|
description: msgTaskName,
|
||||||
isCancelable: false,
|
isCancelable: false,
|
||||||
operation: op => {
|
operation: op => {
|
||||||
this.installDependencies(op, forceInstall)
|
this.installDependencies(op, forceInstall, installSettings?.specificPackages)
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await updateConfig();
|
await updateConfig();
|
||||||
this._installCompletion.resolve();
|
this._installCompletion.resolve();
|
||||||
@@ -432,28 +425,45 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
/**
|
/**
|
||||||
* Opens a dialog for configuring the installation path for the Notebook Python dependencies.
|
* Opens a dialog for configuring the installation path for the Notebook Python dependencies.
|
||||||
*/
|
*/
|
||||||
public async promptForPythonInstall(): Promise<void> {
|
public async promptForPythonInstall(kernelDisplayName: string): Promise<void> {
|
||||||
if (!JupyterServerInstallation.isPythonInstalled(this.apiWrapper)) {
|
if (!JupyterServerInstallation.isPythonInstalled(this.apiWrapper)) {
|
||||||
let pythonDialog = new ConfigurePythonDialog(this.apiWrapper, this);
|
let enablePreviewFeatures = this.apiWrapper.getConfiguration('workbench').get('enablePreviewFeatures');
|
||||||
return pythonDialog.showDialog(true);
|
if (enablePreviewFeatures) {
|
||||||
|
let pythonWizard = new ConfigurePythonWizard(this.apiWrapper, this);
|
||||||
|
await pythonWizard.start(kernelDisplayName, true);
|
||||||
|
return pythonWizard.setupComplete;
|
||||||
|
} else {
|
||||||
|
let pythonDialog = new ConfigurePythonDialog(this.apiWrapper, this);
|
||||||
|
return pythonDialog.showDialog(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompts user to upgrade certain python packages if they're below the minimum expected version.
|
* Prompts user to upgrade certain python packages if they're below the minimum expected version.
|
||||||
*/
|
*/
|
||||||
public async promptForPackageUpgrade(): Promise<void> {
|
public async promptForPackageUpgrade(kernelName: string): Promise<void> {
|
||||||
if (this._installInProgress) {
|
if (this._installInProgress) {
|
||||||
this.apiWrapper.showInfoMessage(msgWaitingForInstall);
|
this.apiWrapper.showInfoMessage(msgWaitingForInstall);
|
||||||
return this._installCompletion.promise;
|
return this._installCompletion.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let requiredPackages: PythonPkgDetails[];
|
||||||
|
let enablePreviewFeatures = this.apiWrapper.getConfiguration('workbench').get('enablePreviewFeatures');
|
||||||
|
if (enablePreviewFeatures) {
|
||||||
|
if (this._kernelSetupCache.get(kernelName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requiredPackages = JupyterServerInstallation.getRequiredPackagesForKernel(kernelName);
|
||||||
|
}
|
||||||
|
|
||||||
this._installInProgress = true;
|
this._installInProgress = true;
|
||||||
this._installCompletion = new Deferred<void>();
|
this._installCompletion = new Deferred<void>();
|
||||||
this.upgradePythonPackages(true, false)
|
this.upgradePythonPackages(true, false, requiredPackages)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this._installCompletion.resolve();
|
this._installCompletion.resolve();
|
||||||
this._installInProgress = false;
|
this._installInProgress = false;
|
||||||
|
this._kernelSetupCache.set(kernelName, true);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
let errorMsg = msgDependenciesInstallationFailed(utils.getErrorMessage(err));
|
let errorMsg = msgDependenciesInstallationFailed(utils.getErrorMessage(err));
|
||||||
@@ -463,10 +473,14 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
return this._installCompletion.promise;
|
return this._installCompletion.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async upgradePythonPackages(promptForUpgrade: boolean, forceInstall: boolean): Promise<void> {
|
private async upgradePythonPackages(promptForUpgrade: boolean, forceInstall: boolean, specificPackages?: PythonPkgDetails[]): Promise<void> {
|
||||||
let expectedCondaPackages: PythonPkgDetails[];
|
let expectedCondaPackages: PythonPkgDetails[];
|
||||||
let expectedPipPackages: PythonPkgDetails[];
|
let expectedPipPackages: PythonPkgDetails[];
|
||||||
if (this._usingConda) {
|
if (specificPackages) {
|
||||||
|
// Always install generic packages with pip, since conda may not have them.
|
||||||
|
expectedCondaPackages = [];
|
||||||
|
expectedPipPackages = specificPackages;
|
||||||
|
} else if (this._usingConda) {
|
||||||
expectedCondaPackages = this._expectedCondaPackages;
|
expectedCondaPackages = this._expectedCondaPackages;
|
||||||
expectedPipPackages = this._expectedCondaPipPackages;
|
expectedPipPackages = this._expectedCondaPipPackages;
|
||||||
} else {
|
} else {
|
||||||
@@ -515,9 +529,12 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
if (promptForUpgrade) {
|
if (promptForUpgrade) {
|
||||||
doUpgrade = await this._prompter.promptSingle<boolean>(<IQuestion>{
|
doUpgrade = await this._prompter.promptSingle<boolean>(<IQuestion>{
|
||||||
type: QuestionTypes.confirm,
|
type: QuestionTypes.confirm,
|
||||||
message: localize('confirmPackageUpgrade', "Some installed python packages need to be upgraded. Would you like to upgrade them now?"),
|
message: localize('confirmPackageUpgrade', "Some required python packages need to be installed. Would you like to install them now?"),
|
||||||
default: true
|
default: true
|
||||||
});
|
});
|
||||||
|
if (!doUpgrade) {
|
||||||
|
throw new Error(localize('configurePython.packageInstallDeclined', "Package installation was declined."));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
doUpgrade = true;
|
doUpgrade = true;
|
||||||
}
|
}
|
||||||
@@ -568,9 +585,17 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getInstalledPipPackages(): Promise<PythonPkgDetails[]> {
|
public async getInstalledPipPackages(pythonExePath?: string): Promise<PythonPkgDetails[]> {
|
||||||
try {
|
try {
|
||||||
let cmd = `"${this.pythonExecutable}" -m pip list --format=json`;
|
if (pythonExePath) {
|
||||||
|
if (!fs.existsSync(pythonExePath)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} else if (!JupyterServerInstallation.isPythonInstalled(this.apiWrapper)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let cmd = `"${pythonExePath ?? this.pythonExecutable}" -m pip list --format=json`;
|
||||||
let packagesInfo = await this.executeBufferedCommand(cmd);
|
let packagesInfo = await this.executeBufferedCommand(cmd);
|
||||||
let packagesResult: PythonPkgDetails[] = [];
|
let packagesResult: PythonPkgDetails[] = [];
|
||||||
if (packagesInfo) {
|
if (packagesInfo) {
|
||||||
@@ -603,6 +628,10 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
|
|
||||||
public async getInstalledCondaPackages(): Promise<PythonPkgDetails[]> {
|
public async getInstalledCondaPackages(): Promise<PythonPkgDetails[]> {
|
||||||
try {
|
try {
|
||||||
|
if (!this.isCondaInstalled()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
let condaExe = this.getCondaExePath();
|
let condaExe = this.getCondaExePath();
|
||||||
let cmd = `"${condaExe}" list --json`;
|
let cmd = `"${condaExe}" list --json`;
|
||||||
let packagesInfo = await this.executeBufferedCommand(cmd);
|
let packagesInfo = await this.executeBufferedCommand(cmd);
|
||||||
@@ -642,32 +671,6 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
return this.executeStreamedCommand(cmd);
|
return this.executeStreamedCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async installOfflinePipDependencies(): Promise<void> {
|
|
||||||
// Skip this step if using existing python, since this is for our provided package
|
|
||||||
if (!this._usingExistingPython && process.platform === constants.winPlatform) {
|
|
||||||
this.outputChannel.show(true);
|
|
||||||
this.outputChannel.appendLine(localize('msgInstallStart', "Installing required packages to run Notebooks..."));
|
|
||||||
|
|
||||||
let requirements = path.join(this._pythonPackageDir, 'requirements.txt');
|
|
||||||
let installJupyterCommand = `"${this._pythonExecutable}" -m pip install --no-index -r "${requirements}" --find-links "${this._pythonPackageDir}" --no-warn-script-location`;
|
|
||||||
await this.executeStreamedCommand(installJupyterCommand);
|
|
||||||
|
|
||||||
// Force reinstall pip to update shebangs in pip*.exe files
|
|
||||||
installJupyterCommand = `"${this._pythonExecutable}" -m pip install --force-reinstall --no-index pip --find-links "${this._pythonPackageDir}" --no-warn-script-location`;
|
|
||||||
await this.executeStreamedCommand(installJupyterCommand);
|
|
||||||
|
|
||||||
fs.remove(this._pythonPackageDir, (err: Error) => {
|
|
||||||
if (err) {
|
|
||||||
this.outputChannel.appendLine(err.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.outputChannel.appendLine(localize('msgJupyterInstallDone', "... Jupyter installation complete."));
|
|
||||||
} else {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async executeStreamedCommand(command: string): Promise<void> {
|
public async executeStreamedCommand(command: string): Promise<void> {
|
||||||
await utils.executeStreamedCommand(command, { env: this.execOptions.env }, this.outputChannel);
|
await utils.executeStreamedCommand(command, { env: this.execOptions.env }, this.outputChannel);
|
||||||
}
|
}
|
||||||
@@ -696,7 +699,7 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
return this._usingConda;
|
return this._usingConda;
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkCondaExists(): boolean {
|
private isCondaInstalled(): boolean {
|
||||||
if (!this._usingExistingPython) {
|
if (!this._usingExistingPython) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -805,6 +808,55 @@ export class JupyterServerInstallation implements IJupyterServerInstallation {
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getRequiredPackagesForKernel(kernelName: string): PythonPkgDetails[] {
|
||||||
|
let packages = [{
|
||||||
|
name: 'jupyter',
|
||||||
|
version: '1.0.0'
|
||||||
|
}];
|
||||||
|
switch (kernelName) {
|
||||||
|
case constants.python3DisplayName:
|
||||||
|
break;
|
||||||
|
case constants.pysparkDisplayName:
|
||||||
|
case constants.sparkScalaDisplayName:
|
||||||
|
case constants.sparkRDisplayName:
|
||||||
|
packages.push({
|
||||||
|
name: 'sparkmagic',
|
||||||
|
version: '0.12.9'
|
||||||
|
}, {
|
||||||
|
name: 'pandas',
|
||||||
|
version: '0.24.2'
|
||||||
|
}, {
|
||||||
|
name: 'prose-codeaccelerator',
|
||||||
|
version: '1.3.0'
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case constants.powershellDisplayName:
|
||||||
|
packages.push({
|
||||||
|
name: 'powershell-kernel',
|
||||||
|
version: '0.1.3'
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case constants.allKernelsName:
|
||||||
|
packages.push({
|
||||||
|
name: 'sparkmagic',
|
||||||
|
version: '0.12.9'
|
||||||
|
}, {
|
||||||
|
name: 'pandas',
|
||||||
|
version: '0.24.2'
|
||||||
|
}, {
|
||||||
|
name: 'prose-codeaccelerator',
|
||||||
|
version: '1.3.0'
|
||||||
|
}, {
|
||||||
|
name: 'powershell-kernel',
|
||||||
|
version: '0.1.3'
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return packages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PythonPkgDetails {
|
export interface PythonPkgDetails {
|
||||||
|
|||||||
@@ -56,16 +56,15 @@ export class LocalJupyterServerManager implements nb.ServerManager, vscode.Dispo
|
|||||||
return this.options && this.options.jupyterInstallation;
|
return this.options && this.options.jupyterInstallation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async startServer(): Promise<void> {
|
public async startServer(kernelSpec: nb.IKernelSpec): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (!this._jupyterServer) {
|
if (!this._jupyterServer) {
|
||||||
this._jupyterServer = await this.doStartServer();
|
this._jupyterServer = await this.doStartServer(kernelSpec);
|
||||||
this.options.extensionContext.subscriptions.push(this);
|
this.options.extensionContext.subscriptions.push(this);
|
||||||
let partialSettings = LocalJupyterServerManager.getLocalConnectionSettings(this._jupyterServer.uri);
|
let partialSettings = LocalJupyterServerManager.getLocalConnectionSettings(this._jupyterServer.uri);
|
||||||
this._serverSettings = partialSettings;
|
this._serverSettings = partialSettings;
|
||||||
this._onServerStarted.fire();
|
this._onServerStarted.fire();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// this is caught and notified up the stack, no longer showing a message here
|
// this is caught and notified up the stack, no longer showing a message here
|
||||||
throw error;
|
throw error;
|
||||||
@@ -107,10 +106,10 @@ export class LocalJupyterServerManager implements nb.ServerManager, vscode.Dispo
|
|||||||
return this.options.documentPath;
|
return this.options.documentPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async doStartServer(): Promise<IServerInstance> { // We can't find or create servers until the installation is complete
|
private async doStartServer(kernelSpec: nb.IKernelSpec): Promise<IServerInstance> { // We can't find or create servers until the installation is complete
|
||||||
let installation = this.options.jupyterInstallation;
|
let installation = this.options.jupyterInstallation;
|
||||||
await installation.promptForPythonInstall();
|
await installation.promptForPythonInstall(kernelSpec.display_name);
|
||||||
await installation.promptForPackageUpgrade();
|
await installation.promptForPackageUpgrade(kernelSpec.display_name);
|
||||||
this._apiWrapper.setCommandContext(CommandContext.NotebookPythonInstalled, true);
|
this._apiWrapper.setCommandContext(CommandContext.NotebookPythonInstalled, true);
|
||||||
|
|
||||||
// Calculate the path to use as the notebook-dir for Jupyter based on the path of the uri of the
|
// Calculate the path to use as the notebook-dir for Jupyter based on the path of the uri of the
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const localize = nls.loadMessageBundle();
|
|||||||
|
|
||||||
import { JupyterKernel } from './jupyterKernel';
|
import { JupyterKernel } from './jupyterKernel';
|
||||||
import { Deferred } from '../common/promise';
|
import { Deferred } from '../common/promise';
|
||||||
|
import { JupyterServerInstallation } from './jupyterServerInstallation';
|
||||||
|
|
||||||
const configBase = {
|
const configBase = {
|
||||||
'kernel_python_credentials': {
|
'kernel_python_credentials': {
|
||||||
@@ -66,6 +67,7 @@ export class JupyterSessionManager implements nb.SessionManager {
|
|||||||
private _isReady: boolean;
|
private _isReady: boolean;
|
||||||
private _sessionManager: Session.IManager;
|
private _sessionManager: Session.IManager;
|
||||||
private static _sessions: JupyterSession[] = [];
|
private static _sessions: JupyterSession[] = [];
|
||||||
|
private _installation: JupyterServerInstallation;
|
||||||
|
|
||||||
constructor(private _pythonEnvVarPath?: string) {
|
constructor(private _pythonEnvVarPath?: string) {
|
||||||
this._isReady = false;
|
this._isReady = false;
|
||||||
@@ -84,6 +86,12 @@ export class JupyterSessionManager implements nb.SessionManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set installation(installation: JupyterServerInstallation) {
|
||||||
|
this._installation = installation;
|
||||||
|
JupyterSessionManager._sessions.forEach(session => {
|
||||||
|
session.installation = installation;
|
||||||
|
});
|
||||||
|
}
|
||||||
public get isReady(): boolean {
|
public get isReady(): boolean {
|
||||||
return this._isReady;
|
return this._isReady;
|
||||||
}
|
}
|
||||||
@@ -126,7 +134,7 @@ export class JupyterSessionManager implements nb.SessionManager {
|
|||||||
return Promise.reject(new Error(localize('errorStartBeforeReady', "Cannot start a session, the manager is not yet initialized")));
|
return Promise.reject(new Error(localize('errorStartBeforeReady', "Cannot start a session, the manager is not yet initialized")));
|
||||||
}
|
}
|
||||||
let sessionImpl = await this._sessionManager.startNew(options);
|
let sessionImpl = await this._sessionManager.startNew(options);
|
||||||
let jupyterSession = new JupyterSession(sessionImpl, skipSettingEnvironmentVars, this._pythonEnvVarPath);
|
let jupyterSession = new JupyterSession(sessionImpl, this._installation, skipSettingEnvironmentVars, this._pythonEnvVarPath);
|
||||||
await jupyterSession.messagesComplete;
|
await jupyterSession.messagesComplete;
|
||||||
let index = JupyterSessionManager._sessions.findIndex(session => session.path === options.path);
|
let index = JupyterSessionManager._sessions.findIndex(session => session.path === options.path);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
@@ -173,7 +181,7 @@ export class JupyterSession implements nb.ISession {
|
|||||||
private _kernel: nb.IKernel;
|
private _kernel: nb.IKernel;
|
||||||
private _messagesComplete: Deferred<void> = new Deferred<void>();
|
private _messagesComplete: Deferred<void> = new Deferred<void>();
|
||||||
|
|
||||||
constructor(private sessionImpl: Session.ISession, skipSettingEnvironmentVars?: boolean, private _pythonEnvVarPath?: string) {
|
constructor(private sessionImpl: Session.ISession, private _installation: JupyterServerInstallation, skipSettingEnvironmentVars?: boolean, private _pythonEnvVarPath?: string) {
|
||||||
this.setEnvironmentVars(skipSettingEnvironmentVars).catch(error => {
|
this.setEnvironmentVars(skipSettingEnvironmentVars).catch(error => {
|
||||||
console.error(`Unexpected exception setting Jupyter Session variables : ${error}`);
|
console.error(`Unexpected exception setting Jupyter Session variables : ${error}`);
|
||||||
// We don't want callers to hang forever waiting - it's better to continue on even if we weren't
|
// We don't want callers to hang forever waiting - it's better to continue on even if we weren't
|
||||||
@@ -221,7 +229,20 @@ export class JupyterSession implements nb.ISession {
|
|||||||
return this._messagesComplete.promise;
|
return this._messagesComplete.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set installation(installation: JupyterServerInstallation) {
|
||||||
|
this._installation = installation;
|
||||||
|
}
|
||||||
|
|
||||||
public async changeKernel(kernelInfo: nb.IKernelSpec): Promise<nb.IKernel> {
|
public async changeKernel(kernelInfo: nb.IKernelSpec): Promise<nb.IKernel> {
|
||||||
|
if (this._installation) {
|
||||||
|
try {
|
||||||
|
await this._installation.promptForPackageUpgrade(kernelInfo.display_name);
|
||||||
|
} catch (err) {
|
||||||
|
// Have to swallow the error here to prevent hangs when changing back to the old kernel.
|
||||||
|
console.error(err.toString());
|
||||||
|
return this._kernel;
|
||||||
|
}
|
||||||
|
}
|
||||||
// For now, Jupyter implementation handles disposal etc. so we can just
|
// For now, Jupyter implementation handles disposal etc. so we can just
|
||||||
// null out our kernel and let the changeKernel call handle this
|
// null out our kernel and let the changeKernel call handle this
|
||||||
this._kernel = undefined;
|
this._kernel = undefined;
|
||||||
@@ -348,11 +369,6 @@ export class JupyterSession implements nb.ISession {
|
|||||||
}
|
}
|
||||||
for (let i = 0; i < Object.keys(process.env).length; i++) {
|
for (let i = 0; i < Object.keys(process.env).length; i++) {
|
||||||
let key = Object.keys(process.env)[i];
|
let key = Object.keys(process.env)[i];
|
||||||
// DOTNET_ROOT gets set as part of the liveshare experience, but confuses the dotnet interactive kernel
|
|
||||||
// Not setting this environment variable for notebooks removes this issue
|
|
||||||
if (key.toLowerCase() === 'dotnet_root') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (key.toLowerCase() === 'path' && this._pythonEnvVarPath) {
|
if (key.toLowerCase() === 'path' && this._pythonEnvVarPath) {
|
||||||
allCode += `%set_env ${key}=${this._pythonEnvVarPath}${EOL}`;
|
allCode += `%set_env ${key}=${this._pythonEnvVarPath}${EOL}`;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
import { IServerInstance } from '../jupyter/common';
|
import { IServerInstance } from '../jupyter/common';
|
||||||
@@ -255,3 +256,160 @@ export class FutureStub implements Kernel.IFuture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
//#region test modelView components
|
||||||
|
class TestComponentBase implements azdata.Component {
|
||||||
|
id: string = '';
|
||||||
|
updateProperties(properties: { [key: string]: any; }): Thenable<void> {
|
||||||
|
Object.assign(this, properties);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
updateProperty(key: string, value: any): Thenable<void> {
|
||||||
|
throw new Error('Method not implemented');
|
||||||
|
}
|
||||||
|
updateCssStyles(cssStyles: { [key: string]: string; }): Thenable<void> {
|
||||||
|
throw new Error('Method not implemented');
|
||||||
|
}
|
||||||
|
onValidityChanged: vscode.Event<boolean> = undefined;
|
||||||
|
valid: boolean = true;
|
||||||
|
validate(): Thenable<boolean> {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
|
focus(): Thenable<void> {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestDropdownComponent extends TestComponentBase implements azdata.DropDownComponent {
|
||||||
|
constructor(private onClick: vscode.EventEmitter<any>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
onValueChanged: vscode.Event<any> = this.onClick.event;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestDeclarativeTableComponent extends TestComponentBase implements azdata.DeclarativeTableComponent {
|
||||||
|
constructor(private onClick: vscode.EventEmitter<any>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
onDataChanged: vscode.Event<any> = this.onClick.event;
|
||||||
|
data: any[][];
|
||||||
|
columns: azdata.DeclarativeTableColumn[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestButtonComponent extends TestComponentBase implements azdata.ButtonComponent {
|
||||||
|
constructor(private onClick: vscode.EventEmitter<any>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
onDidClick: vscode.Event<any> = this.onClick.event;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestRadioButtonComponent extends TestComponentBase implements azdata.RadioButtonComponent {
|
||||||
|
constructor(private onClick: vscode.EventEmitter<any>) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
onDidClick: vscode.Event<any> = this.onClick.event;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestTextComponent extends TestComponentBase implements azdata.TextComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestLoadingComponent extends TestComponentBase implements azdata.LoadingComponent {
|
||||||
|
loading: boolean;
|
||||||
|
component: azdata.Component;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestFormContainer extends TestComponentBase implements azdata.FormContainer {
|
||||||
|
items: azdata.Component[] = [];
|
||||||
|
clearItems(): void {
|
||||||
|
}
|
||||||
|
addItems(itemConfigs: azdata.Component[], itemLayout?: azdata.FormItemLayout): void {
|
||||||
|
}
|
||||||
|
addItem(component: azdata.Component, itemLayout?: azdata.FormItemLayout): void {
|
||||||
|
}
|
||||||
|
insertItem(component: azdata.Component, index: number, itemLayout?: azdata.FormItemLayout): void {
|
||||||
|
}
|
||||||
|
removeItem(component: azdata.Component): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
setLayout(layout: azdata.FormLayout): void {
|
||||||
|
}
|
||||||
|
setItemLayout(component: azdata.Component, layout: azdata.FormItemLayout): void {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestComponentBuilder<T extends azdata.Component> implements azdata.ComponentBuilder<T> {
|
||||||
|
constructor(private _component: T) {
|
||||||
|
}
|
||||||
|
component(): T {
|
||||||
|
return this._component;
|
||||||
|
}
|
||||||
|
withProperties<U>(properties: U): azdata.ComponentBuilder<T> {
|
||||||
|
this._component.updateProperties(properties);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
withValidation(validation: (component: T) => boolean): azdata.ComponentBuilder<T> {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestLoadingBuilder extends TestComponentBuilder<azdata.LoadingComponent> implements azdata.LoadingComponentBuilder {
|
||||||
|
withItem(component: azdata.Component): azdata.LoadingComponentBuilder {
|
||||||
|
this.component().component = component;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createViewContext(): TestContext {
|
||||||
|
let onClick: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
|
||||||
|
|
||||||
|
let form: azdata.FormContainer = new TestFormContainer();
|
||||||
|
let textBuilder: azdata.ComponentBuilder<azdata.TextComponent> = new TestComponentBuilder(new TestTextComponent());
|
||||||
|
let buttonBuilder: azdata.ComponentBuilder<azdata.ButtonComponent> = new TestComponentBuilder(new TestButtonComponent(onClick));
|
||||||
|
let radioButtonBuilder: azdata.ComponentBuilder<azdata.ButtonComponent> = new TestComponentBuilder(new TestRadioButtonComponent(onClick));
|
||||||
|
let declarativeTableBuilder: azdata.ComponentBuilder<azdata.DeclarativeTableComponent> = new TestComponentBuilder(new TestDeclarativeTableComponent(onClick));
|
||||||
|
let loadingBuilder: azdata.LoadingComponentBuilder = new TestLoadingBuilder(new TestLoadingComponent());
|
||||||
|
let dropdownBuilder: azdata.ComponentBuilder<azdata.DropDownComponent> = new TestComponentBuilder(new TestDropdownComponent(onClick));
|
||||||
|
|
||||||
|
let formBuilder: azdata.FormBuilder = Object.assign({}, {
|
||||||
|
component: () => form,
|
||||||
|
addFormItem: () => { },
|
||||||
|
insertFormItem: () => { },
|
||||||
|
removeFormItem: () => true,
|
||||||
|
addFormItems: () => { },
|
||||||
|
withFormItems: () => formBuilder,
|
||||||
|
withProperties: () => formBuilder,
|
||||||
|
withValidation: () => formBuilder,
|
||||||
|
withItems: () => formBuilder,
|
||||||
|
withLayout: () => formBuilder
|
||||||
|
});
|
||||||
|
|
||||||
|
let view: azdata.ModelView = {
|
||||||
|
onClosed: undefined!,
|
||||||
|
connection: undefined!,
|
||||||
|
serverInfo: undefined!,
|
||||||
|
valid: true,
|
||||||
|
onValidityChanged: undefined!,
|
||||||
|
validate: undefined!,
|
||||||
|
initializeModel: () => { return Promise.resolve(); },
|
||||||
|
modelBuilder: <azdata.ModelBuilder>{
|
||||||
|
radioButton: () => radioButtonBuilder,
|
||||||
|
text: () => textBuilder,
|
||||||
|
button: () => buttonBuilder,
|
||||||
|
dropDown: () => dropdownBuilder,
|
||||||
|
declarativeTable: () => declarativeTableBuilder,
|
||||||
|
formContainer: () => formBuilder,
|
||||||
|
loadingComponent: () => loadingBuilder
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
view: view,
|
||||||
|
onClick: onClick,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestContext {
|
||||||
|
view: azdata.ModelView;
|
||||||
|
onClick: vscode.EventEmitter<any>;
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export class MockExtensionContext implements vscode.ExtensionContext {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.subscriptions = [];
|
this.subscriptions = [];
|
||||||
}
|
}
|
||||||
|
environmentVariableCollection: vscode.EnvironmentVariableCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MockOutputChannel implements vscode.OutputChannel {
|
export class MockOutputChannel implements vscode.OutputChannel {
|
||||||
|
|||||||
133
extensions/notebook/src/test/configurePython.test.ts
Normal file
133
extensions/notebook/src/test/configurePython.test.ts
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as azdata from 'azdata';
|
||||||
|
import * as TypeMoq from 'typemoq';
|
||||||
|
import { ApiWrapper } from '../common/apiWrapper';
|
||||||
|
import { ConfigurePythonWizard, ConfigurePythonModel } from '../dialog/configurePython/configurePythonWizard';
|
||||||
|
import { JupyterServerInstallation } from '../jupyter/jupyterServerInstallation';
|
||||||
|
import { ConfigurePathPage } from '../dialog/configurePython/configurePathPage';
|
||||||
|
import * as should from 'should';
|
||||||
|
import { PickPackagesPage } from '../dialog/configurePython/pickPackagesPage';
|
||||||
|
import { python3DisplayName, allKernelsName } from '../common/constants';
|
||||||
|
import { TestContext, createViewContext } from './common';
|
||||||
|
|
||||||
|
describe('Configure Python Wizard', function () {
|
||||||
|
let apiWrapper: ApiWrapper = new ApiWrapper();
|
||||||
|
let testWizard: ConfigurePythonWizard;
|
||||||
|
let viewContext: TestContext;
|
||||||
|
let testInstallation: JupyterServerInstallation;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
let mockInstall = TypeMoq.Mock.ofType(JupyterServerInstallation);
|
||||||
|
mockInstall.setup(i => i.getInstalledPipPackages(TypeMoq.It.isAnyString())).returns(() => Promise.resolve([]));
|
||||||
|
testInstallation = mockInstall.object;
|
||||||
|
|
||||||
|
let mockWizard = TypeMoq.Mock.ofType(ConfigurePythonWizard);
|
||||||
|
mockWizard.setup(w => w.showErrorMessage(TypeMoq.It.isAnyString()));
|
||||||
|
testWizard = mockWizard.object;
|
||||||
|
|
||||||
|
viewContext = createViewContext();
|
||||||
|
});
|
||||||
|
|
||||||
|
// These wizard tests are disabled due to errors with disposable objects
|
||||||
|
//
|
||||||
|
// it('Start wizard test', async () => {
|
||||||
|
// let wizard = new ConfigurePythonWizard(apiWrapper, testInstallation);
|
||||||
|
// await wizard.start();
|
||||||
|
// await wizard.close();
|
||||||
|
// await should(wizard.setupComplete).be.resolved();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it('Reject setup on cancel test', async () => {
|
||||||
|
// let wizard = new ConfigurePythonWizard(apiWrapper, testInstallation);
|
||||||
|
// await wizard.start(undefined, true);
|
||||||
|
// await wizard.close();
|
||||||
|
// await should(wizard.setupComplete).be.rejected();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it('Error message test', async () => {
|
||||||
|
// let wizard = new ConfigurePythonWizard(apiWrapper, testInstallation);
|
||||||
|
// await wizard.start();
|
||||||
|
|
||||||
|
// should(wizard.wizard.message).be.undefined();
|
||||||
|
|
||||||
|
// let testMsg = 'Test message';
|
||||||
|
// wizard.showErrorMessage(testMsg);
|
||||||
|
// should(wizard.wizard.message.text).be.equal(testMsg);
|
||||||
|
// should(wizard.wizard.message.level).be.equal(azdata.window.MessageLevel.Error);
|
||||||
|
|
||||||
|
// wizard.clearStatusMessage();
|
||||||
|
// should(wizard.wizard.message).be.undefined();
|
||||||
|
|
||||||
|
// await wizard.close();
|
||||||
|
// });
|
||||||
|
|
||||||
|
it('Configure Path Page test', async () => {
|
||||||
|
let testPythonLocation = '/not/a/real/path';
|
||||||
|
let model = <ConfigurePythonModel>{
|
||||||
|
useExistingPython: true,
|
||||||
|
pythonPathsPromise: Promise.resolve([{
|
||||||
|
installDir: testPythonLocation,
|
||||||
|
version: '4000'
|
||||||
|
}])
|
||||||
|
};
|
||||||
|
|
||||||
|
let page = azdata.window.createWizardPage('Page 1');
|
||||||
|
let configurePathPage = new ConfigurePathPage(apiWrapper, testWizard, page, model, viewContext.view);
|
||||||
|
|
||||||
|
should(await configurePathPage.initialize()).be.true();
|
||||||
|
|
||||||
|
// First page, so onPageEnter should do nothing
|
||||||
|
await should(configurePathPage.onPageEnter()).be.resolved();
|
||||||
|
|
||||||
|
should(await configurePathPage.onPageLeave()).be.true();
|
||||||
|
should(model.useExistingPython).be.true();
|
||||||
|
should(model.pythonLocation).be.equal(testPythonLocation);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Pick Packages Page test', async () => {
|
||||||
|
let model = <ConfigurePythonModel>{
|
||||||
|
kernelName: allKernelsName,
|
||||||
|
installation: testInstallation,
|
||||||
|
pythonLocation: '/not/a/real/path',
|
||||||
|
useExistingPython: true
|
||||||
|
};
|
||||||
|
|
||||||
|
let page = azdata.window.createWizardPage('Page 2');
|
||||||
|
let pickPackagesPage = new PickPackagesPage(apiWrapper, testWizard, page, model, viewContext.view);
|
||||||
|
|
||||||
|
should(await pickPackagesPage.initialize()).be.true();
|
||||||
|
|
||||||
|
should((<any>pickPackagesPage).kernelLabel).not.be.undefined();
|
||||||
|
should((<any>pickPackagesPage).kernelDropdown).be.undefined();
|
||||||
|
|
||||||
|
// Last page, so onPageLeave should do nothing
|
||||||
|
should(await pickPackagesPage.onPageLeave()).be.true();
|
||||||
|
|
||||||
|
await should(pickPackagesPage.onPageEnter()).be.resolved();
|
||||||
|
should(model.packagesToInstall).be.deepEqual(JupyterServerInstallation.getRequiredPackagesForKernel(allKernelsName));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Undefined kernel test', async () => {
|
||||||
|
let model = <ConfigurePythonModel>{
|
||||||
|
kernelName: undefined,
|
||||||
|
installation: testInstallation,
|
||||||
|
pythonLocation: '/not/a/real/path',
|
||||||
|
useExistingPython: true
|
||||||
|
};
|
||||||
|
|
||||||
|
let page = azdata.window.createWizardPage('Page 2');
|
||||||
|
let pickPackagesPage = new PickPackagesPage(apiWrapper, testWizard, page, model, viewContext.view);
|
||||||
|
|
||||||
|
should(await pickPackagesPage.initialize()).be.true();
|
||||||
|
|
||||||
|
should((<any>pickPackagesPage).kernelLabel).be.undefined();
|
||||||
|
should((<any>pickPackagesPage).kernelDropdown).not.be.undefined();
|
||||||
|
|
||||||
|
await should(pickPackagesPage.onPageEnter()).be.resolved();
|
||||||
|
should(model.packagesToInstall).be.deepEqual(JupyterServerInstallation.getRequiredPackagesForKernel(python3DisplayName));
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -8,7 +8,7 @@ import * as azdata from 'azdata';
|
|||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
import * as TypeMoq from 'typemoq';
|
import * as TypeMoq from 'typemoq';
|
||||||
import { JupyterServerInstallation, PythonPkgDetails, IJupyterServerInstallation } from '../../jupyter/jupyterServerInstallation';
|
import { JupyterServerInstallation, PythonPkgDetails, IJupyterServerInstallation, PythonInstallSettings } from '../../jupyter/jupyterServerInstallation';
|
||||||
import { LocalCondaPackageManageProvider } from '../../jupyter/localCondaPackageManageProvider';
|
import { LocalCondaPackageManageProvider } from '../../jupyter/localCondaPackageManageProvider';
|
||||||
import * as constants from '../../common/constants';
|
import * as constants from '../../common/constants';
|
||||||
import { LocalPipPackageManageProvider } from '../../jupyter/localPipPackageManageProvider';
|
import { LocalPipPackageManageProvider } from '../../jupyter/localPipPackageManageProvider';
|
||||||
@@ -188,7 +188,7 @@ describe('Manage Package Providers', () => {
|
|||||||
serverInstallation: {
|
serverInstallation: {
|
||||||
installCondaPackages: (packages: PythonPkgDetails[], useMinVersion: boolean) => { return Promise.resolve(); },
|
installCondaPackages: (packages: PythonPkgDetails[], useMinVersion: boolean) => { return Promise.resolve(); },
|
||||||
configurePackagePaths: () => { return Promise.resolve(); },
|
configurePackagePaths: () => { return Promise.resolve(); },
|
||||||
startInstallProcess: (forceInstall: boolean, installSettings?: { installPath: string, existingPython: boolean }) => { return Promise.resolve(); },
|
startInstallProcess: (forceInstall: boolean, installSettings?: PythonInstallSettings) => { return Promise.resolve(); },
|
||||||
getInstalledPipPackages: () => { return Promise.resolve([]); },
|
getInstalledPipPackages: () => { return Promise.resolve([]); },
|
||||||
installPipPackages: (packages: PythonPkgDetails[], useMinVersion: boolean) => { return Promise.resolve(); },
|
installPipPackages: (packages: PythonPkgDetails[], useMinVersion: boolean) => { return Promise.resolve(); },
|
||||||
uninstallPipPackages: (packages: PythonPkgDetails[]) => { return Promise.resolve(); },
|
uninstallPipPackages: (packages: PythonPkgDetails[]) => { return Promise.resolve(); },
|
||||||
|
|||||||
@@ -139,7 +139,8 @@ describe('Manage Package Dialog', () => {
|
|||||||
removeItem: () => true,
|
removeItem: () => true,
|
||||||
insertItem: () => { },
|
insertItem: () => { },
|
||||||
items: components,
|
items: components,
|
||||||
setLayout: () => { }
|
setLayout: () => { },
|
||||||
|
setItemLayout: () => { }
|
||||||
};
|
};
|
||||||
let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
|
let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -199,6 +199,36 @@ describe('Manage Packages', () => {
|
|||||||
should.deepEqual(model.getPackageTypes('location1'), [{providerId: 'providerId1', packageType: 'package-type1'}]);
|
should.deepEqual(model.getPackageTypes('location1'), [{providerId: 'providerId1', packageType: 'package-type1'}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should set default location to one set in given options', async function (): Promise<void> {
|
||||||
|
let testContext1 = createContext();
|
||||||
|
testContext1.provider.providerId = 'providerId1';
|
||||||
|
testContext1.provider.packageTarget = {
|
||||||
|
location: 'location1',
|
||||||
|
packageType: 'package-type1'
|
||||||
|
};
|
||||||
|
|
||||||
|
let testContext2 = createContext();
|
||||||
|
testContext2.provider.providerId = 'providerId2';
|
||||||
|
testContext2.provider.packageTarget = {
|
||||||
|
location: 'location1',
|
||||||
|
packageType: 'package-type2'
|
||||||
|
};
|
||||||
|
testContext2.provider.canUseProvider = () => { return Promise.resolve(false); };
|
||||||
|
|
||||||
|
let providers = new Map<string, IPackageManageProvider>();
|
||||||
|
providers.set(testContext1.provider.providerId, createProvider(testContext1));
|
||||||
|
providers.set(testContext2.provider.providerId, createProvider(testContext2));
|
||||||
|
|
||||||
|
let model = new ManagePackagesDialogModel(jupyterServerInstallation, providers, {
|
||||||
|
defaultLocation: testContext2.provider.packageTarget.location,
|
||||||
|
defaultProviderId: testContext2.provider.providerId
|
||||||
|
});
|
||||||
|
|
||||||
|
await model.init();
|
||||||
|
should.equal(model.defaultLocation, testContext2.provider.packageTarget.location);
|
||||||
|
should.deepEqual(model.getPackageTypes('location1'), [{providerId: 'providerId1', packageType: 'package-type1'}]);
|
||||||
|
});
|
||||||
|
|
||||||
it('changeProvider should change current provider successfully', async function (): Promise<void> {
|
it('changeProvider should change current provider successfully', async function (): Promise<void> {
|
||||||
let testContext1 = createContext();
|
let testContext1 = createContext();
|
||||||
testContext1.provider.providerId = 'providerId1';
|
testContext1.provider.providerId = 'providerId1';
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import * as TypeMoq from 'typemoq';
|
import * as TypeMoq from 'typemoq';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
import * as azdata from 'azdata';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
|
|
||||||
import { JupyterServerInstanceStub } from '../common';
|
import { JupyterServerInstanceStub } from '../common';
|
||||||
@@ -18,6 +19,10 @@ import { IServerInstance } from '../../jupyter/common';
|
|||||||
import { MockExtensionContext } from '../common/stubs';
|
import { MockExtensionContext } from '../common/stubs';
|
||||||
|
|
||||||
describe('Local Jupyter Server Manager', function (): void {
|
describe('Local Jupyter Server Manager', function (): void {
|
||||||
|
const pythonKernelSpec: azdata.nb.IKernelSpec = {
|
||||||
|
name: 'python3',
|
||||||
|
display_name: 'Python 3'
|
||||||
|
};
|
||||||
let expectedPath = 'my/notebook.ipynb';
|
let expectedPath = 'my/notebook.ipynb';
|
||||||
let serverManager: LocalJupyterServerManager;
|
let serverManager: LocalJupyterServerManager;
|
||||||
let deferredInstall: Deferred<void>;
|
let deferredInstall: Deferred<void>;
|
||||||
@@ -33,7 +38,7 @@ describe('Local Jupyter Server Manager', function (): void {
|
|||||||
|
|
||||||
deferredInstall = new Deferred<void>();
|
deferredInstall = new Deferred<void>();
|
||||||
let mockInstall = TypeMoq.Mock.ofType(JupyterServerInstallation, undefined, undefined, '/root');
|
let mockInstall = TypeMoq.Mock.ofType(JupyterServerInstallation, undefined, undefined, '/root');
|
||||||
mockInstall.setup(j => j.promptForPythonInstall()).returns(() => deferredInstall.promise);
|
mockInstall.setup(j => j.promptForPythonInstall(TypeMoq.It.isAny())).returns(() => deferredInstall.promise);
|
||||||
mockInstall.object.execOptions = { env: Object.assign({}, process.env) };
|
mockInstall.object.execOptions = { env: Object.assign({}, process.env) };
|
||||||
|
|
||||||
serverManager = new LocalJupyterServerManager({
|
serverManager = new LocalJupyterServerManager({
|
||||||
@@ -53,7 +58,7 @@ describe('Local Jupyter Server Manager', function (): void {
|
|||||||
it('Should show error message on install failure', async function (): Promise<void> {
|
it('Should show error message on install failure', async function (): Promise<void> {
|
||||||
let error = 'Error!!';
|
let error = 'Error!!';
|
||||||
deferredInstall.reject(error);
|
deferredInstall.reject(error);
|
||||||
await testUtils.assertThrowsAsync(() => serverManager.startServer(), undefined);
|
await testUtils.assertThrowsAsync(() => serverManager.startServer(pythonKernelSpec), undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should configure and start install', async function (): Promise<void> {
|
it('Should configure and start install', async function (): Promise<void> {
|
||||||
@@ -65,7 +70,7 @@ describe('Local Jupyter Server Manager', function (): void {
|
|||||||
// When I start the server
|
// When I start the server
|
||||||
let notified = false;
|
let notified = false;
|
||||||
serverManager.onServerStarted(() => notified = true);
|
serverManager.onServerStarted(() => notified = true);
|
||||||
await serverManager.startServer();
|
await serverManager.startServer(pythonKernelSpec);
|
||||||
|
|
||||||
// Then I expect the port to be included in settings
|
// Then I expect the port to be included in settings
|
||||||
should(serverManager.serverSettings.baseUrl.indexOf('1234') > -1).be.true();
|
should(serverManager.serverSettings.baseUrl.indexOf('1234') > -1).be.true();
|
||||||
@@ -89,7 +94,7 @@ describe('Local Jupyter Server Manager', function (): void {
|
|||||||
deferredInstall.resolve();
|
deferredInstall.resolve();
|
||||||
|
|
||||||
// When I start and then the server
|
// When I start and then the server
|
||||||
await serverManager.startServer();
|
await serverManager.startServer(pythonKernelSpec);
|
||||||
await serverManager.stopServer();
|
await serverManager.stopServer();
|
||||||
|
|
||||||
// Then I expect stop to have been called on the server instance
|
// Then I expect stop to have been called on the server instance
|
||||||
@@ -104,7 +109,7 @@ describe('Local Jupyter Server Manager', function (): void {
|
|||||||
deferredInstall.resolve();
|
deferredInstall.resolve();
|
||||||
|
|
||||||
// When I start and then dispose the extension
|
// When I start and then dispose the extension
|
||||||
await serverManager.startServer();
|
await serverManager.startServer(pythonKernelSpec);
|
||||||
should(mockExtensionContext.subscriptions).have.length(1);
|
should(mockExtensionContext.subscriptions).have.length(1);
|
||||||
mockExtensionContext.subscriptions[0].dispose();
|
mockExtensionContext.subscriptions[0].dispose();
|
||||||
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ describe('Jupyter Session', function (): void {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockJupyterSession = TypeMoq.Mock.ofType(SessionStub);
|
mockJupyterSession = TypeMoq.Mock.ofType(SessionStub);
|
||||||
session = new JupyterSession(mockJupyterSession.object);
|
session = new JupyterSession(mockJupyterSession.object, undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should always be able to change kernels', function (): void {
|
it('should always be able to change kernels', function (): void {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "Dependencies shared by all extensions",
|
"description": "Dependencies shared by all extensions",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"typescript": "3.8.3"
|
"typescript": "3.9.1-rc"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "node ./postinstall"
|
"postinstall": "node ./postinstall"
|
||||||
|
|||||||
@@ -26,6 +26,17 @@
|
|||||||
"Microsoft.mssql"
|
"Microsoft.mssql"
|
||||||
],
|
],
|
||||||
"contributes": {
|
"contributes": {
|
||||||
|
"configuration": [
|
||||||
|
{
|
||||||
|
"title": "%sqlDatabaseProjects.Settings%",
|
||||||
|
"properties": {
|
||||||
|
"sqlDatabaseProjects.netCoreSDKLocation": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "%sqlDatabaseProjects.netCoreInstallLocation%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"command": "sqlDatabaseProjects.new",
|
"command": "sqlDatabaseProjects.new",
|
||||||
@@ -71,6 +82,31 @@
|
|||||||
"command": "sqlDatabaseProjects.newFolder",
|
"command": "sqlDatabaseProjects.newFolder",
|
||||||
"title": "%sqlDatabaseProjects.newFolder%",
|
"title": "%sqlDatabaseProjects.newFolder%",
|
||||||
"category": "%sqlDatabaseProjects.displayName%"
|
"category": "%sqlDatabaseProjects.displayName%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.build",
|
||||||
|
"title": "%sqlDatabaseProjects.build%",
|
||||||
|
"category": "%sqlDatabaseProjects.displayName%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.deploy",
|
||||||
|
"title": "%sqlDatabaseProjects.deploy%",
|
||||||
|
"category": "%sqlDatabaseProjects.displayName%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.import",
|
||||||
|
"title": "%sqlDatabaseProjects.import%",
|
||||||
|
"category": "%sqlDatabaseProjects.displayName%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.properties",
|
||||||
|
"title": "%sqlDatabaseProjects.properties%",
|
||||||
|
"category": "%sqlDatabaseProjects.displayName%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.schemaCompare",
|
||||||
|
"title": "%sqlDatabaseProjects.schemaCompare%",
|
||||||
|
"category": "%sqlDatabaseProjects.displayName%"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"menus": {
|
"menus": {
|
||||||
@@ -108,36 +144,87 @@
|
|||||||
{
|
{
|
||||||
"command": "sqlDatabaseProjects.newFolder",
|
"command": "sqlDatabaseProjects.newFolder",
|
||||||
"when": "false"
|
"when": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.build",
|
||||||
|
"when": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.deploy",
|
||||||
|
"when": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.import"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.properties",
|
||||||
|
"when": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.schemaCompare",
|
||||||
|
"when": "false"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"view/item/context": [
|
"view/item/context": [
|
||||||
{
|
{
|
||||||
"command": "sqlDatabaseProjects.close",
|
"command": "sqlDatabaseProjects.build",
|
||||||
"when": "view == sqlDatabaseProjectsView"
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "1_dbProjectsFirst@1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "sqlDatabaseProjects.newScript",
|
"command": "sqlDatabaseProjects.deploy",
|
||||||
"when": "view == sqlDatabaseProjectsView"
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "1_dbProjectsFirst@2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "sqlDatabaseProjects.newTable",
|
"command": "sqlDatabaseProjects.schemaCompare",
|
||||||
"when": "view == sqlDatabaseProjectsView"
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "1_dbProjectsFirst@3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "sqlDatabaseProjects.newView",
|
"command": "sqlDatabaseProjects.import",
|
||||||
"when": "view == sqlDatabaseProjectsView"
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
},
|
"group": "1_dbProjectsFirst@4"
|
||||||
{
|
|
||||||
"command": "sqlDatabaseProjects.newStoredProcedure",
|
|
||||||
"when": "view == sqlDatabaseProjectsView"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "sqlDatabaseProjects.newItem",
|
"command": "sqlDatabaseProjects.newItem",
|
||||||
"when": "view == sqlDatabaseProjectsView"
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "2_dbProjects_newMain@1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "sqlDatabaseProjects.newFolder",
|
"command": "sqlDatabaseProjects.newFolder",
|
||||||
"when": "view == sqlDatabaseProjectsView"
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "2_dbProjects_newMain@2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.newTable",
|
||||||
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "3_dbProjects_newItem@1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.newView",
|
||||||
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "3_dbProjects_newItem@2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.newStoredProcedure",
|
||||||
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "3_dbProjects_newItem@3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.newScript",
|
||||||
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "3_dbProjects_newItem@9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.close",
|
||||||
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "9_dbProjectsLast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlDatabaseProjects.properties",
|
||||||
|
"when": "view == sqlDatabaseProjectsView",
|
||||||
|
"group": "9_dbProjectsLast"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,11 +6,20 @@
|
|||||||
"sqlDatabaseProjects.new": "New Database Project",
|
"sqlDatabaseProjects.new": "New Database Project",
|
||||||
"sqlDatabaseProjects.open": "Open Database Project",
|
"sqlDatabaseProjects.open": "Open Database Project",
|
||||||
"sqlDatabaseProjects.close": "Close Database Project",
|
"sqlDatabaseProjects.close": "Close Database Project",
|
||||||
|
"sqlDatabaseProjects.build": "Build",
|
||||||
|
"sqlDatabaseProjects.deploy": "Deploy",
|
||||||
|
"sqlDatabaseProjects.import": "Import",
|
||||||
|
"sqlDatabaseProjects.properties": "Properties",
|
||||||
|
"sqlDatabaseProjects.schemaCompare": "Schema Compare",
|
||||||
|
|
||||||
"sqlDatabaseProjects.newScript": "Add Script",
|
"sqlDatabaseProjects.newScript": "Add Script",
|
||||||
"sqlDatabaseProjects.newTable": "Add Table",
|
"sqlDatabaseProjects.newTable": "Add Table",
|
||||||
"sqlDatabaseProjects.newView": "Add View",
|
"sqlDatabaseProjects.newView": "Add View",
|
||||||
"sqlDatabaseProjects.newStoredProcedure": "Add Stored Procedure",
|
"sqlDatabaseProjects.newStoredProcedure": "Add Stored Procedure",
|
||||||
"sqlDatabaseProjects.newItem": "Add Item...",
|
"sqlDatabaseProjects.newItem": "Add Item...",
|
||||||
"sqlDatabaseProjects.newFolder": "Add Folder"
|
"sqlDatabaseProjects.newFolder": "Add Folder",
|
||||||
|
|
||||||
|
|
||||||
|
"sqlDatabaseProjects.Settings": "Database Projects",
|
||||||
|
"sqlDatabaseProjects.netCoreInstallLocation": "Full Path to .Net Core SDK on the machine."
|
||||||
}
|
}
|
||||||
|
|||||||
149
extensions/sql-database-projects/src/common/apiWrapper.ts
Normal file
149
extensions/sql-database-projects/src/common/apiWrapper.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper class to act as a facade over VSCode and Data APIs and allow us to test / mock callbacks into
|
||||||
|
* this API from our code
|
||||||
|
*/
|
||||||
|
export class ApiWrapper {
|
||||||
|
public createOutputChannel(name: string): vscode.OutputChannel {
|
||||||
|
return vscode.window.createOutputChannel(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createTerminalWithOptions(options: vscode.TerminalOptions): vscode.Terminal {
|
||||||
|
return vscode.window.createTerminal(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCurrentConnection(): Thenable<azdata.connection.ConnectionProfile> {
|
||||||
|
return azdata.connection.getCurrentConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCredentials(connectionId: string): Thenable<{ [name: string]: string }> {
|
||||||
|
return azdata.connection.getCredentials(connectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerCommand(command: string, callback: (...args: any[]) => any, thisArg?: any): vscode.Disposable {
|
||||||
|
return vscode.commands.registerCommand(command, callback, thisArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public executeCommand<T>(command: string, ...rest: any[]): Thenable<T | undefined> {
|
||||||
|
return vscode.commands.executeCommand(command, ...rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerTaskHandler(taskId: string, handler: (profile: azdata.IConnectionProfile) => void): void {
|
||||||
|
azdata.tasks.registerTask(taskId, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerTreeDataProvider<T>(viewId: string, treeDataProvider: vscode.TreeDataProvider<T>): vscode.Disposable {
|
||||||
|
return vscode.window.registerTreeDataProvider(viewId, treeDataProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUriForConnection(connectionId: string): Thenable<string> {
|
||||||
|
return azdata.connection.getUriForConnection(connectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getProvider<T extends azdata.DataProvider>(providerId: string, providerType: azdata.DataProviderType): T {
|
||||||
|
return azdata.dataprotocol.getProvider<T>(providerId, providerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public showErrorMessage(message: string, ...items: string[]): Thenable<string | undefined> {
|
||||||
|
return vscode.window.showErrorMessage(message, ...items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public showInformationMessage(message: string, ...items: string[]): Thenable<string | undefined> {
|
||||||
|
return vscode.window.showInformationMessage(message, ...items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public showOpenDialog(options: vscode.OpenDialogOptions): Thenable<vscode.Uri[] | undefined> {
|
||||||
|
return vscode.window.showOpenDialog(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public startBackgroundOperation(operationInfo: azdata.BackgroundOperationInfo): void {
|
||||||
|
azdata.tasks.startBackgroundOperation(operationInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public openExternal(target: vscode.Uri): Thenable<boolean> {
|
||||||
|
return vscode.env.openExternal(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getExtension(extensionId: string): vscode.Extension<any> | undefined {
|
||||||
|
return vscode.extensions.getExtension(extensionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getConfiguration(section?: string, resource?: vscode.Uri | null): vscode.WorkspaceConfiguration {
|
||||||
|
return vscode.workspace.getConfiguration(section, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public workspaceFolders(): readonly vscode.WorkspaceFolder[] | undefined {
|
||||||
|
return vscode.workspace.workspaceFolders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createTab(title: string): azdata.window.DialogTab {
|
||||||
|
return azdata.window.createTab(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createModelViewDialog(title: string, dialogName?: string, isWide?: boolean): azdata.window.Dialog {
|
||||||
|
return azdata.window.createModelViewDialog(title, dialogName, isWide);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createWizard(title: string): azdata.window.Wizard {
|
||||||
|
return azdata.window.createWizard(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createWizardPage(title: string): azdata.window.WizardPage {
|
||||||
|
return azdata.window.createWizardPage(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public openDialog(dialog: azdata.window.Dialog): void {
|
||||||
|
return azdata.window.openDialog(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAllAccounts(): Thenable<azdata.Account[]> {
|
||||||
|
return azdata.accounts.getAllAccounts();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSecurityToken(account: azdata.Account, resource: azdata.AzureResource): Thenable<{ [key: string]: any }> {
|
||||||
|
return azdata.accounts.getSecurityToken(account, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public showQuickPick<T extends vscode.QuickPickItem>(items: T[] | Thenable<T[]>, options?: vscode.QuickPickOptions, token?: vscode.CancellationToken): Thenable<T | undefined> {
|
||||||
|
return vscode.window.showQuickPick(items, options, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public showInputBox(options?: vscode.InputBoxOptions, token?: vscode.CancellationToken): Thenable<string | undefined> {
|
||||||
|
return vscode.window.showInputBox(options, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public listDatabases(connectionId: string): Thenable<string[]> {
|
||||||
|
return azdata.connection.listDatabases(connectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public openTextDocument(options?: { language?: string; content?: string; }): Thenable<vscode.TextDocument> {
|
||||||
|
return vscode.workspace.openTextDocument(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public connect(fileUri: string, connectionId: string): Thenable<void> {
|
||||||
|
return azdata.queryeditor.connect(fileUri, connectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public runQuery(fileUri: string, options?: Map<string, string>, runCurrentQuery?: boolean): void {
|
||||||
|
azdata.queryeditor.runQuery(fileUri, options, runCurrentQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
public showTextDocument(uri: vscode.Uri, options?: vscode.TextDocumentShowOptions): Thenable<vscode.TextEditor> {
|
||||||
|
return vscode.window.showTextDocument(uri, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createButton(label: string, position?: azdata.window.DialogButtonPosition): azdata.window.Button {
|
||||||
|
return azdata.window.createButton(label, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerWidget(widgetId: string, handler: (view: azdata.ModelView) => void): void {
|
||||||
|
azdata.ui.registerModelViewProvider(widgetId, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,33 +3,36 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import * as templates from '../templates/templates';
|
import * as templates from '../templates/templates';
|
||||||
import * as constants from '../common/constants';
|
import * as constants from '../common/constants';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import { Uri, Disposable, ExtensionContext, WorkspaceFolder } from 'vscode';
|
||||||
|
import { ApiWrapper } from '../common/apiWrapper';
|
||||||
import { SqlDatabaseProjectTreeViewProvider } from './databaseProjectTreeViewProvider';
|
import { SqlDatabaseProjectTreeViewProvider } from './databaseProjectTreeViewProvider';
|
||||||
import { getErrorMessage } from '../common/utils';
|
import { getErrorMessage } from '../common/utils';
|
||||||
import { ProjectsController } from './projectController';
|
import { ProjectsController } from './projectController';
|
||||||
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
||||||
|
import { NetCoreTool } from '../tools/netcoreTool';
|
||||||
|
import { Project } from '../models/project';
|
||||||
|
|
||||||
const SQL_DATABASE_PROJECTS_VIEW_ID = 'sqlDatabaseProjectsView';
|
const SQL_DATABASE_PROJECTS_VIEW_ID = 'sqlDatabaseProjectsView';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main controller class that initializes the extension
|
* The main controller class that initializes the extension
|
||||||
*/
|
*/
|
||||||
export default class MainController implements vscode.Disposable {
|
export default class MainController implements Disposable {
|
||||||
protected _context: vscode.ExtensionContext;
|
|
||||||
protected dbProjectTreeViewProvider: SqlDatabaseProjectTreeViewProvider = new SqlDatabaseProjectTreeViewProvider();
|
protected dbProjectTreeViewProvider: SqlDatabaseProjectTreeViewProvider = new SqlDatabaseProjectTreeViewProvider();
|
||||||
protected projectsController: ProjectsController;
|
protected projectsController: ProjectsController;
|
||||||
|
protected netcoreTool: NetCoreTool;
|
||||||
|
|
||||||
public constructor(context: vscode.ExtensionContext) {
|
public constructor(private context: ExtensionContext, private apiWrapper: ApiWrapper) {
|
||||||
this._context = context;
|
this.projectsController = new ProjectsController(apiWrapper, this.dbProjectTreeViewProvider);
|
||||||
this.projectsController = new ProjectsController(this.dbProjectTreeViewProvider);
|
this.netcoreTool = new NetCoreTool();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get extensionContext(): vscode.ExtensionContext {
|
public get extensionContext(): ExtensionContext {
|
||||||
return this._context;
|
return this.context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public deactivate(): void {
|
public deactivate(): void {
|
||||||
@@ -41,21 +44,29 @@ export default class MainController implements vscode.Disposable {
|
|||||||
|
|
||||||
private async initializeDatabaseProjects(): Promise<void> {
|
private async initializeDatabaseProjects(): Promise<void> {
|
||||||
// init commands
|
// init commands
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.new', async () => { await this.createNewProject(); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.new', async () => { await this.createNewProject(); });
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.open', async () => { await this.openProjectFromFile(); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.open', async () => { await this.openProjectFromFile(); });
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.close', (node: BaseProjectTreeItem) => { this.projectsController.closeProject(node); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.close', (node: BaseProjectTreeItem) => { this.projectsController.closeProject(node); });
|
||||||
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.properties', async (node: BaseProjectTreeItem) => { await this.apiWrapper.showErrorMessage(`Properties not yet implemented: ${node.uri.path}`); }); // TODO
|
||||||
|
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.newScript', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPrompt(node, templates.script); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.build', async (node: BaseProjectTreeItem) => { await this.projectsController.build(node); });
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.newTable', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPrompt(node, templates.table); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.deploy', async (node: BaseProjectTreeItem) => { await this.projectsController.deploy(node); });
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.newView', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPrompt(node, templates.view); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.import', async (node: BaseProjectTreeItem) => { await this.projectsController.import(node); });
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.newStoredProcedure', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPrompt(node, templates.storedProcedure); });
|
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.newItem', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPrompt(node); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.newScript', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPromptFromNode(node, templates.script); });
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.newFolder', async (node: BaseProjectTreeItem) => { await this.projectsController.addFolderPrompt(node); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.newTable', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPromptFromNode(node, templates.table); });
|
||||||
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.newView', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPromptFromNode(node, templates.view); });
|
||||||
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.newStoredProcedure', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPromptFromNode(node, templates.storedProcedure); });
|
||||||
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.newItem', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPromptFromNode(node); });
|
||||||
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.newFolder', async (node: BaseProjectTreeItem) => { await this.projectsController.addFolderPrompt(node); });
|
||||||
|
|
||||||
// init view
|
// init view
|
||||||
this.extensionContext.subscriptions.push(vscode.window.registerTreeDataProvider(SQL_DATABASE_PROJECTS_VIEW_ID, this.dbProjectTreeViewProvider));
|
this.extensionContext.subscriptions.push(this.apiWrapper.registerTreeDataProvider(SQL_DATABASE_PROJECTS_VIEW_ID, this.dbProjectTreeViewProvider));
|
||||||
|
|
||||||
await templates.loadTemplates(path.join(this._context.extensionPath, 'resources', 'templates'));
|
await templates.loadTemplates(path.join(this.context.extensionPath, 'resources', 'templates'));
|
||||||
|
|
||||||
|
// ensure .net core is installed
|
||||||
|
this.netcoreTool.findOrInstallNetCore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,7 +79,7 @@ export default class MainController implements vscode.Disposable {
|
|||||||
|
|
||||||
filter[constants.sqlDatabaseProject] = ['sqlproj'];
|
filter[constants.sqlDatabaseProject] = ['sqlproj'];
|
||||||
|
|
||||||
let files: vscode.Uri[] | undefined = await vscode.window.showOpenDialog({ filters: filter });
|
let files: Uri[] | undefined = await this.apiWrapper.showOpenDialog({ filters: filter });
|
||||||
|
|
||||||
if (files) {
|
if (files) {
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
@@ -77,48 +88,50 @@ export default class MainController implements vscode.Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
vscode.window.showErrorMessage(getErrorMessage(err));
|
this.apiWrapper.showErrorMessage(getErrorMessage(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new SQL database project from a template, prompting the user for a name and location
|
* Creates a new SQL database project from a template, prompting the user for a name and location
|
||||||
*/
|
*/
|
||||||
public async createNewProject(): Promise<void> {
|
public async createNewProject(): Promise<Project | undefined> {
|
||||||
try {
|
try {
|
||||||
let newProjName = await vscode.window.showInputBox({
|
let newProjName = await this.apiWrapper.showInputBox({
|
||||||
prompt: constants.newDatabaseProjectName,
|
prompt: constants.newDatabaseProjectName,
|
||||||
value: `DatabaseProject${this.projectsController.projects.length + 1}`
|
value: `DatabaseProject${this.projectsController.projects.length + 1}`
|
||||||
// TODO: Smarter way to suggest a name. Easy if we prompt for location first, but that feels odd...
|
// TODO: Smarter way to suggest a name. Easy if we prompt for location first, but that feels odd...
|
||||||
});
|
});
|
||||||
|
|
||||||
|
newProjName = newProjName?.trim();
|
||||||
|
|
||||||
if (!newProjName) {
|
if (!newProjName) {
|
||||||
// TODO: is this case considered an intentional cancellation (shouldn't warn) or an error case (should warn)?
|
// TODO: is this case considered an intentional cancellation (shouldn't warn) or an error case (should warn)?
|
||||||
vscode.window.showErrorMessage(constants.projectNameRequired);
|
this.apiWrapper.showErrorMessage(constants.projectNameRequired);
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectionResult = await vscode.window.showOpenDialog({
|
let selectionResult = await this.apiWrapper.showOpenDialog({
|
||||||
canSelectFiles: false,
|
canSelectFiles: false,
|
||||||
canSelectFolders: true,
|
canSelectFolders: true,
|
||||||
canSelectMany: false,
|
canSelectMany: false,
|
||||||
defaultUri: vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders[0].uri : undefined
|
defaultUri: this.apiWrapper.workspaceFolders() ? (this.apiWrapper.workspaceFolders() as WorkspaceFolder[])[0].uri : undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!selectionResult) {
|
if (!selectionResult) {
|
||||||
vscode.window.showErrorMessage(constants.projectLocationRequired);
|
this.apiWrapper.showErrorMessage(constants.projectLocationRequired);
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: what if the selected folder is outside the workspace?
|
// TODO: what if the selected folder is outside the workspace?
|
||||||
|
|
||||||
const newProjFolderUri = (selectionResult as vscode.Uri[])[0];
|
const newProjFolderUri = (selectionResult as Uri[])[0];
|
||||||
console.log(newProjFolderUri.fsPath);
|
const newProjFilePath = await this.projectsController.createNewProject(newProjName as string, newProjFolderUri as Uri);
|
||||||
const newProjFilePath = await this.projectsController.createNewProject(newProjName as string, newProjFolderUri as vscode.Uri);
|
return this.projectsController.openProject(Uri.file(newProjFilePath));
|
||||||
await this.projectsController.openProject(vscode.Uri.file(newProjFilePath));
|
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
vscode.window.showErrorMessage(getErrorMessage(err));
|
this.apiWrapper.showErrorMessage(getErrorMessage(err));
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as constants from '../common/constants';
|
import * as constants from '../common/constants';
|
||||||
import * as dataSources from '../models/dataSources/dataSources';
|
import * as dataSources from '../models/dataSources/dataSources';
|
||||||
@@ -11,6 +10,8 @@ import * as utils from '../common/utils';
|
|||||||
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
||||||
import * as templates from '../templates/templates';
|
import * as templates from '../templates/templates';
|
||||||
|
|
||||||
|
import { Uri, QuickPickItem } from 'vscode';
|
||||||
|
import { ApiWrapper } from '../common/apiWrapper';
|
||||||
import { Project } from '../models/project';
|
import { Project } from '../models/project';
|
||||||
import { SqlDatabaseProjectTreeViewProvider } from './databaseProjectTreeViewProvider';
|
import { SqlDatabaseProjectTreeViewProvider } from './databaseProjectTreeViewProvider';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
@@ -26,7 +27,7 @@ export class ProjectsController {
|
|||||||
|
|
||||||
projects: Project[] = [];
|
projects: Project[] = [];
|
||||||
|
|
||||||
constructor(projTreeViewProvider: SqlDatabaseProjectTreeViewProvider) {
|
constructor(private apiWrapper: ApiWrapper, projTreeViewProvider: SqlDatabaseProjectTreeViewProvider) {
|
||||||
this.projectTreeViewProvider = projTreeViewProvider;
|
this.projectTreeViewProvider = projTreeViewProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,29 +36,29 @@ export class ProjectsController {
|
|||||||
this.projectTreeViewProvider.load(this.projects);
|
this.projectTreeViewProvider.load(this.projects);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openProject(projectFile: vscode.Uri): Promise<Project> {
|
public async openProject(projectFile: Uri): Promise<Project> {
|
||||||
for (const proj of this.projects) {
|
for (const proj of this.projects) {
|
||||||
if (proj.projectFilePath === projectFile.fsPath) {
|
if (proj.projectFilePath === projectFile.fsPath) {
|
||||||
vscode.window.showInformationMessage(constants.projectAlreadyOpened(projectFile.fsPath));
|
this.apiWrapper.showInformationMessage(constants.projectAlreadyOpened(projectFile.fsPath));
|
||||||
return proj;
|
return proj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read project file
|
|
||||||
const newProject = new Project(projectFile.fsPath);
|
const newProject = new Project(projectFile.fsPath);
|
||||||
await newProject.readProjFile();
|
|
||||||
this.projects.push(newProject);
|
|
||||||
|
|
||||||
// Read datasources.json (if present)
|
|
||||||
const dataSourcesFilePath = path.join(path.dirname(projectFile.fsPath), constants.dataSourcesFileName);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Read project file
|
||||||
|
await newProject.readProjFile();
|
||||||
|
this.projects.push(newProject);
|
||||||
|
|
||||||
|
// Read datasources.json (if present)
|
||||||
|
const dataSourcesFilePath = path.join(path.dirname(projectFile.fsPath), constants.dataSourcesFileName);
|
||||||
|
|
||||||
newProject.dataSources = await dataSources.load(dataSourcesFilePath);
|
newProject.dataSources = await dataSources.load(dataSourcesFilePath);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
if (err instanceof dataSources.NoDataSourcesFileError) {
|
if (err instanceof dataSources.NoDataSourcesFileError) {
|
||||||
// TODO: prompt to create new datasources.json; for now, swallow
|
// TODO: prompt to create new datasources.json; for now, swallow
|
||||||
console.log(`No ${constants.dataSourcesFileName} file found.`);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -69,7 +70,7 @@ export class ProjectsController {
|
|||||||
return newProject;
|
return newProject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createNewProject(newProjName: string, folderUri: vscode.Uri, projectGuid?: string): Promise<string> {
|
public async createNewProject(newProjName: string, folderUri: Uri, projectGuid?: string): Promise<string> {
|
||||||
if (projectGuid && !UUID.isUUID(projectGuid)) {
|
if (projectGuid && !UUID.isUUID(projectGuid)) {
|
||||||
throw new Error(`Specified GUID is invalid: '${projectGuid}'`);
|
throw new Error(`Specified GUID is invalid: '${projectGuid}'`);
|
||||||
}
|
}
|
||||||
@@ -112,6 +113,21 @@ export class ProjectsController {
|
|||||||
this.refreshProjectsTree();
|
this.refreshProjectsTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async build(treeNode: BaseProjectTreeItem) {
|
||||||
|
const project = this.getProjectContextFromTreeNode(treeNode);
|
||||||
|
await this.apiWrapper.showErrorMessage(`Build not yet implemented: ${project.projectFilePath}`); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deploy(treeNode: BaseProjectTreeItem) {
|
||||||
|
const project = this.getProjectContextFromTreeNode(treeNode);
|
||||||
|
await this.apiWrapper.showErrorMessage(`Deploy not yet implemented: ${project.projectFilePath}`); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public async import(treeNode: BaseProjectTreeItem) {
|
||||||
|
const project = this.getProjectContextFromTreeNode(treeNode);
|
||||||
|
await this.apiWrapper.showErrorMessage(`Import not yet implemented: ${project.projectFilePath}`); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
public async addFolderPrompt(treeNode: BaseProjectTreeItem) {
|
public async addFolderPrompt(treeNode: BaseProjectTreeItem) {
|
||||||
const project = this.getProjectContextFromTreeNode(treeNode);
|
const project = this.getProjectContextFromTreeNode(treeNode);
|
||||||
const newFolderName = await this.promptForNewObjectName(new templates.ProjectScriptType(templates.folder, constants.folderFriendlyName, ''), project);
|
const newFolderName = await this.promptForNewObjectName(new templates.ProjectScriptType(templates.folder, constants.folderFriendlyName, ''), project);
|
||||||
@@ -120,26 +136,28 @@ export class ProjectsController {
|
|||||||
return; // user cancelled
|
return; // user cancelled
|
||||||
}
|
}
|
||||||
|
|
||||||
const relativeFolderPath = this.prependContextPath(treeNode, newFolderName);
|
const relativeFolderPath = path.join(this.getRelativePath(treeNode), newFolderName);
|
||||||
|
|
||||||
await project.addFolderItem(relativeFolderPath);
|
await project.addFolderItem(relativeFolderPath);
|
||||||
|
|
||||||
this.refreshProjectsTree();
|
this.refreshProjectsTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async addItemPrompt(treeNode: BaseProjectTreeItem, itemTypeName?: string) {
|
public async addItemPromptFromNode(treeNode: BaseProjectTreeItem, itemTypeName?: string) {
|
||||||
const project = this.getProjectContextFromTreeNode(treeNode);
|
await this.addItemPrompt(this.getProjectContextFromTreeNode(treeNode), this.getRelativePath(treeNode), itemTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addItemPrompt(project: Project, relativePath: string, itemTypeName?: string) {
|
||||||
if (!itemTypeName) {
|
if (!itemTypeName) {
|
||||||
let itemFriendlyNames: string[] = [];
|
const items: QuickPickItem[] = [];
|
||||||
|
|
||||||
for (const itemType of templates.projectScriptTypes()) {
|
for (const itemType of templates.projectScriptTypes()) {
|
||||||
itemFriendlyNames.push(itemType.friendlyName);
|
items.push({ label: itemType.friendlyName });
|
||||||
}
|
}
|
||||||
|
|
||||||
itemTypeName = await vscode.window.showQuickPick(itemFriendlyNames, {
|
itemTypeName = (await this.apiWrapper.showQuickPick(items, {
|
||||||
canPickMany: false
|
canPickMany: false
|
||||||
});
|
}))?.label;
|
||||||
|
|
||||||
if (!itemTypeName) {
|
if (!itemTypeName) {
|
||||||
return; // user cancelled
|
return; // user cancelled
|
||||||
@@ -147,7 +165,9 @@ export class ProjectsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const itemType = templates.projectScriptTypeMap()[itemTypeName.toLocaleLowerCase()];
|
const itemType = templates.projectScriptTypeMap()[itemTypeName.toLocaleLowerCase()];
|
||||||
const itemObjectName = await this.promptForNewObjectName(itemType, project);
|
let itemObjectName = await this.promptForNewObjectName(itemType, project);
|
||||||
|
|
||||||
|
itemObjectName = itemObjectName?.trim();
|
||||||
|
|
||||||
if (!itemObjectName) {
|
if (!itemObjectName) {
|
||||||
return; // user cancelled
|
return; // user cancelled
|
||||||
@@ -156,11 +176,11 @@ export class ProjectsController {
|
|||||||
// TODO: file already exists?
|
// TODO: file already exists?
|
||||||
|
|
||||||
const newFileText = this.macroExpansion(itemType.templateScript, { 'OBJECT_NAME': itemObjectName });
|
const newFileText = this.macroExpansion(itemType.templateScript, { 'OBJECT_NAME': itemObjectName });
|
||||||
const relativeFilePath = this.prependContextPath(treeNode, itemObjectName + '.sql');
|
const relativeFilePath = path.join(relativePath, itemObjectName + '.sql');
|
||||||
|
|
||||||
const newEntry = await project.addScriptItem(relativeFilePath, newFileText);
|
const newEntry = await project.addScriptItem(relativeFilePath, newFileText);
|
||||||
|
|
||||||
vscode.commands.executeCommand('vscode.open', newEntry.fsUri);
|
this.apiWrapper.executeCommand('vscode.open', newEntry.fsUri);
|
||||||
|
|
||||||
this.refreshProjectsTree();
|
this.refreshProjectsTree();
|
||||||
}
|
}
|
||||||
@@ -193,7 +213,7 @@ export class ProjectsController {
|
|||||||
return (treeNode.root as ProjectRootTreeItem).project;
|
return (treeNode.root as ProjectRootTreeItem).project;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new Error('"Add item" command invoked from unexpected location: ' + treeNode.uri.path);
|
throw new Error('Unable to establish project context. Command invoked from unexpected location: ' + treeNode.uri.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +221,7 @@ export class ProjectsController {
|
|||||||
// TODO: ask project for suggested name that doesn't conflict
|
// TODO: ask project for suggested name that doesn't conflict
|
||||||
const suggestedName = itemType.friendlyName.replace(new RegExp('\s', 'g'), '') + '1';
|
const suggestedName = itemType.friendlyName.replace(new RegExp('\s', 'g'), '') + '1';
|
||||||
|
|
||||||
const itemObjectName = await vscode.window.showInputBox({
|
const itemObjectName = await this.apiWrapper.showInputBox({
|
||||||
prompt: constants.newObjectNamePrompt(itemType.friendlyName),
|
prompt: constants.newObjectNamePrompt(itemType.friendlyName),
|
||||||
value: suggestedName,
|
value: suggestedName,
|
||||||
});
|
});
|
||||||
@@ -209,13 +229,8 @@ export class ProjectsController {
|
|||||||
return itemObjectName;
|
return itemObjectName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private prependContextPath(treeNode: BaseProjectTreeItem, objectName: string): string {
|
private getRelativePath(treeNode: BaseProjectTreeItem): string {
|
||||||
if (treeNode instanceof FolderNode) {
|
return treeNode instanceof FolderNode ? utils.trimUri(treeNode.root.uri, treeNode.uri) : '';
|
||||||
return path.join(utils.trimUri(treeNode.root.uri, treeNode.uri), objectName);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return objectName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|||||||
@@ -5,12 +5,13 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import MainController from './controllers/mainController';
|
import MainController from './controllers/mainController';
|
||||||
|
import { ApiWrapper } from './common/apiWrapper';
|
||||||
|
|
||||||
let controllers: MainController[] = [];
|
let controllers: MainController[] = [];
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||||
// Start the main controller
|
// Start the main controller
|
||||||
const mainController = new MainController(context);
|
const mainController = new MainController(context, new ApiWrapper());
|
||||||
controllers.push(mainController);
|
controllers.push(mainController);
|
||||||
context.subscriptions.push(mainController);
|
context.subscriptions.push(mainController);
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user