mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-19 18:46:52 -05:00
Compare commits
173 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96e2145931 | ||
|
|
5845135366 | ||
|
|
4b05fa9075 | ||
|
|
bd1f133ca5 | ||
|
|
944a8f9c72 | ||
|
|
441b57f3ca | ||
|
|
803b02be81 | ||
|
|
d76bba1458 | ||
|
|
9b40838ea3 | ||
|
|
6c98603979 | ||
|
|
0e2d1e515e | ||
|
|
bfb03d160e | ||
|
|
ff21a93e00 | ||
|
|
763080aea0 | ||
|
|
fb713e0762 | ||
|
|
a8d41a6717 | ||
|
|
34bc0efc1c | ||
|
|
23e4a30cd1 | ||
|
|
ba58b0f429 | ||
|
|
89e6e062ab | ||
|
|
33ff8ec5a3 | ||
|
|
9f4053d051 | ||
|
|
1bb9d142f1 | ||
|
|
b10bd70d67 | ||
|
|
dc2ff2295e | ||
|
|
f231d6945f | ||
|
|
8786caf630 | ||
|
|
5c520bc82e | ||
|
|
1ccf408654 | ||
|
|
76ebfe38a1 | ||
|
|
364206010b | ||
|
|
559c675164 | ||
|
|
1773dede25 | ||
|
|
fa52478ffa | ||
|
|
ae0603c041 | ||
|
|
a364af5c4c | ||
|
|
6b76611c93 | ||
|
|
d7df71c8ba | ||
|
|
ea464abaaf | ||
|
|
a026b682c4 | ||
|
|
8e3fa660fd | ||
|
|
c9257822ec | ||
|
|
783bb8bd92 | ||
|
|
bf19ab6ad9 | ||
|
|
c4e59027fc | ||
|
|
f9b7bc26c0 | ||
|
|
a197cd6158 | ||
|
|
ae5b506848 | ||
|
|
f8ab5fef78 | ||
|
|
0893ba33fc | ||
|
|
75ce8d6c20 | ||
|
|
6d56701b5b | ||
|
|
ea8aa92dd5 | ||
|
|
9fc634dfe0 | ||
|
|
4b1088edbc | ||
|
|
8e355a14e9 | ||
|
|
ef2b6f91f1 | ||
|
|
9cae7a0a49 | ||
|
|
9a55ca3021 | ||
|
|
e8b20c86c1 | ||
|
|
dc78a4af88 | ||
|
|
34273907f7 | ||
|
|
ca1c5899a1 | ||
|
|
0064f17ad4 | ||
|
|
f8ccafd2af | ||
|
|
d386311e54 | ||
|
|
0e2475aa72 | ||
|
|
b4cce9f147 | ||
|
|
3e8f25d864 | ||
|
|
4c68580e82 | ||
|
|
3bc82c10b1 | ||
|
|
c7d94055a4 | ||
|
|
34316b0ffd | ||
|
|
3d494dcd73 | ||
|
|
85b2c4de4a | ||
|
|
d0ce6bb066 | ||
|
|
38c6495fd8 | ||
|
|
bcc449b524 | ||
|
|
361ada4963 | ||
|
|
2e8d62d0ca | ||
|
|
fb97bf6041 | ||
|
|
6d9c95720d | ||
|
|
db5a0a892a | ||
|
|
7b01a6ca61 | ||
|
|
fbbf767700 | ||
|
|
776e2cf6e7 | ||
|
|
34ca0e8671 | ||
|
|
0d37047c37 | ||
|
|
25c8f60e6e | ||
|
|
aae1480e4f | ||
|
|
5e8a52bcc0 | ||
|
|
cf8f8907ee | ||
|
|
ca36f20c6b | ||
|
|
bdae02e51e | ||
|
|
2d7eb0dcb5 | ||
|
|
2e0756ad8d | ||
|
|
ef2bbce34b | ||
|
|
65e59dc57d | ||
|
|
2ca39b571a | ||
|
|
48a6157efb | ||
|
|
ab3a64604a | ||
|
|
e9ddf43c6c | ||
|
|
2549d91ddf | ||
|
|
81ae86ff79 | ||
|
|
7670104e4d | ||
|
|
3fc2ad5bc9 | ||
|
|
c84367e2ee | ||
|
|
77413ad25c | ||
|
|
c1f73255b5 | ||
|
|
fe7ec76cd5 | ||
|
|
0bc9849ad8 | ||
|
|
c9a4f8f664 | ||
|
|
1315b8e42a | ||
|
|
7a03da42ec | ||
|
|
8b9bb2a8fc | ||
|
|
162dfbaab0 | ||
|
|
71b6e35231 | ||
|
|
9268513128 | ||
|
|
5d44b6a6a7 | ||
|
|
586fe10525 | ||
|
|
a59d1d3c05 | ||
|
|
1fce604a11 | ||
|
|
8ea831c845 | ||
|
|
94061fa634 | ||
|
|
fe17955fa1 | ||
|
|
3158e9f63a | ||
|
|
fcb2b53bf8 | ||
|
|
5470702c16 | ||
|
|
8c05c6e122 | ||
|
|
cc397a012f | ||
|
|
e12827de3f | ||
|
|
02a646c7ea | ||
|
|
86986efb15 | ||
|
|
a718fb3cae | ||
|
|
48ba9ce175 | ||
|
|
be60ad6766 | ||
|
|
b1b58f2550 | ||
|
|
99d00e2057 | ||
|
|
7da0dddaa9 | ||
|
|
3ea45e4ef5 | ||
|
|
dbac187b44 | ||
|
|
43293b98c0 | ||
|
|
34eef8e97d | ||
|
|
5a48fd80cd | ||
|
|
5260afc15d | ||
|
|
66b4e08026 | ||
|
|
2b8e0cc6c4 | ||
|
|
a501214bfa | ||
|
|
2b9a8b9136 | ||
|
|
1cc3cb5408 | ||
|
|
7df793f208 | ||
|
|
ca2b7cc4bc | ||
|
|
9e0a74da3d | ||
|
|
da5076a4dc | ||
|
|
005808f003 | ||
|
|
1dd1919200 | ||
|
|
0a9db55dc4 | ||
|
|
4551ba5b7c | ||
|
|
0f25ad5676 | ||
|
|
c6564c0d84 | ||
|
|
a7e94d433f | ||
|
|
64480a35ac | ||
|
|
4f96d5caf2 | ||
|
|
7cca1b9f48 | ||
|
|
56ebbedbfd | ||
|
|
4e5b8ce875 | ||
|
|
5c5ee50983 | ||
|
|
0a67488447 | ||
|
|
022761aa4b | ||
|
|
b9d985b663 | ||
|
|
08ed9d285e | ||
|
|
ab0cd71d10 | ||
|
|
df7645e4e5 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
.DS_Store
|
||||
.cache
|
||||
npm-debug.log
|
||||
Thumbs.db
|
||||
node_modules/
|
||||
@@ -14,7 +15,10 @@ out-editor-min/
|
||||
out-monaco-editor-core/
|
||||
out-vscode/
|
||||
out-vscode-min/
|
||||
build/node_modules
|
||||
out-vscode-reh/
|
||||
out-vscode-reh-min/
|
||||
out-vscode-reh-pkg/
|
||||
**/node_modules
|
||||
coverage/
|
||||
test_data/
|
||||
test-results/
|
||||
|
||||
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,5 +1,18 @@
|
||||
# Change Log
|
||||
|
||||
## Version 1.7.0
|
||||
* Release date: May 8, 2019
|
||||
* Release status: General Availability
|
||||
|
||||
## What's new in this version
|
||||
* Announcing Schema Compare *Preview* extension
|
||||
* Tasks Panel UX improvement
|
||||
* Announcing new Welcome page
|
||||
* Resolved [bugs and issues](https://github.com/microsoft/azuredatastudio/milestone/31?closed=1).
|
||||
|
||||
## Contributions and "thank you"
|
||||
We would like to thank all our users who raised issues.
|
||||
|
||||
## Version 1.6.0
|
||||
* Release date: April 18, 2019
|
||||
* Release status: General Availability
|
||||
|
||||
14
README.md
14
README.md
@@ -9,13 +9,13 @@ Azure Data Studio is a data management tool that enables you to work with SQL Se
|
||||
|
||||
Platform | Link
|
||||
-- | --
|
||||
Windows User Installer | https://go.microsoft.com/fwlink/?linkid=2087316
|
||||
Windows System Installer | https://go.microsoft.com/fwlink/?linkid=2087317
|
||||
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=2087318
|
||||
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=2087170
|
||||
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=2087414
|
||||
Linux RPM | https://go.microsoft.com/fwlink/?linkid=2087171
|
||||
Linux DEB | https://go.microsoft.com/fwlink/?linkid=2087415
|
||||
Windows User Installer | https://go.microsoft.com/fwlink/?linkid=2091882
|
||||
Windows System Installer | https://go.microsoft.com/fwlink/?linkid=2091491
|
||||
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=2091490
|
||||
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=2091489
|
||||
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=2091488
|
||||
Linux RPM | https://go.microsoft.com/fwlink/?linkid=2091487
|
||||
Linux DEB | https://go.microsoft.com/fwlink/?linkid=2092022
|
||||
|
||||
Go to our [download page](https://aka.ms/azuredatastudio) for more specific instructions.
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '8.x'
|
||||
versionSpec: '10.15.1'
|
||||
displayName: 'Install Node.js'
|
||||
|
||||
- script: |
|
||||
git submodule update --init --recursive
|
||||
nvm install 10.15.1
|
||||
nvm use 10.15.1
|
||||
npm i -g yarn
|
||||
displayName: 'preinstall'
|
||||
|
||||
|
||||
126
build/.nativeignore
Normal file
126
build/.nativeignore
Normal file
@@ -0,0 +1,126 @@
|
||||
# cleanup rules for native node modules, .gitignore style
|
||||
|
||||
fsevents/binding.gyp
|
||||
fsevents/fsevents.cc
|
||||
fsevents/build/**
|
||||
fsevents/src/**
|
||||
fsevents/test/**
|
||||
!fsevents/**/*.node
|
||||
|
||||
vscode-sqlite3/binding.gyp
|
||||
vscode-sqlite3/benchmark/**
|
||||
vscode-sqlite3/cloudformation/**
|
||||
vscode-sqlite3/deps/**
|
||||
vscode-sqlite3/test/**
|
||||
vscode-sqlite3/build/**
|
||||
vscode-sqlite3/src/**
|
||||
!vscode-sqlite3/build/Release/*.node
|
||||
|
||||
oniguruma/binding.gyp
|
||||
oniguruma/build/**
|
||||
oniguruma/src/**
|
||||
oniguruma/deps/**
|
||||
!oniguruma/build/Release/*.node
|
||||
!oniguruma/src/*.js
|
||||
|
||||
windows-mutex/binding.gyp
|
||||
windows-mutex/build/**
|
||||
windows-mutex/src/**
|
||||
!windows-mutex/**/*.node
|
||||
|
||||
native-keymap/binding.gyp
|
||||
native-keymap/build/**
|
||||
native-keymap/src/**
|
||||
native-keymap/deps/**
|
||||
!native-keymap/build/Release/*.node
|
||||
|
||||
native-is-elevated/binding.gyp
|
||||
native-is-elevated/build/**
|
||||
native-is-elevated/src/**
|
||||
native-is-elevated/deps/**
|
||||
!native-is-elevated/build/Release/*.node
|
||||
|
||||
native-watchdog/binding.gyp
|
||||
native-watchdog/build/**
|
||||
native-watchdog/src/**
|
||||
!native-watchdog/build/Release/*.node
|
||||
|
||||
spdlog/binding.gyp
|
||||
spdlog/build/**
|
||||
spdlog/deps/**
|
||||
spdlog/src/**
|
||||
spdlog/test/**
|
||||
!spdlog/build/Release/*.node
|
||||
|
||||
jschardet/dist/**
|
||||
|
||||
windows-foreground-love/binding.gyp
|
||||
windows-foreground-love/build/**
|
||||
windows-foreground-love/src/**
|
||||
!windows-foreground-love/**/*.node
|
||||
|
||||
windows-process-tree/binding.gyp
|
||||
windows-process-tree/build/**
|
||||
windows-process-tree/src/**
|
||||
!windows-process-tree/**/*.node
|
||||
|
||||
gc-signals/binding.gyp
|
||||
gc-signals/build/**
|
||||
gc-signals/src/**
|
||||
gc-signals/deps/**
|
||||
|
||||
!gc-signals/build/Release/*.node
|
||||
!gc-signals/src/index.js
|
||||
|
||||
keytar/binding.gyp
|
||||
keytar/build/**
|
||||
keytar/src/**
|
||||
keytar/script/**
|
||||
keytar/node_modules/**
|
||||
!keytar/**/*.node
|
||||
|
||||
node-pty/binding.gyp
|
||||
node-pty/build/**
|
||||
node-pty/src/**
|
||||
node-pty/tools/**
|
||||
!node-pty/build/Release/*.exe
|
||||
!node-pty/build/Release/*.dll
|
||||
!node-pty/build/Release/*.node
|
||||
|
||||
chart.js/node_modules/**
|
||||
|
||||
emmet/node_modules/**
|
||||
|
||||
pty.js/build/**
|
||||
!pty.js/build/Release/**
|
||||
|
||||
jquery-ui/external/**
|
||||
jquery-ui/demos/**
|
||||
|
||||
core-js/**/**
|
||||
|
||||
slickgrid/node_modules/**
|
||||
slickgrid/examples/**
|
||||
|
||||
vscode-nsfw/binding.gyp
|
||||
vscode-nsfw/build/**
|
||||
vscode-nsfw/src/**
|
||||
vscode-nsfw/openpa/**
|
||||
vscode-nsfw/includes/**
|
||||
!vscode-nsfw/build/Release/*.node
|
||||
!vscode-nsfw/**/*.a
|
||||
|
||||
vsda/binding.gyp
|
||||
vsda/README.md
|
||||
vsda/build/**
|
||||
vsda/*.bat
|
||||
vsda/*.sh
|
||||
vsda/*.cpp
|
||||
vsda/*.h
|
||||
!vsda/build/Release/vsda.node
|
||||
|
||||
vscode-windows-ca-certs/**/*
|
||||
!vscode-windows-ca-certs/package.json
|
||||
!vscode-windows-ca-certs/**/*.node
|
||||
|
||||
node-addon-api/**/*
|
||||
@@ -3,10 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
|
||||
function yarnInstall(packageName: string): void {
|
||||
cp.execSync(`yarn add --no-lockfile ${packageName}`);
|
||||
cp.execSync(`yarn add --no-lockfile ${packageName}`, { cwd: path.join( process.cwd(), 'remote') });
|
||||
}
|
||||
|
||||
const product = require('../../../product.json');
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
yarn gulp vscode-darwin-min
|
||||
yarn gulp vscode-reh-darwin-min
|
||||
yarn gulp upload-vscode-sourcemaps
|
||||
@@ -7,15 +7,21 @@ steps:
|
||||
inputs:
|
||||
versionSpec: "1.10.1"
|
||||
|
||||
- task: AzureKeyVault@1
|
||||
displayName: 'Azure Key Vault: Get Secrets'
|
||||
inputs:
|
||||
azureSubscription: 'vscode-builds-subscription'
|
||||
KeyVaultName: vscode
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
cat << EOF > ~/.netrc
|
||||
machine monacotools.visualstudio.com
|
||||
password $(VSO_PAT)
|
||||
password $(devops-pat)
|
||||
machine github.com
|
||||
login vscode
|
||||
password $(VSCODE_MIXIN_PASSWORD)
|
||||
password $(github-distro-mixin-password)
|
||||
EOF
|
||||
|
||||
git config user.email "vscode@microsoft.com"
|
||||
@@ -34,8 +40,8 @@ steps:
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \
|
||||
AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \
|
||||
VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \
|
||||
AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \
|
||||
./build/azure-pipelines/darwin/build.sh
|
||||
displayName: Build
|
||||
|
||||
@@ -77,11 +83,11 @@ steps:
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
|
||||
AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
|
||||
VSCODE_HOCKEYAPP_TOKEN="$(VSCODE_HOCKEYAPP_TOKEN)" \
|
||||
VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \
|
||||
AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \
|
||||
VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \
|
||||
./build/azure-pipelines/darwin/publish.sh
|
||||
displayName: Publish
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# remove pkg from archive
|
||||
zip -d ../VSCode-darwin.zip "*.pkg"
|
||||
@@ -15,6 +16,19 @@ node build/azure-pipelines/common/publish.js \
|
||||
true \
|
||||
../VSCode-darwin.zip
|
||||
|
||||
# package Remote Extension Host
|
||||
pushd .. && mv vscode-reh-darwin vscode-server-darwin && zip -Xry vscode-server-darwin.zip vscode-server-darwin && popd
|
||||
|
||||
# publish Remote Extension Host
|
||||
node build/azure-pipelines/common/publish.js \
|
||||
"$VSCODE_QUALITY" \
|
||||
server-darwin \
|
||||
archive-unsigned \
|
||||
"vscode-server-darwin.zip" \
|
||||
$VERSION \
|
||||
true \
|
||||
../vscode-server-darwin.zip
|
||||
|
||||
# publish hockeyapp symbols
|
||||
node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "$VSCODE_ARCH" "$VSCODE_HOCKEYAPP_ID_MACOS"
|
||||
|
||||
|
||||
@@ -10,13 +10,19 @@ steps:
|
||||
inputs:
|
||||
versionSpec: "10.15.1"
|
||||
|
||||
- task: AzureKeyVault@1
|
||||
displayName: 'Azure Key Vault: Get Secrets'
|
||||
inputs:
|
||||
azureSubscription: 'vscode-builds-subscription'
|
||||
KeyVaultName: vscode
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
cat << EOF > ~/.netrc
|
||||
machine github.com
|
||||
login vscode
|
||||
password $(VSCODE_MIXIN_PASSWORD)
|
||||
password $(github-distro-mixin-password)
|
||||
EOF
|
||||
|
||||
git config user.email "vscode@microsoft.com"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
yarn gulp "vscode-linux-$VSCODE_ARCH-min"
|
||||
yarn gulp "vscode-linux-$VSCODE_ARCH-min"
|
||||
|
||||
if [[ "$VSCODE_ARCH" != "ia32" ]]; then
|
||||
yarn gulp vscode-reh-linux-$VSCODE_ARCH-min
|
||||
fi
|
||||
@@ -7,6 +7,12 @@ steps:
|
||||
inputs:
|
||||
versionSpec: "1.10.1"
|
||||
|
||||
- task: AzureKeyVault@1
|
||||
displayName: 'Azure Key Vault: Get Secrets'
|
||||
inputs:
|
||||
azureSubscription: 'vscode-builds-subscription'
|
||||
KeyVaultName: vscode
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
export npm_config_arch="$(VSCODE_ARCH)"
|
||||
@@ -16,10 +22,10 @@ steps:
|
||||
|
||||
cat << EOF > ~/.netrc
|
||||
machine monacotools.visualstudio.com
|
||||
password $(VSO_PAT)
|
||||
password $(devops-pat)
|
||||
machine github.com
|
||||
login vscode
|
||||
password $(VSCODE_MIXIN_PASSWORD)
|
||||
password $(github-distro-mixin-password)
|
||||
EOF
|
||||
|
||||
git config user.email "vscode@microsoft.com"
|
||||
@@ -38,7 +44,7 @@ steps:
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \
|
||||
VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \
|
||||
./build/azure-pipelines/linux/build.sh
|
||||
displayName: Build
|
||||
|
||||
@@ -55,10 +61,10 @@ steps:
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
|
||||
VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \
|
||||
VSCODE_HOCKEYAPP_TOKEN="$(VSCODE_HOCKEYAPP_TOKEN)" \
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \
|
||||
VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \
|
||||
VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \
|
||||
./build/azure-pipelines/linux/publish.sh
|
||||
displayName: Publish
|
||||
|
||||
|
||||
@@ -20,6 +20,19 @@ rm -rf $ROOT/code-*.tar.*
|
||||
|
||||
node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_LINUX" archive-unsigned "$TARBALL_FILENAME" "$VERSION" true "$TARBALL_PATH"
|
||||
|
||||
# Publish Remote Extension Host
|
||||
if [[ "$VSCODE_ARCH" != "ia32" ]]; then
|
||||
LEGACY_SERVER_BUILD_NAME="vscode-reh-$PLATFORM_LINUX"
|
||||
SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX"
|
||||
SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX.tar.gz"
|
||||
SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME"
|
||||
|
||||
rm -rf $ROOT/vscode-server-*.tar.*
|
||||
(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME)
|
||||
|
||||
node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "server-$PLATFORM_LINUX" archive-unsigned "$SERVER_TARBALL_FILENAME" "$VERSION" true "$SERVER_TARBALL_PATH"
|
||||
fi
|
||||
|
||||
# Publish hockeyapp symbols
|
||||
node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "$VSCODE_ARCH" "$VSCODE_HOCKEYAPP_ID_LINUX64"
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@ steps:
|
||||
inputs:
|
||||
versionSpec: "1.10.1"
|
||||
|
||||
- task: AzureKeyVault@1
|
||||
displayName: 'Azure Key Vault: Get Secrets'
|
||||
inputs:
|
||||
azureSubscription: 'vscode-builds-subscription'
|
||||
KeyVaultName: vscode
|
||||
|
||||
- task: DownloadPipelineArtifact@0
|
||||
displayName: 'Download Pipeline Artifact'
|
||||
inputs:
|
||||
@@ -44,6 +50,6 @@ steps:
|
||||
(cd $SNAP_ROOT/code-* && sudo snapcraft snap --output "$SNAP_PATH")
|
||||
|
||||
# Publish snap package
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \
|
||||
node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "linux-snap-$ARCH" package "$SNAP_FILENAME" "$VERSION" true "$SNAP_PATH"
|
||||
@@ -1,9 +1,11 @@
|
||||
resources:
|
||||
containers:
|
||||
- container: vscode-x64
|
||||
image: joaomoreno/vscode-linux-build-agent:x64
|
||||
endpoint: VSCodeHub
|
||||
image: vscodehub.azurecr.io/vscode-linux-build-agent:x64
|
||||
- container: vscode-ia32
|
||||
image: joaomoreno/vscode-linux-build-agent:ia32
|
||||
endpoint: VSCodeHub
|
||||
image: vscodehub.azurecr.io/vscode-linux-build-agent:ia32
|
||||
- container: snapcraft
|
||||
image: snapcore/snapcraft
|
||||
|
||||
|
||||
2
build/azure-pipelines/publish-types/.gitignore
vendored
Normal file
2
build/azure-pipelines/publish-types/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
*.js
|
||||
36
build/azure-pipelines/publish-types/check-version.js
Normal file
36
build/azure-pipelines/publish-types/check-version.js
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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const cp = require("child_process");
|
||||
let tag = '';
|
||||
try {
|
||||
tag = cp
|
||||
.execSync('git describe --tags `git rev-list --tags --max-count=1`')
|
||||
.toString()
|
||||
.trim();
|
||||
if (!isValidTag(tag)) {
|
||||
throw Error(`Invalid tag ${tag}`);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
console.error('Failed to update types');
|
||||
process.exit(1);
|
||||
}
|
||||
function isValidTag(t) {
|
||||
if (t.split('.').length !== 3) {
|
||||
return false;
|
||||
}
|
||||
const [major, minor, bug] = t.split('.');
|
||||
// Only release for tags like 1.34.0
|
||||
if (bug !== '0') {
|
||||
return false;
|
||||
}
|
||||
if (parseInt(major, 10) === NaN || parseInt(minor, 10) === NaN) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
43
build/azure-pipelines/publish-types/check-version.ts
Normal file
43
build/azure-pipelines/publish-types/check-version.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as cp from 'child_process';
|
||||
|
||||
let tag = '';
|
||||
try {
|
||||
tag = cp
|
||||
.execSync('git describe --tags `git rev-list --tags --max-count=1`')
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
if (!isValidTag(tag)) {
|
||||
throw Error(`Invalid tag ${tag}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.error('Failed to update types');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function isValidTag(t: string) {
|
||||
if (t.split('.').length !== 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const [major, minor, bug] = t.split('.');
|
||||
|
||||
// Only release for tags like 1.34.0
|
||||
if (bug !== '0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parseInt(major, 10) === NaN || parseInt(minor, 10) === NaN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
67
build/azure-pipelines/publish-types/publish-types.yml
Normal file
67
build/azure-pipelines/publish-types/publish-types.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
# Publish @types/vscode for each release
|
||||
|
||||
trigger:
|
||||
branches:
|
||||
include: ['refs/tags/*']
|
||||
|
||||
pr: none
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: "10.15.1"
|
||||
|
||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
|
||||
inputs:
|
||||
versionSpec: "1.10.1"
|
||||
|
||||
- bash: |
|
||||
# Install build dependencies
|
||||
(cd build && yarn)
|
||||
node build/azure-pipelines/publish-types/check-version.js
|
||||
displayName: Check version
|
||||
|
||||
- bash: |
|
||||
git config --global user.email "vscode@microsoft.com"
|
||||
git config --global user.name "VSCode"
|
||||
|
||||
git clone https://$(GITHUB_TOKEN)@github.com/DefinitelyTyped/DefinitelyTyped.git --depth=1
|
||||
node build/azure-pipelines/publish-types/update-types.js
|
||||
|
||||
TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
|
||||
cd DefinitelyTyped
|
||||
|
||||
git diff --color | cat
|
||||
git add -A
|
||||
git status
|
||||
git checkout -b "vscode-types-$TAG_VERSION"
|
||||
git commit -m "VS Code $TAG_VERSION Extension API"
|
||||
git push origin "vscode-types-$TAG_VERSION"
|
||||
|
||||
displayName: Push update to DefinitelyTyped
|
||||
|
||||
- bash: |
|
||||
TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
CHANNEL="G1C14HJ2F"
|
||||
|
||||
MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:"
|
||||
LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details."
|
||||
MESSAGE2="[@octref, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode."
|
||||
|
||||
curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
||||
-H 'Content-type: application/json; charset=utf-8' \
|
||||
--data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \
|
||||
https://slack.com/api/chat.postMessage
|
||||
|
||||
curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
||||
-H 'Content-type: application/json; charset=utf-8' \
|
||||
--data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$LINK"'"}' \
|
||||
https://slack.com/api/chat.postMessage
|
||||
|
||||
curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
||||
-H 'Content-type: application/json; charset=utf-8' \
|
||||
--data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE2"'"}' \
|
||||
https://slack.com/api/chat.postMessage
|
||||
|
||||
displayName: Send message on Slack
|
||||
62
build/azure-pipelines/publish-types/update-types.js
Normal file
62
build/azure-pipelines/publish-types/update-types.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = require("fs");
|
||||
const cp = require("child_process");
|
||||
const path = require("path");
|
||||
let tag = '';
|
||||
try {
|
||||
tag = cp
|
||||
.execSync('git describe --tags `git rev-list --tags --max-count=1`')
|
||||
.toString()
|
||||
.trim();
|
||||
const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vs/vscode.d.ts`;
|
||||
const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts');
|
||||
cp.execSync(`curl ${dtsUri} --output ${outPath}`);
|
||||
updateDTSFile(outPath, tag);
|
||||
console.log(`Done updating vscode.d.ts at ${outPath}`);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
console.error('Failed to update types');
|
||||
process.exit(1);
|
||||
}
|
||||
function updateDTSFile(outPath, tag) {
|
||||
const oldContent = fs.readFileSync(outPath, 'utf-8');
|
||||
const newContent = getNewFileContent(oldContent, tag);
|
||||
fs.writeFileSync(outPath, newContent);
|
||||
}
|
||||
function getNewFileContent(content, tag) {
|
||||
const oldheader = [
|
||||
`/*---------------------------------------------------------------------------------------------`,
|
||||
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
||||
` * Licensed under the Source EULA. See License.txt in the project root for license information.`,
|
||||
` *--------------------------------------------------------------------------------------------*/`
|
||||
].join('\n');
|
||||
return getNewFileHeader(tag) + content.slice(oldheader.length);
|
||||
}
|
||||
function getNewFileHeader(tag) {
|
||||
const [major, minor] = tag.split('.');
|
||||
const shorttag = `${major}.${minor}`;
|
||||
const header = [
|
||||
`// Type definitions for Visual Studio Code ${shorttag}`,
|
||||
`// Project: https://github.com/microsoft/vscode`,
|
||||
`// Definitions by: Visual Studio Code Team, Microsoft <https://github.com/Microsoft>`,
|
||||
`// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped`,
|
||||
``,
|
||||
`/*---------------------------------------------------------------------------------------------`,
|
||||
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
||||
` * Licensed under the Source EULA.`,
|
||||
` * See https://github.com/Microsoft/vscode/blob/master/LICENSE.txt for license information.`,
|
||||
` *--------------------------------------------------------------------------------------------*/`,
|
||||
``,
|
||||
`/**`,
|
||||
` * Type Definition for Visual Studio Code ${shorttag} Extension API`,
|
||||
` * See https://code.visualstudio.com/api for more information`,
|
||||
` */`
|
||||
].join('\n');
|
||||
return header;
|
||||
}
|
||||
73
build/azure-pipelines/publish-types/update-types.ts
Normal file
73
build/azure-pipelines/publish-types/update-types.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
|
||||
let tag = '';
|
||||
try {
|
||||
tag = cp
|
||||
.execSync('git describe --tags `git rev-list --tags --max-count=1`')
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vs/vscode.d.ts`;
|
||||
const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts');
|
||||
cp.execSync(`curl ${dtsUri} --output ${outPath}`);
|
||||
|
||||
updateDTSFile(outPath, tag);
|
||||
|
||||
console.log(`Done updating vscode.d.ts at ${outPath}`);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.error('Failed to update types');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function updateDTSFile(outPath: string, tag: string) {
|
||||
const oldContent = fs.readFileSync(outPath, 'utf-8');
|
||||
const newContent = getNewFileContent(oldContent, tag);
|
||||
|
||||
fs.writeFileSync(outPath, newContent);
|
||||
}
|
||||
|
||||
function getNewFileContent(content: string, tag: string) {
|
||||
const oldheader = [
|
||||
`/*---------------------------------------------------------------------------------------------`,
|
||||
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
||||
` * Licensed under the Source EULA. See License.txt in the project root for license information.`,
|
||||
` *--------------------------------------------------------------------------------------------*/`
|
||||
].join('\n');
|
||||
|
||||
return getNewFileHeader(tag) + content.slice(oldheader.length);
|
||||
}
|
||||
|
||||
function getNewFileHeader(tag: string) {
|
||||
const [major, minor] = tag.split('.');
|
||||
const shorttag = `${major}.${minor}`;
|
||||
|
||||
const header = [
|
||||
`// Type definitions for Visual Studio Code ${shorttag}`,
|
||||
`// Project: https://github.com/microsoft/vscode`,
|
||||
`// Definitions by: Visual Studio Code Team, Microsoft <https://github.com/Microsoft>`,
|
||||
`// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped`,
|
||||
``,
|
||||
`/*---------------------------------------------------------------------------------------------`,
|
||||
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
||||
` * Licensed under the Source EULA.`,
|
||||
` * See https://github.com/Microsoft/vscode/blob/master/LICENSE.txt for license information.`,
|
||||
` *--------------------------------------------------------------------------------------------*/`,
|
||||
``,
|
||||
`/**`,
|
||||
` * Type Definition for Visual Studio Code ${shorttag} Extension API`,
|
||||
` * See https://code.visualstudio.com/api for more information`,
|
||||
` */`
|
||||
].join('\n');
|
||||
|
||||
return header;
|
||||
}
|
||||
@@ -7,12 +7,18 @@ steps:
|
||||
inputs:
|
||||
versionSpec: "1.10.1"
|
||||
|
||||
- task: AzureKeyVault@1
|
||||
displayName: 'Azure Key Vault: Get Secrets'
|
||||
inputs:
|
||||
azureSubscription: 'vscode-builds-subscription'
|
||||
KeyVaultName: vscode
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
(cd build ; yarn)
|
||||
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \
|
||||
MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \
|
||||
AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \
|
||||
MOONCAKE_STORAGE_ACCESS_KEY="$(vscode-mooncake-storage-key)" \
|
||||
node build/azure-pipelines/common/sync-mooncake.js "$VSCODE_QUALITY"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min" }
|
||||
exec { yarn gulp "vscode-reh-win32-$env:VSCODE_ARCH-min" }
|
||||
exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" }
|
||||
@@ -12,10 +12,16 @@ steps:
|
||||
versionSpec: '2.x'
|
||||
addToPath: true
|
||||
|
||||
- task: AzureKeyVault@1
|
||||
displayName: 'Azure Key Vault: Get Secrets'
|
||||
inputs:
|
||||
azureSubscription: 'vscode-builds-subscription'
|
||||
KeyVaultName: vscode
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
"machine monacotools.visualstudio.com`npassword $(VSO_PAT)`nmachine github.com`nlogin vscode`npassword $(VSCODE_MIXIN_PASSWORD)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII
|
||||
"machine monacotools.visualstudio.com`npassword $(devops-pat)`nmachine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII
|
||||
$env:npm_config_arch="$(VSCODE_ARCH)"
|
||||
$env:CHILD_CONCURRENCY="1"
|
||||
|
||||
@@ -36,7 +42,7 @@ steps:
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)"
|
||||
$env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)"
|
||||
.\build\azure-pipelines\win32\build.ps1
|
||||
displayName: Build
|
||||
|
||||
@@ -126,15 +132,15 @@ steps:
|
||||
|
||||
- powershell: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
.\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY)
|
||||
.\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(esrp-auth-certificate) -AuthCertificateKey $(esrp-auth-certificate-key)
|
||||
displayName: Import ESRP Auth Certificate
|
||||
|
||||
- powershell: |
|
||||
. build/azure-pipelines/win32/exec.ps1
|
||||
$ErrorActionPreference = "Stop"
|
||||
$env:AZURE_STORAGE_ACCESS_KEY_2 = "$(AZURE_STORAGE_ACCESS_KEY_2)"
|
||||
$env:AZURE_DOCUMENTDB_MASTERKEY = "$(AZURE_DOCUMENTDB_MASTERKEY)"
|
||||
$env:VSCODE_HOCKEYAPP_TOKEN = "$(VSCODE_HOCKEYAPP_TOKEN)"
|
||||
$env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)"
|
||||
$env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)"
|
||||
$env:VSCODE_HOCKEYAPP_TOKEN = "$(vscode-hockeyapp-token)"
|
||||
.\build\azure-pipelines\win32\publish.ps1
|
||||
displayName: Publish
|
||||
|
||||
|
||||
@@ -10,8 +10,16 @@ $Root = "$Repo\.."
|
||||
$SystemExe = "$Repo\.build\win32-$Arch\system-setup\VSCodeSetup.exe"
|
||||
$UserExe = "$Repo\.build\win32-$Arch\user-setup\VSCodeSetup.exe"
|
||||
$Zip = "$Repo\.build\win32-$Arch\archive\VSCode-win32-$Arch.zip"
|
||||
$LegacyServer = "$Root\vscode-reh-win32-$Arch"
|
||||
$ServerName = "vscode-server-win32-$Arch"
|
||||
$Server = "$Root\$ServerName"
|
||||
$ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip"
|
||||
$Build = "$Root\VSCode-win32-$Arch"
|
||||
|
||||
# Create server archive
|
||||
exec { Rename-Item -Path $LegacyServer -NewName $ServerName }
|
||||
exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r }
|
||||
|
||||
# get version
|
||||
$PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json
|
||||
$Version = $PackageJson.version
|
||||
@@ -22,6 +30,7 @@ $AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-x64" }
|
||||
exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform-archive" archive "VSCode-win32-$Arch-$Version.zip" $Version true $Zip }
|
||||
exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform" setup "VSCodeSetup-$Arch-$Version.exe" $Version true $SystemExe }
|
||||
exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform-user" setup "VSCodeUserSetup-$Arch-$Version.exe" $Version true $UserExe }
|
||||
exec { node build/azure-pipelines/common/publish.js $Quality "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $Version true $ServerZip }
|
||||
|
||||
# publish hockeyapp symbols
|
||||
$hockeyAppId = if ("$Arch" -eq "ia32") { "$env:VSCODE_HOCKEYAPP_ID_WIN32" } else { "$env:VSCODE_HOCKEYAPP_ID_WIN64" }
|
||||
|
||||
@@ -92,11 +92,11 @@ const indentationFilter = [
|
||||
'!**/*.dockerfile',
|
||||
'!extensions/markdown-language-features/media/*.js',
|
||||
// {{SQL CARBON EDIT}}
|
||||
'!**/*.xlf',
|
||||
'!**/*.docx',
|
||||
'!**/*.sql',
|
||||
'!**/*.{xlf,docx,sql,vsix}',
|
||||
'!extensions/mssql/sqltoolsservice/**',
|
||||
'!extensions/import/flatfileimportservice/**',
|
||||
'!extensions/admin-tool-ext-win/ssmsmin/**',
|
||||
'!extensions/resource-deployment/notebooks/**'
|
||||
];
|
||||
|
||||
const copyrightFilter = [
|
||||
@@ -155,7 +155,8 @@ const copyrightFilter = [
|
||||
'!extensions/mssql/src/prompts/**',
|
||||
'!extensions/notebook/resources/jupyter_config/**',
|
||||
'!**/*.gif',
|
||||
'!**/*.xlf'
|
||||
'!**/*.xlf',
|
||||
'!**/*.dacpac'
|
||||
];
|
||||
|
||||
const eslintFilter = [
|
||||
@@ -184,6 +185,11 @@ const tslintFilter = [
|
||||
'!extensions/html-language-features/server/lib/jquery.d.ts'
|
||||
];
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
const useStrictFilter = [
|
||||
'src/**'
|
||||
];
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
const copyrightHeaderLines = [
|
||||
'/*---------------------------------------------------------------------------------------------',
|
||||
@@ -234,8 +240,8 @@ function hygiene(some) {
|
||||
});
|
||||
|
||||
const copyrights = es.through(function (file) {
|
||||
const lines = file.__lines;
|
||||
|
||||
const lines = file.__lines;
|
||||
for (let i = 0; i < copyrightHeaderLines.length; i++) {
|
||||
if (lines[i] !== copyrightHeaderLines[i]) {
|
||||
console.error(file.relative + ': Missing or bad copyright statement');
|
||||
@@ -247,6 +253,23 @@ function hygiene(some) {
|
||||
this.emit('data', file);
|
||||
});
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Check for unnecessary 'use strict' lines. These are automatically added by the alwaysStrict compiler option so don't need to be added manually
|
||||
const useStrict = es.through(function (file) {
|
||||
const lines = file.__lines;
|
||||
// Only take the first 10 lines to reduce false positives- the compiler will throw an error if it's not the first non-comment line in a file
|
||||
// (10 is used to account for copyright and extraneous newlines)
|
||||
lines.slice(0, 10).forEach((line, i) => {
|
||||
if (/\s*'use\s*strict\s*'/.test(line)) {
|
||||
console.error(file.relative + '(' + (i + 1) + ',1): Unnecessary \'use strict\' - this is already added by the compiler');
|
||||
errorCount++;
|
||||
}
|
||||
});
|
||||
|
||||
this.emit('data', file);
|
||||
});
|
||||
// {{SQL CARBON EDIT}} END
|
||||
|
||||
const formatting = es.map(function (file, cb) {
|
||||
tsfmt.processString(file.path, file.contents.toString('utf8'), {
|
||||
verify: false,
|
||||
@@ -305,7 +328,10 @@ function hygiene(some) {
|
||||
const typescript = result
|
||||
.pipe(filter(tslintFilter))
|
||||
.pipe(formatting)
|
||||
.pipe(tsl);
|
||||
.pipe(tsl)
|
||||
// {{SQL CARBON EDIT}}
|
||||
.pipe(filter(useStrictFilter))
|
||||
.pipe(useStrict);
|
||||
|
||||
const javascript = result
|
||||
.pipe(filter(eslintFilter))
|
||||
@@ -359,7 +385,7 @@ function createGitIndexVinyls(paths) {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
cp.exec(`git show :${relativePath}`, { maxBuffer: 2000 * 1024, encoding: 'buffer' }, (err, out) => {
|
||||
cp.exec(`git show ":${relativePath}"`, { maxBuffer: 2000 * 1024, encoding: 'buffer' }, (err, out) => {
|
||||
if (err) {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ const gulp = require('gulp');
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
const jeditor = require('gulp-json-editor');
|
||||
const product = require('../product.json');
|
||||
|
||||
gulp.task('mixin', function () {
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -25,19 +26,53 @@ gulp.task('mixin', function () {
|
||||
return;
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
let serviceUrl = 'https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery.json';
|
||||
if (quality === 'insider') {
|
||||
serviceUrl = `https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery-${quality}.json`;
|
||||
}
|
||||
// {{SQL CARBON EDIT}} - apply ADS insiders values if needed
|
||||
let newValues = {
|
||||
"nameShort": product.nameShort,
|
||||
"nameLong": product.nameLong,
|
||||
"applicationName": product.applicationName,
|
||||
"dataFolderName": product.dataFolderName,
|
||||
"win32MutexName": product.win32MutexName,
|
||||
"win32DirName": product.win32DirName,
|
||||
"win32NameVersion": product.win32NameVersion,
|
||||
"win32RegValueName": product.win32RegValueName,
|
||||
"win32AppId": product.win32AppId,
|
||||
"win32x64AppId": product.win32x64AppId,
|
||||
"win32UserAppId": product.win32UserAppId,
|
||||
"win32x64UserAppId": product.win32x64UserAppId,
|
||||
"win32AppUserModelId": product.win32AppUserModelId,
|
||||
"win32ShellNameShort": product.win32ShellNameShort,
|
||||
"darwinBundleIdentifier": product.darwinBundleIdentifier,
|
||||
"updateUrl": updateUrl,
|
||||
"quality": quality,
|
||||
"extensionsGallery": {
|
||||
"serviceUrl": serviceUrl
|
||||
"serviceUrl": 'https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery.json'
|
||||
}
|
||||
};
|
||||
|
||||
if (quality === 'insider') {
|
||||
let dashSuffix = '-insiders';
|
||||
let dotSuffix = '.insiders';
|
||||
let displaySuffix = ' - Insiders';
|
||||
|
||||
newValues.extensionsGallery.serviceUrl = `https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery-${quality}.json`;
|
||||
newValues.nameShort += dashSuffix;
|
||||
newValues.nameLong += displaySuffix;
|
||||
newValues.applicationName += dashSuffix;
|
||||
newValues.dataFolderName += dashSuffix;
|
||||
newValues.win32MutexName += dashSuffix;
|
||||
newValues.win32DirName += displaySuffix;
|
||||
newValues.win32NameVersion += displaySuffix;
|
||||
newValues.win32RegValueName += dashSuffix;
|
||||
newValues.win32AppId = "{{9F0801B2-DEE3-4272-A2C6-FBDF25BAAF0F}";
|
||||
newValues.win32x64AppId = "{{6748A5FD-29EB-4BA6-B3C6-E7B981B8D6B0}";
|
||||
newValues.win32UserAppId = "{{0F8CD1ED-483C-40EB-8AD2-8ED784651AA1}";
|
||||
newValues.win32x64UserAppId += dashSuffix;
|
||||
newValues.win32AppUserModelId += dotSuffix;
|
||||
newValues.win32ShellNameShort += displaySuffix;
|
||||
newValues.darwinBundleIdentifier += dotSuffix;
|
||||
}
|
||||
|
||||
return gulp.src('./product.json')
|
||||
.pipe(jeditor(newValues))
|
||||
.pipe(gulp.dest('.'));
|
||||
|
||||
16
build/gulpfile.reh.js
Normal file
16
build/gulpfile.reh.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
|
||||
const noop = () => { return Promise.resolve(); };
|
||||
|
||||
gulp.task('vscode-reh-win32-ia32-min', noop);
|
||||
gulp.task('vscode-reh-win32-x64-min', noop);
|
||||
gulp.task('vscode-reh-darwin-min', noop);
|
||||
gulp.task('vscode-reh-linux-x64-min', noop);
|
||||
gulp.task('vscode-reh-linux-arm-min', noop);
|
||||
@@ -335,33 +335,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
||||
|
||||
const deps = gulp.src(depsSrc, { base: '.', dot: true })
|
||||
.pipe(filter(['**', '!**/package-lock.json']))
|
||||
.pipe(util.cleanNodeModule('fsevents', ['binding.gyp', 'fsevents.cc', 'build/**', 'src/**', 'test/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('vscode-sqlite3', ['binding.gyp', 'benchmark/**', 'cloudformation/**', 'deps/**', 'test/**', 'build/**', 'src/**'], ['build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('oniguruma', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node', 'src/*.js']))
|
||||
.pipe(util.cleanNodeModule('windows-mutex', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-is-elevated', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('native-watchdog', ['binding.gyp', 'build/**', 'src/**'], ['build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('spdlog', ['binding.gyp', 'build/**', 'deps/**', 'src/**', 'test/**'], ['build/Release/*.node']))
|
||||
.pipe(util.cleanNodeModule('jschardet', ['dist/**']))
|
||||
.pipe(util.cleanNodeModule('windows-foreground-love', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('windows-process-tree', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node', 'src/index.js']))
|
||||
.pipe(util.cleanNodeModule('keytar', ['binding.gyp', 'build/**', 'src/**', 'script/**', 'node_modules/**'], ['**/*.node']))
|
||||
.pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'tools/**'], ['build/Release/*.exe', 'build/Release/*.dll', 'build/Release/*.node']))
|
||||
// {{SQL CARBON EDIT}}
|
||||
.pipe(util.cleanNodeModule('chart.js', ['node_modules/**'], undefined))
|
||||
.pipe(util.cleanNodeModule('emmet', ['node_modules/**'], undefined))
|
||||
.pipe(util.cleanNodeModule('pty.js', ['build/**'], ['build/Release/**']))
|
||||
.pipe(util.cleanNodeModule('jquery-ui', ['external/**', 'demos/**'], undefined))
|
||||
.pipe(util.cleanNodeModule('core-js', ['**/**'], undefined))
|
||||
.pipe(util.cleanNodeModule('slickgrid', ['node_modules/**', 'examples/**'], undefined))
|
||||
.pipe(util.cleanNodeModule('nsfw', ['binding.gyp', 'build/**', 'src/**', 'openpa/**', 'includes/**'], ['**/*.node', '**/*.a']))
|
||||
.pipe(util.cleanNodeModule('vscode-nsfw', ['binding.gyp', 'build/**', 'src/**', 'openpa/**', 'includes/**'], ['build/Release/*.node', '**/*.a']))
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
.pipe(util.cleanNodeModule('vsda', ['binding.gyp', 'README.md', 'build/**', '*.bat', '*.sh', '*.cpp', '*.h'], ['build/Release/vsda.node']))
|
||||
.pipe(util.cleanNodeModule('vscode-windows-ca-certs', ['**/*'], ['package.json', '**/*.node']))
|
||||
.pipe(util.cleanNodeModule('node-addon-api', ['**/*']))
|
||||
.pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore')))
|
||||
.pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar'));
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
@@ -380,7 +354,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
|
||||
], { base: '.', dot: true });
|
||||
|
||||
let all = es.merge(
|
||||
packageJsonStream,
|
||||
packageJsonStream,
|
||||
productJsonStream,
|
||||
license,
|
||||
api,
|
||||
@@ -703,3 +677,26 @@ function installService() {
|
||||
gulp.task('install-sqltoolsservice', () => {
|
||||
return installService();
|
||||
});
|
||||
|
||||
function installSsmsMin() {
|
||||
const config = require('../extensions/admin-tool-ext-win/src/config.json');
|
||||
return platformInfo.getCurrent().then(p => {
|
||||
const runtime = p.runtimeId;
|
||||
// fix path since it won't be correct
|
||||
config.installDirectory = path.join(__dirname, '..', 'extensions', 'admin-tool-ext-win', config.installDirectory);
|
||||
var installer = new serviceDownloader(config);
|
||||
const serviceInstallFolder = installer.getInstallDirectory(runtime);
|
||||
const serviceCleanupFolder = path.join(serviceInstallFolder, '..');
|
||||
console.log('Cleaning up the install folder: ' + serviceCleanupFolder);
|
||||
return del(serviceCleanupFolder + '/*').then(() => {
|
||||
console.log('Installing the service. Install folder: ' + serviceInstallFolder);
|
||||
return installer.installService(runtime);
|
||||
}, delError => {
|
||||
console.log('failed to delete the install folder error: ' + delError);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
gulp.task('install-ssmsmin', () => {
|
||||
return installSsmsMin();
|
||||
});
|
||||
|
||||
@@ -40,8 +40,19 @@ function packageBuiltInExtensions() {
|
||||
.filter(({ name }) => excludedExtensions.indexOf(name) === -1)
|
||||
.filter(({ name }) => builtInExtensions.every(b => b.name !== name))
|
||||
.filter(({ name }) => sqlBuiltInExtensions.indexOf(name) >= 0);
|
||||
const visxDirectory = path.join(path.dirname(root), 'vsix');
|
||||
try {
|
||||
if (!fs.existsSync(visxDirectory)) {
|
||||
fs.mkdirSync(visxDirectory);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
// don't fail the build if the output directory already exists
|
||||
console.warn(err);
|
||||
}
|
||||
sqlBuiltInLocalExtensionDescriptions.forEach(element => {
|
||||
const packagePath = path.join(path.dirname(root), element.name + '.vsix');
|
||||
let pkgJson = JSON.parse(fs.readFileSync(path.join(element.path, 'package.json'), { encoding: 'utf8' }));
|
||||
const packagePath = path.join(visxDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`);
|
||||
console.info('Creating vsix for ' + element.path + ' result:' + packagePath);
|
||||
vsce.createVSIX({
|
||||
cwd: element.path,
|
||||
@@ -255,6 +266,7 @@ const sqlBuiltInExtensions = [
|
||||
'big-data-cluster',
|
||||
'dacpac',
|
||||
'schema-compare',
|
||||
'resource-deployment',
|
||||
'cms'
|
||||
];
|
||||
const builtInExtensions = require('../builtInExtensions.json');
|
||||
@@ -302,9 +314,7 @@ function packageExtensionsStream(optsIn) {
|
||||
..._.flatten(extensionsProductionDependencies.map((d) => path.relative(root, d.path)).map((d) => [`${d}/**`, `!${d}/**/{test,tests}/**`])),
|
||||
];
|
||||
const localExtensionDependencies = () => gulp.src(extensionDepsSrc, { base: '.', dot: true })
|
||||
.pipe(filter(['**', '!**/package-lock.json']))
|
||||
.pipe(util2.cleanNodeModule('account-provider-azure', ['node_modules/date-utils/doc/**', 'node_modules/adal_node/node_modules/**'], undefined))
|
||||
.pipe(util2.cleanNodeModule('typescript', ['**/**'], undefined));
|
||||
.pipe(filter(['**', '!**/package-lock.json']));
|
||||
// Original code commented out here
|
||||
// const localExtensionDependencies = () => gulp.src('extensions/node_modules/**', { base: '.' });
|
||||
// const marketplaceExtensions = () => es.merge(
|
||||
|
||||
@@ -43,8 +43,18 @@ export function packageBuiltInExtensions() {
|
||||
.filter(({ name }) => excludedExtensions.indexOf(name) === -1)
|
||||
.filter(({ name }) => builtInExtensions.every(b => b.name !== name))
|
||||
.filter(({ name }) => sqlBuiltInExtensions.indexOf(name) >= 0);
|
||||
const visxDirectory = path.join(path.dirname(root), 'vsix');
|
||||
try {
|
||||
if (!fs.existsSync(visxDirectory)) {
|
||||
fs.mkdirSync(visxDirectory);
|
||||
}
|
||||
} catch (err) {
|
||||
// don't fail the build if the output directory already exists
|
||||
console.warn(err);
|
||||
}
|
||||
sqlBuiltInLocalExtensionDescriptions.forEach(element => {
|
||||
const packagePath = path.join(path.dirname(root), element.name + '.vsix');
|
||||
let pkgJson = JSON.parse(fs.readFileSync(path.join(element.path, 'package.json'), { encoding: 'utf8' }));
|
||||
const packagePath = path.join(visxDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`);
|
||||
console.info('Creating vsix for ' + element.path + ' result:' + packagePath);
|
||||
vsce.createVSIX({
|
||||
cwd: element.path,
|
||||
@@ -302,6 +312,7 @@ const sqlBuiltInExtensions = [
|
||||
'big-data-cluster',
|
||||
'dacpac',
|
||||
'schema-compare',
|
||||
'resource-deployment',
|
||||
'cms'
|
||||
];
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
@@ -365,9 +376,7 @@ export function packageExtensionsStream(optsIn?: IPackageExtensionsOptions): Nod
|
||||
];
|
||||
|
||||
const localExtensionDependencies = () => gulp.src(extensionDepsSrc, { base: '.', dot: true })
|
||||
.pipe(filter(['**', '!**/package-lock.json']))
|
||||
.pipe(util2.cleanNodeModule('account-provider-azure', ['node_modules/date-utils/doc/**', 'node_modules/adal_node/node_modules/**'], undefined))
|
||||
.pipe(util2.cleanNodeModule('typescript', ['**/**'], undefined));
|
||||
.pipe(filter(['**', '!**/package-lock.json']))
|
||||
|
||||
// Original code commented out here
|
||||
// const localExtensionDependencies = () => gulp.src('extensions/node_modules/**', { base: '.' });
|
||||
|
||||
@@ -110,6 +110,10 @@
|
||||
"name": "vs/workbench/contrib/quickopen",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/remote",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/relauncher",
|
||||
"project": "vscode-workbench"
|
||||
|
||||
@@ -40,6 +40,7 @@ function extractEditor(options) {
|
||||
compilerOptions.noUnusedLocals = false;
|
||||
compilerOptions.preserveConstEnums = false;
|
||||
compilerOptions.declaration = false;
|
||||
compilerOptions.noImplicitAny = false;
|
||||
compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic;
|
||||
options.compilerOptions = compilerOptions;
|
||||
let result = tss.shake(options);
|
||||
@@ -90,6 +91,8 @@ function extractEditor(options) {
|
||||
}
|
||||
delete tsConfig.compilerOptions.moduleResolution;
|
||||
writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t'));
|
||||
const tsConfigBase = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.base.json')).toString());
|
||||
writeOutputFile('tsconfig.base.json', JSON.stringify(tsConfigBase, null, '\t'));
|
||||
[
|
||||
'vs/css.build.js',
|
||||
'vs/css.d.ts',
|
||||
|
||||
@@ -44,6 +44,7 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str
|
||||
compilerOptions.noUnusedLocals = false;
|
||||
compilerOptions.preserveConstEnums = false;
|
||||
compilerOptions.declaration = false;
|
||||
compilerOptions.noImplicitAny = false;
|
||||
compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic;
|
||||
|
||||
|
||||
@@ -99,6 +100,8 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str
|
||||
|
||||
delete tsConfig.compilerOptions.moduleResolution;
|
||||
writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t'));
|
||||
const tsConfigBase = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.base.json')).toString());
|
||||
writeOutputFile('tsconfig.base.json', JSON.stringify(tsConfigBase, null, '\t'));
|
||||
|
||||
[
|
||||
'vs/css.build.js',
|
||||
|
||||
@@ -8,7 +8,6 @@ const es = require("event-stream");
|
||||
const debounce = require("debounce");
|
||||
const _filter = require("gulp-filter");
|
||||
const rename = require("gulp-rename");
|
||||
const _ = require("underscore");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const _rimraf = require("rimraf");
|
||||
@@ -100,22 +99,18 @@ function skipDirectories() {
|
||||
});
|
||||
}
|
||||
exports.skipDirectories = skipDirectories;
|
||||
function cleanNodeModule(name, excludes, includes) {
|
||||
const toGlob = (path) => '**/node_modules/' + name + (path ? '/' + path : '');
|
||||
const negate = (str) => '!' + str;
|
||||
const allFilter = _filter(toGlob('**'), { restore: true });
|
||||
const globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob)));
|
||||
function cleanNodeModules(rulePath) {
|
||||
const rules = fs.readFileSync(rulePath, 'utf8')
|
||||
.split(/\r?\n/g)
|
||||
.map(line => line.trim())
|
||||
.filter(line => line && !/^#/.test(line));
|
||||
const excludes = rules.filter(line => !/^!/.test(line)).map(line => `!**/node_modules/${line}`);
|
||||
const includes = rules.filter(line => /^!/.test(line)).map(line => `**/node_modules/${line.substr(1)}`);
|
||||
const input = es.through();
|
||||
const nodeModuleInput = input.pipe(allFilter);
|
||||
let output = nodeModuleInput.pipe(_filter(globs));
|
||||
if (includes) {
|
||||
const includeGlobs = includes.map(toGlob);
|
||||
output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs)));
|
||||
}
|
||||
output = output.pipe(allFilter.restore);
|
||||
const output = es.merge(input.pipe(_filter(['**', ...excludes])), input.pipe(_filter(includes)));
|
||||
return es.duplex(input, output);
|
||||
}
|
||||
exports.cleanNodeModule = cleanNodeModule;
|
||||
exports.cleanNodeModules = cleanNodeModules;
|
||||
function loadSourcemaps() {
|
||||
const input = es.through();
|
||||
const output = input
|
||||
|
||||
@@ -132,23 +132,21 @@ export function skipDirectories(): NodeJS.ReadWriteStream {
|
||||
});
|
||||
}
|
||||
|
||||
export function cleanNodeModule(name: string, excludes: string[], includes?: string[]): NodeJS.ReadWriteStream {
|
||||
const toGlob = (path: string) => '**/node_modules/' + name + (path ? '/' + path : '');
|
||||
const negate = (str: string) => '!' + str;
|
||||
export function cleanNodeModules(rulePath: string): NodeJS.ReadWriteStream {
|
||||
const rules = fs.readFileSync(rulePath, 'utf8')
|
||||
.split(/\r?\n/g)
|
||||
.map(line => line.trim())
|
||||
.filter(line => line && !/^#/.test(line));
|
||||
|
||||
const allFilter = _filter(toGlob('**'), { restore: true });
|
||||
const globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob) as (x: string) => string));
|
||||
const excludes = rules.filter(line => !/^!/.test(line)).map(line => `!**/node_modules/${line}`);
|
||||
const includes = rules.filter(line => /^!/.test(line)).map(line => `**/node_modules/${line.substr(1)}`);
|
||||
|
||||
const input = es.through();
|
||||
const nodeModuleInput = input.pipe(allFilter);
|
||||
let output: NodeJS.ReadWriteStream = nodeModuleInput.pipe(_filter(globs));
|
||||
const output = es.merge(
|
||||
input.pipe(_filter(['**', ...excludes])),
|
||||
input.pipe(_filter(includes))
|
||||
);
|
||||
|
||||
if (includes) {
|
||||
const includeGlobs = includes.map(toGlob);
|
||||
output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs)));
|
||||
}
|
||||
|
||||
output = output.pipe(allFilter.restore);
|
||||
return es.duplex(input, output);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ function yarnInstall(location, opts) {
|
||||
|
||||
yarnInstall('extensions'); // node modules shared by all extensions
|
||||
|
||||
yarnInstall('remote'); // node modules used by vscode server
|
||||
|
||||
const allExtensionFolders = fs.readdirSync('extensions');
|
||||
const extensions = allExtensionFolders.filter(e => {
|
||||
try {
|
||||
|
||||
@@ -133,7 +133,7 @@ begin
|
||||
|
||||
#if "user" == InstallTarget
|
||||
if not WizardSilent() and IsAdminLoggedOn() then begin
|
||||
if MsgBox('This User Installer is not meant to be run as an Administrator. If you would like to install VS Code for all users in this system, download the System Installer instead from https://code.visualstudio.com. Are you sure you want to continue?', mbError, MB_OKCANCEL) = IDCANCEL then begin
|
||||
if MsgBox('This User Installer is not meant to be run as an Administrator. If you would like to install Azure Data Studio for all users in this system, download the System Installer instead from https://docs.microsoft.com/sql/azure-data-studio/download. Are you sure you want to continue?', mbError, MB_OKCANCEL) = IDCANCEL then begin
|
||||
Result := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
out/test/**
|
||||
src/**
|
||||
ssmsmin/**
|
||||
.gitignore
|
||||
tsconfig.json
|
||||
InstallSsmsMin.bat
|
||||
4
extensions/admin-tool-ext-win/InstallSsmsMin.bat
Normal file
4
extensions/admin-tool-ext-win/InstallSsmsMin.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
|
||||
REM Run this command to install SsmsMin for local development testing
|
||||
gulp install-ssmsmin
|
||||
@@ -1,9 +1,26 @@
|
||||
# Database Admin Tool Extensions for Windows
|
||||
# Database Admin Tool Extensions for Windows *(preview)*
|
||||
|
||||
This adds Windows-specific functionality into Azure Data Studio.
|
||||
The Database Admin Tool Extensions for Windows adds Windows-specific functionality into Azure Data Studio. Currently this
|
||||
functionality includes the ability to launch a set of SQL Server Management Studio experiences directly from Azure Data Studio.
|
||||
|
||||
These experiences include:
|
||||
|
||||
* SSMS Property dialogs for select object types, such as Databases, Views, Stored Procedures and more
|
||||
* The [Generate Scripts Wizard](https://docs.microsoft.com/en-us/sql/ssms/scripting/generate-and-publish-scripts-wizard)
|
||||
|
||||
### How do I launch these experiences?
|
||||
|
||||
Both of these are available as menu items on the context menu for nodes in the Object Explorer tree. Right click on a node that supports one of the experiences and select the appropriate item.
|
||||
|
||||
**Properties** for the property dialogs of supported object types
|
||||
|
||||

|
||||
|
||||
**Generate Scripts...** for the Generate Scripts Wizard (only available on Database nodes)
|
||||
|
||||

|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
MICROSOFT SOFTWARE LICENSE TERMS
|
||||
MICROSOFT AZURE DATA STUDIO EXTENSION
|
||||
________________________________________
|
||||
These license terms are an agreement between you and Microsoft Corporation (or one of its affiliates). They apply to the software named above and any Microsoft services or software updates (except to the extent such services or updates are accompanied by new or additional terms, in which case those different terms apply prospectively and do not alter your or Microsoft’s rights relating to pre-updated software or services). IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS.
|
||||
|
||||
1. INSTALLATION AND USE RIGHTS.
|
||||
|
||||
a) General. You may install and use one copy of the software on your device.
|
||||
|
||||
b) Third Party Software. The software may include third party applications that are licensed to you under this agreement or under their own terms. License terms, notices, and acknowledgements, if any, for the third party applications may be accessible online at http://aka.ms/thirdpartynotices or in an accompanying notices file. Even if such applications are governed by other agreements, the disclaimer, limitations on, and exclusions of damages below also apply to the extent allowed by applicable law.
|
||||
|
||||
2. DISTRIBUTABLE CODE. The software may contain code you are permitted to distribute (i.e. make available for third parties) in applications you develop, as described in this Section.
|
||||
|
||||
a) Distribution Rights. The code and files described below are distributable if included with the software.
|
||||
|
||||
i. Sample Code, Templates, and Styles. You may copy, modify, and distribute the source and object code form of code marked as “sample”, “template”, “simple styles”, and “sketch styles”.
|
||||
|
||||
ii. Third Party Distribution. You may permit distributors of your applications to copy and distribute any of this distributable code you elect to distribute with your applications.
|
||||
|
||||
b) Distribution Requirements. For any code you distribute, you must:
|
||||
|
||||
i. add significant primary functionality to it in your applications;
|
||||
|
||||
ii. require distributors and external end users to agree to terms that protect it and Microsoft at least as much as this agreement; and
|
||||
|
||||
iii. indemnify, defend, and hold harmless Microsoft from any claims, including attorneys’ fees, related to the distribution or use of your applications, except to the extent that any claim is based solely on the unmodified distributable code.
|
||||
|
||||
c) Distribution Restrictions. You may not:
|
||||
|
||||
i. use Microsoft’s trademarks or trade dress in your application in any way that suggests your application comes from or is endorsed by Microsoft; or
|
||||
|
||||
ii. modify or distribute the source code of any distributable code so that any part of it becomes subject to any license that requires that the distributable code, any other part of the software, or any of Microsoft’s other intellectual property be disclosed or distributed in source code form, or that others have the right to modify it.
|
||||
|
||||
3. DATA COLLECTION. The software may collect information about you and your use of the software and send that to Microsoft. Microsoft may use this information to provide services and improve Microsoft’s products and services. Your opt-out rights, if any, are described in the product documentation. Some features in the software may enable collection of data from users of your applications that access or use the software. If you use these features to enable data collection in your applications, you must comply with applicable law, including getting any required user consent, and maintain a prominent privacy policy that accurately informs users about how you use, collect, and share their data. You can learn more about Microsoft’s data collection and use in the product documentation and the Microsoft Privacy Statement at https://go.microsoft.com/fwlink/?LinkId=512132. You agree to comply with all applicable provisions of the Microsoft Privacy Statement.
|
||||
|
||||
4. SCOPE OF LICENSE. The software is licensed, not sold. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you will not (and have no right to):
|
||||
|
||||
a) work around any technical limitations in the software that only allow you to use it in certain ways;
|
||||
|
||||
b) reverse engineer, decompile or disassemble the software;
|
||||
|
||||
c) remove, minimize, block, or modify any notices of Microsoft or its suppliers in the software;
|
||||
|
||||
d) use the software in any way that is against the law or to create or propagate malware; or
|
||||
|
||||
e) share, publish, distribute, or lend the software (except for any distributable code, subject to the terms above), provide the software as a stand-alone hosted solution for others to use, or transfer the software or this agreement to any third party.
|
||||
|
||||
5. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit http://aka.ms/exporting.
|
||||
|
||||
6. SUPPORT SERVICES. Microsoft is not obligated under this agreement to provide any support services for the software. Any support provided is “as is”, “with all faults”, and without warranty of any kind.
|
||||
|
||||
7. ENTIRE AGREEMENT. This agreement, and any other terms Microsoft may provide for supplements, updates, or third-party applications, is the entire agreement for the software.
|
||||
|
||||
8. APPLICABLE LAW AND PLACE TO RESOLVE DISPUTES. If you acquired the software in the United States or Canada, the laws of the state or province where you live (or, if a business, where your principal place of business is located) govern the interpretation of this agreement, claims for its breach, and all other claims (including consumer protection, unfair competition, and tort claims), regardless of conflict of laws principles. If you acquired the software in any other country, its laws apply. If U.S. federal jurisdiction exists, you and Microsoft consent to exclusive jurisdiction and venue in the federal court in King County, Washington for all disputes heard in court. If not, you and Microsoft consent to exclusive jurisdiction and venue in the Superior Court of King County, Washington for all disputes heard in court.
|
||||
|
||||
9. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you:
|
||||
|
||||
a) Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights.
|
||||
|
||||
b) Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software.
|
||||
|
||||
c) Germany and Austria.
|
||||
|
||||
i. Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software.
|
||||
|
||||
ii. Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in case of death or personal or physical injury, Microsoft is liable according to the statutory law.
|
||||
|
||||
Subject to the foregoing clause ii., Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence.
|
||||
|
||||
10. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS IS.” YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS. TO THE EXTENT PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
|
||||
|
||||
11. LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY BASIS FOR RECOVERING DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT, OR INCIDENTAL DAMAGES.
|
||||
|
||||
This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, warranty, guarantee, or condition; strict liability, negligence, or other tort; or any other claim; in each case to the extent permitted by applicable law.
|
||||
|
||||
It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your state, province, or country may not allow the exclusion or limitation of incidental, consequential, or other damages.
|
||||
|
||||
Please note: As this software is distributed in Canada, some of the clauses in this agreement are provided below in French.
|
||||
|
||||
Remarque: Ce logiciel étant distribué au Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français.
|
||||
|
||||
EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d’adéquation à un usage particulier et d’absence de contrefaçon sont exclues.
|
||||
|
||||
LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.
|
||||
|
||||
Cette limitation concerne:
|
||||
|
||||
• tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers; et
|
||||
|
||||
• les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur.
|
||||
|
||||
Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion ci-dessus ne s’appliquera pas à votre égard.
|
||||
|
||||
EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
"version": "0.0.1",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/extensions/admin-tool-ext-win/license/Azure%20Data%20Studio%20Extension%20-%20Standalone%20(free)%20Use%20Terms.txt",
|
||||
"icon": "images/sqlserver.png",
|
||||
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
|
||||
"engines": {
|
||||
"vscode": "^1.30.1",
|
||||
"sqlops": "*"
|
||||
"azdata": ">=1.8.0"
|
||||
},
|
||||
"activationEvents": [
|
||||
"*"
|
||||
@@ -55,7 +55,12 @@
|
||||
},
|
||||
{
|
||||
"command": "adminToolExtWin.launchSsmsMinPropertiesDialog",
|
||||
"when": "isWindows && connectionProvider == MSSQL && nodeType && nodeType =~ /^(Server|Database|Table|Column|Index|Statistic|View|ServerLevelLogin|ServerLevelServerRole|ServerLevelCredential|ServerLevelServerAudit|ServerLevelServerAuditSpecification|StoredProcedure|ScalarValuedFunction|TableValuedFunction|AggregateFunction|Synonym|Assembly|UserDefinedDataType|UserDefinedType|UserDefinedTableType|Sequence|User|DatabaseRole|ApplicationRole|Schema|SecurityPolicy|ServerLevelLinkedServer)$/",
|
||||
"when": "isWindows && connectionProvider == MSSQL && serverInfo && !isCloud && nodeType && nodeType == Server",
|
||||
"group": "z-AdminToolExt@2"
|
||||
},
|
||||
{
|
||||
"command": "adminToolExtWin.launchSsmsMinPropertiesDialog",
|
||||
"when": "isWindows && connectionProvider == MSSQL && serverInfo && nodeType && nodeType =~ /^(Database|Table|Column|Index|Statistic|View|ServerLevelLogin|ServerLevelServerRole|ServerLevelCredential|ServerLevelServerAudit|ServerLevelServerAuditSpecification|StoredProcedure|ScalarValuedFunction|TableValuedFunction|AggregateFunction|Synonym|Assembly|UserDefinedDataType|UserDefinedType|UserDefinedTableType|Sequence|User|DatabaseRole|ApplicationRole|Schema|SecurityPolicy|ServerLevelLinkedServer)$/",
|
||||
"group": "z-AdminToolExt@2"
|
||||
}
|
||||
]
|
||||
@@ -65,7 +70,6 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
|
||||
"vscode-extension-telemetry": "^0.0.15",
|
||||
"vscode-nls": "^3.2.1"
|
||||
},
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"downloadUrl": "https://sqlopsextensions.blob.core.windows.net/tools/ssmsmin/{#version#}/{#fileName#}",
|
||||
"version": "15.0.18120.0",
|
||||
"version": "15.0.18124.0",
|
||||
"downloadFileNames": {
|
||||
"Windows_64": "SsmsMin-15.0.18120.0-win-x64.zip",
|
||||
"Windows_86": "SsmsMin-15.0.18120.0-win-x86.zip"
|
||||
"Windows_64": "SsmsMin-15.0.18124.0-win-x64.zip",
|
||||
"Windows_86": "SsmsMin-15.0.18124.0-win-x86.zip"
|
||||
},
|
||||
"installDirectory": "ssmsmin/{#platform#}/{#version#}",
|
||||
"executableFiles": [
|
||||
"SsmsMin.exe"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -7,16 +7,14 @@ import * as nls from 'vscode-nls';
|
||||
import * as path from 'path';
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { IConfig, ServerProvider } from 'service-downloader';
|
||||
import { Telemetry } from './telemetry';
|
||||
import { doubleEscapeSingleQuotes, backEscapeDoubleQuotes, getConfiguration } from './utils';
|
||||
import { doubleEscapeSingleQuotes, backEscapeDoubleQuotes, getTelemetryErrorType } from './utils';
|
||||
import { ChildProcess, exec } from 'child_process';
|
||||
|
||||
const baseConfig = require('./config.json');
|
||||
const localize = nls.loadMessageBundle();
|
||||
let exePath: string;
|
||||
let runningProcesses: Map<number, ChildProcess> = new Map<number, ChildProcess>();
|
||||
const ssmsMinVer = JSON.parse(JSON.stringify(require('./config.json'))).version;
|
||||
|
||||
let exePath: string;
|
||||
const runningProcesses: Map<number, ChildProcess> = new Map<number, ChildProcess>();
|
||||
|
||||
interface SmoMapping {
|
||||
action: string;
|
||||
@@ -68,39 +66,8 @@ export interface LaunchSsmsDialogParams {
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||
// This is for Windows-specific support so do nothing on other platforms
|
||||
if (process.platform === 'win32') {
|
||||
Telemetry.sendTelemetryEvent('startup/ExtensionActivated');
|
||||
|
||||
let config: IConfig = JSON.parse(JSON.stringify(baseConfig));
|
||||
config.installDirectory = path.join(context.extensionPath, config.installDirectory);
|
||||
config.proxy = getConfiguration('http').get('proxy');
|
||||
config.strictSSL = getConfiguration('http').get('proxyStrictSSL') || true;
|
||||
|
||||
const serverdownloader = new ServerProvider(config);
|
||||
const installationStart = Date.now();
|
||||
|
||||
try {
|
||||
let downloadedExePath = await serverdownloader.getOrDownloadServer();
|
||||
const installationComplete = Date.now();
|
||||
|
||||
// Don't register the command if we couldn't find the EXE since it won't be able to do anything
|
||||
if (downloadedExePath) {
|
||||
exePath = downloadedExePath;
|
||||
} else {
|
||||
throw new Error('Could not find SsmsMin.exe after downloading');
|
||||
}
|
||||
|
||||
// Register the commands now that we have the exePath to run the tool with
|
||||
registerCommands(context);
|
||||
|
||||
Telemetry.sendTelemetryEvent('startup/ExtensionStarted', {
|
||||
installationTime: String(installationComplete - installationStart),
|
||||
beginningTimestamp: String(installationStart)
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
Telemetry.sendTelemetryEvent('startup/ExtensionInitializationFailed');
|
||||
console.error(`Error Initializing Admin Tool Extension for Windows - ${err}`);
|
||||
}
|
||||
exePath = path.join(context.extensionPath, 'ssmsmin', 'Windows', ssmsMinVer, 'ssmsmin.exe');
|
||||
registerCommands(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +75,7 @@ export async function deactivate(): Promise<void> {
|
||||
// If the extension is being deactivated we want to kill all processes that are still currently
|
||||
// running otherwise they will continue to run as orphan processes. We use taskkill here in case
|
||||
// they started off child processes of their own
|
||||
runningProcesses.forEach(p => exec('taskkill /pid ' + p.pid + ' /T /F'));
|
||||
runningProcesses.forEach(p => exec(`taskkill /pid ${p.pid} /T /F`));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,6 +95,7 @@ function registerCommands(context: vscode.ExtensionContext): void {
|
||||
*/
|
||||
async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||
if (!connectionContext) {
|
||||
Telemetry.sendTelemetryEventForError('NoConnectionContext', { action: 'Properties' });
|
||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForProp', 'No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand'));
|
||||
return;
|
||||
}
|
||||
@@ -138,9 +106,9 @@ async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: az
|
||||
}
|
||||
else if (connectionContext.nodeInfo) {
|
||||
nodeType = connectionContext.nodeInfo.nodeType;
|
||||
}
|
||||
else {
|
||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noOeNode', 'Could not determine NodeType for handleLaunchSsmsMinPropertiesDialogCommand with context {0}', JSON.stringify(connectionContext)));
|
||||
} else {
|
||||
Telemetry.sendTelemetryEventForError('NoOENode', { action: 'Properties' });
|
||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', 'Could not determine Object Explorer node from connectionContext : {0}', JSON.stringify(connectionContext)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,12 +122,14 @@ async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: az
|
||||
* @param connectionId The connection context from the command
|
||||
*/
|
||||
async function handleLaunchSsmsMinGswDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||
const action = 'GenerateScripts';
|
||||
if (!connectionContext) {
|
||||
Telemetry.sendTelemetryEventForError('NoConnectionContext', { action: action });
|
||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForGsw', 'No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand'));
|
||||
}
|
||||
|
||||
launchSsmsDialog(
|
||||
'GenerateScripts',
|
||||
action,
|
||||
connectionContext);
|
||||
}
|
||||
|
||||
@@ -170,33 +140,12 @@ async function handleLaunchSsmsMinGswDialogCommand(connectionContext?: azdata.Ob
|
||||
* @param urn The URN to pass to SsmsMin
|
||||
*/
|
||||
async function launchSsmsDialog(action: string, connectionContext: azdata.ObjectExplorerContext): Promise<void> {
|
||||
if (!exePath) {
|
||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noExeError', 'Unable to find SsmsMin.exe.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!connectionContext.connectionProfile) {
|
||||
Telemetry.sendTelemetryEventForError('NoConnectionProfile', { action: action });
|
||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionProfile', 'No connectionProfile provided from connectionContext : {0}', JSON.stringify(connectionContext)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Currently Azure isn't supported by the SSMS server properties dialog
|
||||
const serverInfo = await azdata.connection.getServerInfo(connectionContext.connectionProfile.id);
|
||||
if (serverInfo && serverInfo.isCloud) {
|
||||
vscode.window.showErrorMessage(localize('adminToolExtWin.invalidEngineType', 'This option is not currently available for this engine type.'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Note - this is a temporary fix for the issue that currently the connection API doesn't allow retrieving credentials for a disconnected
|
||||
// node. So until that's fixed we'll prevent users from attempting to launch SsmsMin on a disconnected node.
|
||||
// We also aren't able to hide the menu item on disconnected nodes because we currently don't have a contextKey for the connected status
|
||||
// of a node.
|
||||
const activeConnections = await azdata.connection.getActiveConnections();
|
||||
if (!activeConnections.some(conn => conn.connectionId === connectionContext.connectionProfile.id)) {
|
||||
vscode.window.showErrorMessage(localize('adminToolExtWin.notConnected', 'This option requires a connected node - please connect and try again.'));
|
||||
return;
|
||||
}
|
||||
|
||||
let oeNode: azdata.objectexplorer.ObjectExplorerNode;
|
||||
// Server node is a Connection node and so doesn't have the NodeInfo
|
||||
if (connectionContext.isConnectionNode) {
|
||||
@@ -206,19 +155,20 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object
|
||||
oeNode = await azdata.objectexplorer.getNode(connectionContext.connectionProfile.id, connectionContext.nodeInfo.nodePath);
|
||||
}
|
||||
else {
|
||||
Telemetry.sendTelemetryEventForError('NoOENode', { action: action });
|
||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', 'Could not determine Object Explorer node from connectionContext : {0}', JSON.stringify(connectionContext)));
|
||||
return;
|
||||
}
|
||||
|
||||
let urn: string = await buildUrn(connectionContext.connectionProfile.serverName, oeNode);
|
||||
const urn: string = await buildUrn(oeNode);
|
||||
let password: string = connectionContext.connectionProfile.password;
|
||||
|
||||
if (!password || password === '') {
|
||||
let creds = await azdata.connection.getCredentials(connectionContext.connectionProfile.id);
|
||||
const creds = await azdata.connection.getCredentials(connectionContext.connectionProfile.id);
|
||||
password = creds[azdata.ConnectionOptionSpecialType.password];
|
||||
}
|
||||
|
||||
let params: LaunchSsmsDialogParams = {
|
||||
const params: LaunchSsmsDialogParams = {
|
||||
action: action,
|
||||
server: connectionContext.connectionProfile.serverName,
|
||||
database: connectionContext.connectionProfile.databaseName,
|
||||
@@ -227,22 +177,30 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object
|
||||
urn: urn
|
||||
};
|
||||
|
||||
let args = buildSsmsMinCommandArgs(params);
|
||||
const args = buildSsmsMinCommandArgs(params);
|
||||
|
||||
Telemetry.sendTelemetryEvent('LaunchSsmsDialog', { 'action': action });
|
||||
Telemetry.sendTelemetryEvent('LaunchSsmsDialog',
|
||||
{
|
||||
action: action,
|
||||
nodeType: oeNode ? oeNode.nodeType : 'Server',
|
||||
authType: connectionContext.connectionProfile.authenticationType
|
||||
});
|
||||
|
||||
vscode.window.setStatusBarMessage(localize('adminToolExtWin.launchingDialogStatus', 'Launching dialog...'), 3000);
|
||||
|
||||
// This will be an async call since we pass in the callback
|
||||
let proc: ChildProcess = exec(
|
||||
const proc: ChildProcess = exec(
|
||||
/*command*/ `"${exePath}" ${args}`,
|
||||
/*options*/ undefined,
|
||||
(execException, stdout, stderr) => {
|
||||
// Process has exited so remove from map of running processes
|
||||
runningProcesses.delete(proc.pid);
|
||||
const err = stderr.toString();
|
||||
Telemetry.sendTelemetryEvent('LaunchSsmsDialogResult', {
|
||||
'action': params.action,
|
||||
'returnCode': execException && execException.code ? execException.code.toString() : '0'
|
||||
action: params.action,
|
||||
returnCode: execException && execException.code ? execException.code.toString() : '0',
|
||||
errorType: getTelemetryErrorType(err)
|
||||
});
|
||||
let err = stderr.toString();
|
||||
if (err !== '') {
|
||||
vscode.window.showErrorMessage(localize(
|
||||
'adminToolExtWin.ssmsMinError',
|
||||
@@ -269,17 +227,16 @@ export function buildSsmsMinCommandArgs(params: LaunchSsmsDialogParams): string
|
||||
return `${params.action ? '-a "' + backEscapeDoubleQuotes(params.action) + '"' : ''}\
|
||||
${params.server ? ' -S "' + backEscapeDoubleQuotes(params.server) + '"' : ''}\
|
||||
${params.database ? ' -D "' + backEscapeDoubleQuotes(params.database) + '"' : ''}\
|
||||
${params.useAad !== true && params.user ? ' -U "' + backEscapeDoubleQuotes(params.user) + '"' : ''}\
|
||||
${params.user ? ' -U "' + backEscapeDoubleQuotes(params.user) + '"' : ''}\
|
||||
${params.useAad === true ? ' -G' : ''}\
|
||||
${params.urn ? ' -u "' + backEscapeDoubleQuotes(params.urn) + '"' : ''}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the URN string for a given ObjectExplorerNode in the form understood by SsmsMin
|
||||
* @param serverName The name of the Server to use for the Server segment
|
||||
* @param node The node to get the URN of
|
||||
*/
|
||||
export async function buildUrn(serverName: string, node: azdata.objectexplorer.ObjectExplorerNode): Promise<string> {
|
||||
export async function buildUrn(node: azdata.objectexplorer.ObjectExplorerNode): Promise<string> {
|
||||
let urnNodes: string[] = [];
|
||||
while (node) {
|
||||
// Server is special since it's a connection node - always add it as the root
|
||||
@@ -288,12 +245,13 @@ export async function buildUrn(serverName: string, node: azdata.objectexplorer.O
|
||||
}
|
||||
else if (node.metadata && node.nodeType !== 'Folder') {
|
||||
// SFC URN expects Name and Schema to be separate properties
|
||||
let urnSegment = node.metadata.schema && node.metadata.schema !== '' ?
|
||||
const urnSegment = node.metadata.schema && node.metadata.schema !== '' ?
|
||||
`${nodeTypeToUrnNameMapping[node.nodeType].urnName}[@Name='${doubleEscapeSingleQuotes(node.metadata.name)}' and @Schema='${doubleEscapeSingleQuotes(node.metadata.schema)}']` :
|
||||
`${nodeTypeToUrnNameMapping[node.nodeType].urnName}[@Name='${doubleEscapeSingleQuotes(node.metadata.name)}']`;
|
||||
urnNodes = [urnSegment].concat(urnNodes);
|
||||
}
|
||||
node = await node.getParent();
|
||||
}
|
||||
return [`Server[@Name='${doubleEscapeSingleQuotes(serverName)}']`].concat(urnNodes).join('/');
|
||||
|
||||
return ['Server'].concat(urnNodes).join('/');
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
'use strict';
|
||||
import * as vscode from 'vscode';
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
import { PlatformInformation } from 'service-downloader/out/platform';
|
||||
|
||||
import * as Utils from './utils';
|
||||
|
||||
@@ -37,23 +36,8 @@ export function filterErrorPath(line: string): string {
|
||||
|
||||
export class Telemetry {
|
||||
private static reporter: TelemetryReporter;
|
||||
private static userId: string;
|
||||
private static platformInformation: PlatformInformation;
|
||||
private static disabled: boolean;
|
||||
|
||||
public static getPlatformInformation(): Promise<PlatformInformation> {
|
||||
if (this.platformInformation) {
|
||||
return Promise.resolve(this.platformInformation);
|
||||
} else {
|
||||
return new Promise<PlatformInformation>(resolve => {
|
||||
PlatformInformation.getCurrent().then(info => {
|
||||
this.platformInformation = info;
|
||||
resolve(this.platformInformation);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable telemetry reporting
|
||||
*/
|
||||
@@ -78,27 +62,11 @@ export class Telemetry {
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a telemetry event for an exception
|
||||
* Send a telemetry event for a general error
|
||||
* @param err The error to log
|
||||
*/
|
||||
public static sendTelemetryEventForException(
|
||||
err: any, methodName: string, extensionConfigName: string): void {
|
||||
try {
|
||||
let stackArray: string[];
|
||||
let firstLine: string = '';
|
||||
if (err !== undefined && err.stack !== undefined) {
|
||||
stackArray = err.stack.split('\n');
|
||||
if (stackArray !== undefined && stackArray.length >= 2) {
|
||||
firstLine = stackArray[1]; // The first line is the error message and we don't want to send that telemetry event
|
||||
firstLine = filterErrorPath(firstLine);
|
||||
}
|
||||
}
|
||||
|
||||
// Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII
|
||||
this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine });
|
||||
} catch (telemetryErr) {
|
||||
// If sending telemetry event fails ignore it so it won't break the extension
|
||||
console.error('Failed to send telemetry event. error: ' + telemetryErr, extensionConfigName);
|
||||
}
|
||||
public static sendTelemetryEventForError(err: string, properties?: ITelemetryEventProperties): void {
|
||||
this.sendTelemetryEvent('Error', { error: err, ...properties });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,13 +90,12 @@ export class Telemetry {
|
||||
properties = {};
|
||||
}
|
||||
|
||||
// Augment the properties structure with additional common properties before sending
|
||||
Promise.all([this.getPlatformInformation()]).then(() => {
|
||||
properties['distribution'] = (this.platformInformation && this.platformInformation.distribution) ?
|
||||
`${this.platformInformation.distribution.name}, ${this.platformInformation.distribution.version}` : '';
|
||||
|
||||
try {
|
||||
this.reporter.sendTelemetryEvent(eventName, properties, measures);
|
||||
});
|
||||
} catch (telemetryErr) {
|
||||
// If sending telemetry event fails ignore it so it won't break the extension
|
||||
console.error('Failed to send telemetry event. error: ' + telemetryErr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ describe('buildSsmsMinCommandArgs Method Tests', () => {
|
||||
urn: 'Server\\Database\\Table'
|
||||
};
|
||||
const args = buildSsmsMinCommandArgs(params);
|
||||
// User is omitted since UseAAD is true
|
||||
should(args).equal('-a "myAction" -S "myServer" -D "myDatabase" -G -u "Server\\Database\\Table"');
|
||||
|
||||
should(args).equal('-a "myAction" -S "myServer" -D "myDatabase" -U "user" -G -u "Server\\Database\\Table"');
|
||||
});
|
||||
|
||||
it('Should be built correctly and names escaped correctly', function (): void {
|
||||
@@ -50,8 +50,8 @@ describe('buildSsmsMinCommandArgs Method Tests', () => {
|
||||
urn: 'Server\\Database[\'myDatabase\'\'"/\\[]tricky\']\\Table["myTable\'""/\\[]tricky"]'
|
||||
};
|
||||
const args = buildSsmsMinCommandArgs(params);
|
||||
// User is omitted since UseAAD is true
|
||||
should(args).equal('-a "myAction\'\\"/\\[]tricky" -S "myServer\'\\"/\\[]tricky" -D "myDatabase\'\\"/\\[]tricky" -G -u "Server\\Database[\'myDatabase\'\'\\"/\\[]tricky\']\\Table[\\"myTable\'\\"\\"/\\[]tricky\\"]"');
|
||||
|
||||
should(args).equal('-a "myAction\'\\"/\\[]tricky" -S "myServer\'\\"/\\[]tricky" -D "myDatabase\'\\"/\\[]tricky" -U "user\'\\"/\\[]tricky" -G -u "Server\\Database[\'myDatabase\'\'\\"/\\[]tricky\']\\Table[\\"myTable\'\\"\\"/\\[]tricky\\"]"');
|
||||
});
|
||||
|
||||
it('Should be built correctly with only action and server', function (): void {
|
||||
@@ -65,8 +65,6 @@ describe('buildSsmsMinCommandArgs Method Tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
const serverName = 'My\'Server';
|
||||
const escapedServerName = doubleEscapeSingleQuotes(serverName);
|
||||
const dbName = 'My\'Db';
|
||||
const escapedDbName = doubleEscapeSingleQuotes(dbName);
|
||||
const dbSchema = 'db\'sch';
|
||||
@@ -78,21 +76,21 @@ const escapedTableSchema = doubleEscapeSingleQuotes(tableSchema);
|
||||
|
||||
describe('buildUrn Method Tests', () => {
|
||||
it('Urn should be correct with just server', async function (): Promise<void> {
|
||||
should(await buildUrn(serverName, undefined)).equal(`Server[@Name=\'${escapedServerName}\']`);
|
||||
should(await buildUrn(undefined)).equal('Server');
|
||||
});
|
||||
|
||||
it('Urn should be correct with Server and only Databases folder', async function (): Promise<void> {
|
||||
const leafNode: ExtHostObjectExplorerNodeStub =
|
||||
new ExtHostObjectExplorerNodeStub('Databases', undefined, 'Folder', undefined);
|
||||
should(await buildUrn(serverName, leafNode)).equal(`Server[@Name='${escapedServerName}']`);
|
||||
should(await buildUrn(leafNode)).equal('Server');
|
||||
});
|
||||
|
||||
it('Urn should be correct with Server and Database node', async function (): Promise<void> {
|
||||
const leafNode: ExtHostObjectExplorerNodeStub =
|
||||
new ExtHostObjectExplorerNodeStub('Databases', undefined, 'Folder', undefined)
|
||||
.createChild(dbName, dbSchema, 'Database');
|
||||
should(await buildUrn(serverName, leafNode)).equal(
|
||||
`Server[@Name='${escapedServerName}']/Database[@Name='${escapedDbName}' and @Schema='${escapedDbSchema}']`);
|
||||
should(await buildUrn(leafNode)).equal(
|
||||
`Server/Database[@Name='${escapedDbName}' and @Schema='${escapedDbSchema}']`);
|
||||
});
|
||||
|
||||
it('Urn should be correct with Multiple levels of Nodes', async function (): Promise<void> {
|
||||
@@ -101,8 +99,8 @@ describe('buildUrn Method Tests', () => {
|
||||
.createChild(dbName, dbSchema, 'Database')
|
||||
.createChild('Tables', undefined, 'Folder')
|
||||
.createChild(tableName, tableSchema, 'Table');
|
||||
should(await buildUrn(serverName, rootNode)).equal(
|
||||
`Server[@Name='${escapedServerName}']/Database[@Name='${escapedDbName}' and @Schema='${escapedDbSchema}']/Table[@Name='${escapedTableName}' and @Schema='${escapedTableSchema}']`);
|
||||
should(await buildUrn(rootNode)).equal(
|
||||
`Server/Database[@Name='${escapedDbName}' and @Schema='${escapedDbSchema}']/Table[@Name='${escapedTableName}' and @Schema='${escapedTableSchema}']`);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export interface IPackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
@@ -22,25 +20,6 @@ export function getPackageInfo(packageJson: any): IPackageInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration for a extensionName
|
||||
* @param extensionName The string name of the extension to get the configuration for
|
||||
* @param resource The optional URI, as a URI object or a string, to use to get resource-scoped configurations
|
||||
*/
|
||||
export function getConfiguration(extensionName?: string, resource?: vscode.Uri | string): vscode.WorkspaceConfiguration {
|
||||
if (typeof resource === 'string') {
|
||||
try {
|
||||
resource = this.parseUri(resource);
|
||||
} catch (e) {
|
||||
resource = undefined;
|
||||
}
|
||||
} else if (!resource) {
|
||||
// Fix to avoid adding lots of errors to debug console. Expects a valid resource or null, not undefined
|
||||
resource = null;
|
||||
}
|
||||
return vscode.workspace.getConfiguration(extensionName, resource as vscode.Uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes all single-quotes (') by prefixing them with another single quote ('')
|
||||
* ' => ''
|
||||
@@ -57,4 +36,26 @@ export function doubleEscapeSingleQuotes(value: string): string {
|
||||
*/
|
||||
export function backEscapeDoubleQuotes(value: string): string {
|
||||
return value.replace(/"/g, '\\"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an error message into a friendly short name for the type of error.
|
||||
* @param msg The error message to map
|
||||
*/
|
||||
export function getTelemetryErrorType(msg: string): string {
|
||||
if (msg.indexOf('is not recognized as an internal or external command') !== -1) {
|
||||
return 'ExeNotFound';
|
||||
}
|
||||
else if (msg.indexOf('Unknown Action') !== -1) {
|
||||
return 'UnknownAction';
|
||||
}
|
||||
else if (msg.indexOf('No Action Provided') !== -1) {
|
||||
return 'NoActionProvided';
|
||||
}
|
||||
else if (msg.indexOf('Run exception') !== -1) {
|
||||
return 'RunException';
|
||||
}
|
||||
else {
|
||||
return 'Other';
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,6 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
agent-base@4, agent-base@^4.1.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
||||
integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
ajv@^6.5.5:
|
||||
version "6.9.2"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.9.2.tgz#4927adb83e7f48e5a32b45729744c71ec39c9c7b"
|
||||
@@ -141,11 +134,6 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
base64-js@^1.0.2:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
|
||||
integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==
|
||||
|
||||
bcrypt-pbkdf@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
|
||||
@@ -158,14 +146,6 @@ beeper@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809"
|
||||
integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=
|
||||
|
||||
bl@^1.0.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
|
||||
integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==
|
||||
dependencies:
|
||||
readable-stream "^2.3.5"
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
block-stream@*:
|
||||
version "0.0.9"
|
||||
resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
|
||||
@@ -190,37 +170,11 @@ braces@^1.8.2:
|
||||
preserve "^0.2.0"
|
||||
repeat-element "^1.1.2"
|
||||
|
||||
buffer-alloc-unsafe@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
|
||||
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
|
||||
|
||||
buffer-alloc@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
|
||||
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
|
||||
dependencies:
|
||||
buffer-alloc-unsafe "^1.1.0"
|
||||
buffer-fill "^1.0.0"
|
||||
|
||||
buffer-crc32@~0.2.3:
|
||||
version "0.2.13"
|
||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||
|
||||
buffer-fill@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
|
||||
integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
|
||||
|
||||
buffer@^5.2.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6"
|
||||
integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==
|
||||
dependencies:
|
||||
base64-js "^1.0.2"
|
||||
ieee754 "^1.1.4"
|
||||
|
||||
camelcase-keys@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
|
||||
@@ -317,13 +271,6 @@ commander@2.3.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873"
|
||||
integrity sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=
|
||||
|
||||
commander@~2.8.1:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
|
||||
integrity sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=
|
||||
dependencies:
|
||||
graceful-readlink ">= 1.0.0"
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
@@ -375,78 +322,11 @@ debug@2.2.0:
|
||||
dependencies:
|
||||
ms "0.7.1"
|
||||
|
||||
debug@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^3.1.0:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
decamelize@^1.1.2:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||
|
||||
decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
|
||||
integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==
|
||||
dependencies:
|
||||
file-type "^5.2.0"
|
||||
is-stream "^1.1.0"
|
||||
tar-stream "^1.5.2"
|
||||
|
||||
decompress-tarbz2@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
|
||||
integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
|
||||
dependencies:
|
||||
decompress-tar "^4.1.0"
|
||||
file-type "^6.1.0"
|
||||
is-stream "^1.1.0"
|
||||
seek-bzip "^1.0.5"
|
||||
unbzip2-stream "^1.0.9"
|
||||
|
||||
decompress-targz@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
|
||||
integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
|
||||
dependencies:
|
||||
decompress-tar "^4.1.1"
|
||||
file-type "^5.2.0"
|
||||
is-stream "^1.1.0"
|
||||
|
||||
decompress-unzip@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
|
||||
integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k=
|
||||
dependencies:
|
||||
file-type "^3.8.0"
|
||||
get-stream "^2.2.0"
|
||||
pify "^2.3.0"
|
||||
yauzl "^2.4.2"
|
||||
|
||||
decompress@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d"
|
||||
integrity sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=
|
||||
dependencies:
|
||||
decompress-tar "^4.0.0"
|
||||
decompress-tarbz2 "^4.0.0"
|
||||
decompress-targz "^4.0.0"
|
||||
decompress-unzip "^4.0.1"
|
||||
graceful-fs "^4.1.10"
|
||||
make-dir "^1.0.0"
|
||||
pify "^2.3.0"
|
||||
strip-dirs "^2.0.0"
|
||||
|
||||
deep-assign@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/deep-assign/-/deep-assign-1.0.0.tgz#b092743be8427dc621ea0067cdec7e70dd19f37b"
|
||||
@@ -520,18 +400,6 @@ error-ex@^1.2.0:
|
||||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
es6-promise@^4.0.3:
|
||||
version "4.2.6"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f"
|
||||
integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==
|
||||
|
||||
es6-promisify@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
|
||||
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
|
||||
dependencies:
|
||||
es6-promise "^4.0.3"
|
||||
|
||||
escape-string-regexp@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1"
|
||||
@@ -581,11 +449,6 @@ event-stream@~3.1.5:
|
||||
stream-combiner "~0.0.4"
|
||||
through "~2.3.1"
|
||||
|
||||
eventemitter2@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452"
|
||||
integrity sha1-YZegldX7a1folC9v1+qtY6CclFI=
|
||||
|
||||
expand-brackets@^0.1.4:
|
||||
version "0.1.5"
|
||||
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
|
||||
@@ -656,21 +519,6 @@ fd-slicer@~1.1.0:
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
file-type@^3.8.0:
|
||||
version "3.9.0"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
|
||||
integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek=
|
||||
|
||||
file-type@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
|
||||
integrity sha1-LdvqfHP/42No365J3DOMBYwritY=
|
||||
|
||||
file-type@^6.1.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
|
||||
integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==
|
||||
|
||||
filename-regex@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
|
||||
@@ -731,11 +579,6 @@ from@^0.1.7, from@~0:
|
||||
resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
|
||||
integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=
|
||||
|
||||
fs-constants@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
@@ -761,14 +604,6 @@ get-stdin@^4.0.1:
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
|
||||
integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=
|
||||
|
||||
get-stream@^2.2.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
|
||||
integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=
|
||||
dependencies:
|
||||
object-assign "^4.0.1"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
getpass@^0.1.1:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
|
||||
@@ -851,7 +686,7 @@ glogg@^1.0.0:
|
||||
dependencies:
|
||||
sparkles "^1.0.0"
|
||||
|
||||
graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.2:
|
||||
graceful-fs@^4.0.0, graceful-fs@^4.1.2:
|
||||
version "4.1.15"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||
@@ -863,11 +698,6 @@ graceful-fs@~3.0.2:
|
||||
dependencies:
|
||||
natives "^1.1.0"
|
||||
|
||||
"graceful-readlink@>= 1.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
|
||||
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
|
||||
|
||||
growl@1.9.2:
|
||||
version "1.9.2"
|
||||
resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
|
||||
@@ -1046,14 +876,6 @@ hosted-git-info@^2.1.4:
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
|
||||
integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==
|
||||
|
||||
http-proxy-agent@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
|
||||
integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
|
||||
dependencies:
|
||||
agent-base "4"
|
||||
debug "3.1.0"
|
||||
|
||||
http-signature@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
||||
@@ -1063,19 +885,6 @@ http-signature@~1.2.0:
|
||||
jsprim "^1.2.2"
|
||||
sshpk "^1.7.0"
|
||||
|
||||
https-proxy-agent@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
|
||||
integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==
|
||||
dependencies:
|
||||
agent-base "^4.1.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
ieee754@^1.1.4:
|
||||
version "1.1.12"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"
|
||||
integrity sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==
|
||||
|
||||
indent-string@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
|
||||
@@ -1154,11 +963,6 @@ is-glob@^3.1.0:
|
||||
dependencies:
|
||||
is-extglob "^2.1.0"
|
||||
|
||||
is-natural-number@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
|
||||
integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=
|
||||
|
||||
is-number@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
|
||||
@@ -1531,13 +1335,6 @@ lru-cache@2:
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
|
||||
integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=
|
||||
|
||||
make-dir@^1.0.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
|
||||
integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
|
||||
dependencies:
|
||||
pify "^3.0.0"
|
||||
|
||||
map-obj@^1.0.0, map-obj@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
|
||||
@@ -1675,16 +1472,6 @@ ms@0.7.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
|
||||
integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
multimatch@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b"
|
||||
@@ -1780,11 +1567,6 @@ ordered-read-streams@^0.3.0:
|
||||
is-stream "^1.0.1"
|
||||
readable-stream "^2.0.1"
|
||||
|
||||
os-tmpdir@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
|
||||
|
||||
parse-glob@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
|
||||
@@ -1855,16 +1637,11 @@ performance-now@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
pify@^2.0.0, pify@^2.3.0:
|
||||
pify@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||
integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
|
||||
|
||||
pify@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
|
||||
|
||||
pinkie-promise@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
|
||||
@@ -1950,7 +1727,7 @@ read-pkg@^1.0.0:
|
||||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6:
|
||||
readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.3.5, readable-stream@~2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
||||
@@ -2060,7 +1837,7 @@ rimraf@2:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
@@ -2070,29 +1847,11 @@ safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
seek-bzip@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
|
||||
integrity sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=
|
||||
dependencies:
|
||||
commander "~2.8.1"
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
|
||||
|
||||
"service-downloader@github:anthonydresser/service-downloader#0.1.5":
|
||||
version "0.1.5"
|
||||
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/6ebb0465573cc140e461a22f334260f55ef45546"
|
||||
dependencies:
|
||||
decompress "^4.2.0"
|
||||
eventemitter2 "^5.0.1"
|
||||
http-proxy-agent "^2.1.0"
|
||||
https-proxy-agent "^2.2.1"
|
||||
mkdirp "^0.5.1"
|
||||
tmp "^0.0.33"
|
||||
|
||||
sigmund@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
|
||||
@@ -2262,13 +2021,6 @@ strip-bom@^2.0.0:
|
||||
dependencies:
|
||||
is-utf8 "^0.2.0"
|
||||
|
||||
strip-dirs@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
|
||||
integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==
|
||||
dependencies:
|
||||
is-natural-number "^4.0.1"
|
||||
|
||||
strip-indent@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
|
||||
@@ -2291,19 +2043,6 @@ supports-color@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||
integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
|
||||
|
||||
tar-stream@^1.5.2:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
|
||||
integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
|
||||
dependencies:
|
||||
bl "^1.0.0"
|
||||
buffer-alloc "^1.2.0"
|
||||
end-of-stream "^1.0.0"
|
||||
fs-constants "^1.0.0"
|
||||
readable-stream "^2.3.0"
|
||||
to-buffer "^1.1.1"
|
||||
xtend "^4.0.0"
|
||||
|
||||
tar@~0.1.19:
|
||||
version "0.1.20"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-0.1.20.tgz#42940bae5b5f22c74483699126f9f3f27449cb13"
|
||||
@@ -2371,13 +2110,6 @@ time-stamp@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3"
|
||||
integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
|
||||
dependencies:
|
||||
os-tmpdir "~1.0.2"
|
||||
|
||||
to-absolute-glob@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz#1cdfa472a9ef50c239ee66999b662ca0eb39937f"
|
||||
@@ -2385,11 +2117,6 @@ to-absolute-glob@^0.1.1:
|
||||
dependencies:
|
||||
extend-shallow "^2.0.1"
|
||||
|
||||
to-buffer@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
|
||||
integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
|
||||
|
||||
to-iso-string@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1"
|
||||
@@ -2420,14 +2147,6 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
||||
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
||||
|
||||
unbzip2-stream@^1.0.9:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a"
|
||||
integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==
|
||||
dependencies:
|
||||
buffer "^5.2.1"
|
||||
through "^2.3.8"
|
||||
|
||||
unique-stream@^2.0.2:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac"
|
||||
@@ -2588,7 +2307,7 @@ wrappy@1:
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1:
|
||||
"xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.0, xtend@~4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
||||
integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68=
|
||||
@@ -2605,7 +2324,7 @@ xtend@~3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-3.0.0.tgz#5cce7407baf642cba7becda568111c493f59665a"
|
||||
integrity sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=
|
||||
|
||||
yauzl@^2.2.1, yauzl@^2.4.2:
|
||||
yauzl@^2.2.1:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
|
||||
|
||||
1
extensions/agent/.gitignore
vendored
Normal file
1
extensions/agent/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.vsix
|
||||
@@ -1,2 +1,3 @@
|
||||
client/src/**
|
||||
client/tsconfig.json
|
||||
src/**
|
||||
out/test/**
|
||||
tsconfig.json
|
||||
|
||||
2710
extensions/agent/package-lock.json
generated
2710
extensions/agent/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
"name": "agent",
|
||||
"displayName": "SQL Server Agent",
|
||||
"description": "Manage and troubleshoot SQL Server Agent jobs",
|
||||
"version": "0.38.0",
|
||||
"version": "0.40.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
|
||||
|
||||
@@ -17,6 +17,7 @@ export abstract class AgentDialog<T extends IAgentDialogData> {
|
||||
private static readonly CancelButtonText: string = localize('agentDialog.Cancel', 'Cancel');
|
||||
|
||||
protected _onSuccess: vscode.EventEmitter<T> = new vscode.EventEmitter<T>();
|
||||
protected _isOpen: boolean = false;
|
||||
public readonly onSuccess: vscode.Event<T> = this._onSuccess.event;
|
||||
public dialog: azdata.window.Dialog;
|
||||
|
||||
@@ -35,29 +36,34 @@ export abstract class AgentDialog<T extends IAgentDialogData> {
|
||||
protected abstract async initializeDialog(dialog: azdata.window.Dialog);
|
||||
|
||||
public async openDialog(dialogName?: string) {
|
||||
let event = dialogName ? dialogName : null;
|
||||
this.dialog = azdata.window.createModelViewDialog(this.title, event);
|
||||
if (!this._isOpen) {
|
||||
this._isOpen = true;
|
||||
let event = dialogName ? dialogName : null;
|
||||
this.dialog = azdata.window.createModelViewDialog(this.title, event);
|
||||
|
||||
await this.model.initialize();
|
||||
await this.model.initialize();
|
||||
|
||||
await this.initializeDialog(this.dialog);
|
||||
await this.initializeDialog(this.dialog);
|
||||
|
||||
this.dialog.okButton.label = AgentDialog.OkButtonText;
|
||||
this.dialog.okButton.onClick(async () => await this.execute());
|
||||
this.dialog.okButton.label = AgentDialog.OkButtonText;
|
||||
this.dialog.okButton.onClick(async () => await this.execute());
|
||||
|
||||
this.dialog.cancelButton.label = AgentDialog.CancelButtonText;
|
||||
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
||||
this.dialog.cancelButton.label = AgentDialog.CancelButtonText;
|
||||
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
||||
|
||||
azdata.window.openDialog(this.dialog);
|
||||
azdata.window.openDialog(this.dialog);
|
||||
}
|
||||
}
|
||||
|
||||
protected async execute() {
|
||||
this.updateModel();
|
||||
await this.model.save();
|
||||
this._isOpen = false;
|
||||
this._onSuccess.fire(this.model);
|
||||
}
|
||||
|
||||
protected async cancel() {
|
||||
this._isOpen = false;
|
||||
}
|
||||
|
||||
protected getActualConditionValue(checkbox: azdata.CheckBoxComponent, dropdown: azdata.DropDownComponent): azdata.JobCompletionActionCondition {
|
||||
@@ -77,4 +83,8 @@ export abstract class AgentDialog<T extends IAgentDialogData> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get isOpen(): boolean {
|
||||
return this._isOpen;
|
||||
}
|
||||
}
|
||||
@@ -229,7 +229,7 @@ export class JobDialog extends AgentDialog<JobData> {
|
||||
this.StepsTable_FailureColumnString
|
||||
],
|
||||
data: data,
|
||||
height: 650
|
||||
height: 500
|
||||
}).component();
|
||||
|
||||
this.startStepDropdown = view.modelBuilder.dropDown().withProperties({ width: 180 }).component();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"engines": {
|
||||
"vscode": "^1.25.0",
|
||||
"vscode": "^1.30.1",
|
||||
"azdata": "*"
|
||||
},
|
||||
"activationEvents": [
|
||||
@@ -118,6 +118,33 @@
|
||||
]
|
||||
},
|
||||
"menus": {
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "azure.resource.signin",
|
||||
"when": "true"
|
||||
},
|
||||
{
|
||||
"command": "azure.resource.refreshall",
|
||||
"when": "true"
|
||||
},
|
||||
{
|
||||
"command": "azure.resource.selectsubscriptions",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "azure.resource.refresh",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "azure.resource.connectsqlserver",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "azure.resource.connectsqldb",
|
||||
"when": "false"
|
||||
}
|
||||
|
||||
],
|
||||
"view/title": [
|
||||
{
|
||||
"command": "azure.resource.signin",
|
||||
@@ -169,6 +196,7 @@
|
||||
"@types/request": "^2.48.1",
|
||||
"mocha": "^5.2.0",
|
||||
"should": "^13.2.1",
|
||||
"typemoq": "^2.1.0"
|
||||
"typemoq": "^2.1.0",
|
||||
"vscode": "1.1.26"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
"azure.resource.config.title": "Azure Resource Configuration",
|
||||
"azure.resource.config.filter.description": "The resource filter, each element is an account id, a subscription id and name separated by a slash",
|
||||
"azure.resource.explorer.title": "Azure",
|
||||
"azure.resource.refreshall.title": "Refresh All",
|
||||
"azure.resource.refreshall.title": "Azure: Refresh All Accounts",
|
||||
"azure.resource.refresh.title": "Refresh",
|
||||
"azure.resource.signin.title": "Sign In",
|
||||
"azure.resource.signin.title": "Azure: Sign In",
|
||||
"azure.resource.selectsubscriptions.title": "Select Subscriptions",
|
||||
"azure.resource.connectsqlserver.title": "Connect",
|
||||
"azure.resource.connectsqldb.title": "Add to Servers",
|
||||
|
||||
@@ -103,16 +103,16 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
});
|
||||
}
|
||||
|
||||
public prompt(): Thenable<AzureAccount> {
|
||||
public prompt(): Thenable<AzureAccount | azdata.PromptFailedResult> {
|
||||
return this.doIfInitialized(() => this.signIn(true));
|
||||
}
|
||||
|
||||
public refresh(account: AzureAccount): Thenable<AzureAccount> {
|
||||
public refresh(account: AzureAccount): Thenable<AzureAccount | azdata.PromptFailedResult> {
|
||||
return this.doIfInitialized(() => this.signIn(false));
|
||||
}
|
||||
|
||||
// PRIVATE METHODS /////////////////////////////////////////////////////
|
||||
private cancelAutoOAuth(): Thenable<void> {
|
||||
private cancelAutoOAuth(): Promise<void> {
|
||||
let self = this;
|
||||
|
||||
if (!this._inProgressAutoOAuth) {
|
||||
@@ -137,23 +137,23 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private clearAccountTokens(accountKey: azdata.AccountKey): Thenable<void> {
|
||||
private async clearAccountTokens(accountKey: azdata.AccountKey): Promise<void> {
|
||||
// Put together a query to look up any tokens associated with the account key
|
||||
let query = <adal.TokenResponse>{ userId: accountKey.accountId };
|
||||
|
||||
// 1) Look up the tokens associated with the query
|
||||
// 2) Remove them
|
||||
return this._tokenCache.findThenable(query)
|
||||
.then(results => this._tokenCache.removeThenable(results));
|
||||
let results = await this._tokenCache.findThenable(query);
|
||||
this._tokenCache.removeThenable(results);
|
||||
}
|
||||
|
||||
private doIfInitialized<T>(op: () => Thenable<T>): Thenable<T> {
|
||||
private doIfInitialized<T>(op: () => Promise<T>): Promise<T> {
|
||||
return this._isInitialized
|
||||
? op()
|
||||
: Promise.reject(localize('accountProviderNotInitialized', 'Account provider not initialized, cannot perform action'));
|
||||
}
|
||||
|
||||
private getAccessTokens(account: AzureAccount, resource: azdata.AzureResource): Thenable<AzureAccountSecurityTokenCollection> {
|
||||
private getAccessTokens(account: AzureAccount, resource: azdata.AzureResource): Promise<AzureAccountSecurityTokenCollection> {
|
||||
let self = this;
|
||||
|
||||
const resourceIdMap = new Map<azdata.AzureResource, string>([
|
||||
@@ -228,7 +228,7 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
});
|
||||
}
|
||||
|
||||
private getDeviceLoginToken(oAuth: InProgressAutoOAuth, isAddAccount: boolean): Thenable<adal.TokenResponse> {
|
||||
private getDeviceLoginToken(oAuth: InProgressAutoOAuth, isAddAccount: boolean): Thenable<adal.TokenResponse | azdata.PromptFailedResult> {
|
||||
let self = this;
|
||||
|
||||
// 1) Open the auto OAuth dialog
|
||||
@@ -239,14 +239,15 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
localize('refreshAccount', 'Refresh {0} account', self._metadata.displayName);
|
||||
return azdata.accounts.beginAutoOAuthDeviceCode(self._metadata.id, title, oAuth.userCodeInfo.message, oAuth.userCodeInfo.userCode, oAuth.userCodeInfo.verificationUrl)
|
||||
.then(() => {
|
||||
return new Promise<adal.TokenResponse>((resolve, reject) => {
|
||||
return new Promise<adal.TokenResponse | azdata.PromptFailedResult>((resolve, reject) => {
|
||||
let context = oAuth.context;
|
||||
context.acquireTokenWithDeviceCode(self._metadata.settings.signInResourceId, self._metadata.settings.clientId, oAuth.userCodeInfo,
|
||||
(err, response) => {
|
||||
if (err) {
|
||||
if (self._autoOAuthCancelled) {
|
||||
let result: azdata.PromptFailedResult = { canceled: true };
|
||||
// Auto OAuth was cancelled by the user, indicate this with the error we return
|
||||
reject(<azdata.UserCancelledSignInError>{ userCancelledSignIn: true });
|
||||
resolve(result);
|
||||
} else {
|
||||
// Auto OAuth failed for some other reason
|
||||
azdata.accounts.endAutoOAuthDeviceCode();
|
||||
@@ -368,74 +369,73 @@ export class AzureAccountProvider implements azdata.AccountProvider {
|
||||
});
|
||||
}
|
||||
|
||||
private signIn(isAddAccount: boolean): Thenable<AzureAccount> {
|
||||
let self = this;
|
||||
private isPromptFailed(value: adal.TokenResponse | azdata.PromptFailedResult): value is azdata.PromptFailedResult {
|
||||
return value && (<azdata.PromptFailedResult>value).canceled;
|
||||
}
|
||||
|
||||
private async signIn(isAddAccount: boolean): Promise<AzureAccount | azdata.PromptFailedResult> {
|
||||
// 1) Get the user code for this login
|
||||
// 2) Get an access token from the device code
|
||||
// 3) Get the list of tenants
|
||||
// 4) Generate the AzureAccount object and return it
|
||||
let tokenResponse: adal.TokenResponse = null;
|
||||
return this.getDeviceLoginUserCode()
|
||||
.then((result: InProgressAutoOAuth) => {
|
||||
self._autoOAuthCancelled = false;
|
||||
self._inProgressAutoOAuth = result;
|
||||
return self.getDeviceLoginToken(self._inProgressAutoOAuth, isAddAccount);
|
||||
})
|
||||
.then((response: adal.TokenResponse) => {
|
||||
tokenResponse = response;
|
||||
self._autoOAuthCancelled = false;
|
||||
self._inProgressAutoOAuth = null;
|
||||
return self.getTenants(tokenResponse.userId, tokenResponse.userId);
|
||||
})
|
||||
.then((tenants: Tenant[]) => {
|
||||
// Figure out where we're getting the identity from
|
||||
let identityProvider = tokenResponse.identityProvider;
|
||||
if (identityProvider) {
|
||||
identityProvider = identityProvider.toLowerCase();
|
||||
}
|
||||
let result: InProgressAutoOAuth = await this.getDeviceLoginUserCode();
|
||||
this._autoOAuthCancelled = false;
|
||||
this._inProgressAutoOAuth = result;
|
||||
let response: adal.TokenResponse | azdata.PromptFailedResult = await this.getDeviceLoginToken(this._inProgressAutoOAuth, isAddAccount);
|
||||
if (this.isPromptFailed(response)) {
|
||||
return response;
|
||||
}
|
||||
tokenResponse = response;
|
||||
this._autoOAuthCancelled = false;
|
||||
this._inProgressAutoOAuth = null;
|
||||
let tenants: Tenant[] = await this.getTenants(tokenResponse.userId, tokenResponse.userId);
|
||||
// Figure out where we're getting the identity from
|
||||
let identityProvider = tokenResponse.identityProvider;
|
||||
if (identityProvider) {
|
||||
identityProvider = identityProvider.toLowerCase();
|
||||
}
|
||||
|
||||
// Determine if this is a microsoft account
|
||||
let msa = identityProvider && (
|
||||
identityProvider.indexOf('live.com') !== -1 ||
|
||||
identityProvider.indexOf('live-int.com') !== -1 ||
|
||||
identityProvider.indexOf('f8cdef31-a31e-4b4a-93e4-5f571e91255a') !== -1 ||
|
||||
identityProvider.indexOf('ea8a4392-515e-481f-879e-6571ff2a8a36') !== -1);
|
||||
// Determine if this is a microsoft account
|
||||
let msa = identityProvider && (
|
||||
identityProvider.indexOf('live.com') !== -1 ||
|
||||
identityProvider.indexOf('live-int.com') !== -1 ||
|
||||
identityProvider.indexOf('f8cdef31-a31e-4b4a-93e4-5f571e91255a') !== -1 ||
|
||||
identityProvider.indexOf('ea8a4392-515e-481f-879e-6571ff2a8a36') !== -1);
|
||||
|
||||
// Calculate the display name for the user
|
||||
let displayName = (tokenResponse.givenName && tokenResponse.familyName)
|
||||
? `${tokenResponse.givenName} ${tokenResponse.familyName}`
|
||||
: tokenResponse.userId;
|
||||
// Calculate the display name for the user
|
||||
let displayName = (tokenResponse.givenName && tokenResponse.familyName)
|
||||
? `${tokenResponse.givenName} ${tokenResponse.familyName}`
|
||||
: tokenResponse.userId;
|
||||
|
||||
// Calculate the home tenant display name to use for the contextual display name
|
||||
let contextualDisplayName = msa
|
||||
? localize('microsoftAccountDisplayName', 'Microsoft Account')
|
||||
: tenants[0].displayName;
|
||||
// Calculate the home tenant display name to use for the contextual display name
|
||||
let contextualDisplayName = msa
|
||||
? localize('microsoftAccountDisplayName', 'Microsoft Account')
|
||||
: tenants[0].displayName;
|
||||
|
||||
// Calculate the account type
|
||||
let accountType = msa
|
||||
? AzureAccountProvider.MicrosoftAccountType
|
||||
: AzureAccountProvider.WorkSchoolAccountType;
|
||||
// Calculate the account type
|
||||
let accountType = msa
|
||||
? AzureAccountProvider.MicrosoftAccountType
|
||||
: AzureAccountProvider.WorkSchoolAccountType;
|
||||
|
||||
return <AzureAccount>{
|
||||
key: {
|
||||
providerId: self._metadata.id,
|
||||
accountId: tokenResponse.userId
|
||||
},
|
||||
name: tokenResponse.userId,
|
||||
displayInfo: {
|
||||
accountType: accountType,
|
||||
userId: tokenResponse.userId,
|
||||
contextualDisplayName: contextualDisplayName,
|
||||
displayName: displayName
|
||||
},
|
||||
properties: {
|
||||
isMsAccount: msa,
|
||||
tenants: tenants
|
||||
},
|
||||
isStale: false
|
||||
};
|
||||
});
|
||||
return <AzureAccount>{
|
||||
key: {
|
||||
providerId: this._metadata.id,
|
||||
accountId: tokenResponse.userId
|
||||
},
|
||||
name: tokenResponse.userId,
|
||||
displayInfo: {
|
||||
accountType: accountType,
|
||||
userId: tokenResponse.userId,
|
||||
contextualDisplayName: contextualDisplayName,
|
||||
displayName: displayName
|
||||
},
|
||||
properties: {
|
||||
isMsAccount: msa,
|
||||
tenants: tenants
|
||||
},
|
||||
isStale: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export enum AzureResourceItemType {
|
||||
}
|
||||
|
||||
export enum AzureResourceServiceNames {
|
||||
resourceService = 'AzureResourceService',
|
||||
cacheService = 'AzureResourceCacheService',
|
||||
accountService = 'AzureResourceAccountService',
|
||||
subscriptionService = 'AzureResourceSubscriptionService',
|
||||
|
||||
@@ -12,11 +12,11 @@ import { azureResource } from './azure-resource';
|
||||
import { IAzureResourceNodeWithProviderId } from './interfaces';
|
||||
|
||||
export class AzureResourceService {
|
||||
private constructor() {
|
||||
}
|
||||
private _areResourceProvidersLoaded: boolean = false;
|
||||
private _resourceProviders: { [resourceProviderId: string]: azureResource.IAzureResourceProvider } = {};
|
||||
private _treeDataProviders: { [resourceProviderId: string]: azureResource.IAzureResourceTreeDataProvider } = {};
|
||||
|
||||
public static getInstance(): AzureResourceService {
|
||||
return AzureResourceService._instance;
|
||||
public constructor() {
|
||||
}
|
||||
|
||||
public async listResourceProviderIds(): Promise<string[]> {
|
||||
@@ -121,9 +121,4 @@ export class AzureResourceService {
|
||||
this._treeDataProviders[resourceProvider.providerId] = resourceProvider.getTreeDataProvider();
|
||||
}
|
||||
|
||||
private _areResourceProvidersLoaded: boolean = false;
|
||||
private _resourceProviders: { [resourceProviderId: string]: azureResource.IAzureResourceProvider } = {};
|
||||
private _treeDataProviders: { [resourceProviderId: string]: azureResource.IAzureResourceTreeDataProvider } = {};
|
||||
|
||||
private static readonly _instance = new AzureResourceService();
|
||||
}
|
||||
@@ -15,14 +15,19 @@ import { AzureResourceService } from './resourceService';
|
||||
import { IAzureResourceNodeWithProviderId } from './interfaces';
|
||||
import { AzureResourceMessageTreeNode } from './messageTreeNode';
|
||||
import { AzureResourceErrorMessageUtil } from './utils';
|
||||
import { AppContext } from '../appContext';
|
||||
import { AzureResourceServiceNames } from './constants';
|
||||
|
||||
export class AzureResourceResourceTreeNode extends TreeNode {
|
||||
private _resourceService: AzureResourceService;
|
||||
|
||||
public constructor(
|
||||
public readonly resourceNodeWithProviderId: IAzureResourceNodeWithProviderId,
|
||||
parent: TreeNode
|
||||
parent: TreeNode,
|
||||
private appContext: AppContext
|
||||
) {
|
||||
super();
|
||||
|
||||
this._resourceService = appContext.getService<AzureResourceService>(AzureResourceServiceNames.resourceService);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@@ -36,12 +41,12 @@ export class AzureResourceResourceTreeNode extends TreeNode {
|
||||
const children = await this._resourceService.getChildren(this.resourceNodeWithProviderId.resourceProviderId, this.resourceNodeWithProviderId.resourceNode);
|
||||
|
||||
if (children.length === 0) {
|
||||
return [AzureResourceMessageTreeNode.create(AzureResourceResourceTreeNode.noResourcesLabel, this)];
|
||||
return [AzureResourceMessageTreeNode.create(localize('azure.resource.resourceTreeNode.noResourcesLabel', 'No Resources found'), this)];
|
||||
} else {
|
||||
return children.map((child) => {
|
||||
// To make tree node's id unique, otherwise, treeModel.js would complain 'item already registered'
|
||||
child.resourceNode.treeItem.id = `${this.resourceNodeWithProviderId.resourceNode.treeItem.id}.${child.resourceNode.treeItem.id}`;
|
||||
return new AzureResourceResourceTreeNode(child, this);
|
||||
return new AzureResourceResourceTreeNode(child, this, this.appContext);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -73,7 +78,4 @@ export class AzureResourceResourceTreeNode extends TreeNode {
|
||||
return this.resourceNodeWithProviderId.resourceNode.treeItem.id;
|
||||
}
|
||||
|
||||
private _resourceService = AzureResourceService.getInstance();
|
||||
|
||||
private static readonly noResourcesLabel = localize('azure.resource.resourceTreeNode.noResourcesLabel', 'No Resources found.');
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import { azureResource } from '../azure-resource';
|
||||
import { TreeNode } from '../treeNode';
|
||||
import { IAzureResourceNodeWithProviderId } from '../interfaces';
|
||||
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
|
||||
import { AzureResourceItemType } from '../constants';
|
||||
import { AzureResourceItemType, AzureResourceServiceNames } from '../constants';
|
||||
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
|
||||
import { AzureResourceMessageTreeNode } from '../messageTreeNode';
|
||||
import { AzureResourceErrorMessageUtil } from '../utils';
|
||||
@@ -39,7 +39,7 @@ export class AzureResourceSubscriptionTreeNode extends AzureResourceContainerTre
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
try {
|
||||
const resourceService = AzureResourceService.getInstance();
|
||||
const resourceService = this.appContext.getService<AzureResourceService>(AzureResourceServiceNames.resourceService);
|
||||
|
||||
const children: IAzureResourceNodeWithProviderId[] = [];
|
||||
|
||||
@@ -53,7 +53,7 @@ export class AzureResourceSubscriptionTreeNode extends AzureResourceContainerTre
|
||||
return children.map((child) => {
|
||||
// To make tree node's id unique, otherwise, treeModel.js would complain 'item already registered'
|
||||
child.resourceNode.treeItem.id = `${this._id}.${child.resourceNode.treeItem.id}`;
|
||||
return new AzureResourceResourceTreeNode(child, this);
|
||||
return new AzureResourceResourceTreeNode(child, this, this.appContext);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
'use strict';
|
||||
|
||||
import { TreeDataProvider, EventEmitter, Event, TreeItem } from 'vscode';
|
||||
import { setInterval, clearInterval } from 'timers';
|
||||
import * as azdata from 'azdata';
|
||||
import { AppContext } from '../../appContext';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
@@ -16,15 +16,40 @@ import { AzureResourceAccountTreeNode } from './accountTreeNode';
|
||||
import { AzureResourceAccountNotSignedInTreeNode } from './accountNotSignedInTreeNode';
|
||||
import { AzureResourceMessageTreeNode } from '../messageTreeNode';
|
||||
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
|
||||
import { AzureResourceErrorMessageUtil } from '../utils';
|
||||
import { AzureResourceErrorMessageUtil, equals } from '../utils';
|
||||
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
|
||||
import { IAzureResourceAccountService } from '../../azureResource/interfaces';
|
||||
import { AzureResourceServiceNames } from '../constants';
|
||||
|
||||
|
||||
export class AzureResourceTreeProvider implements TreeDataProvider<TreeNode>, IAzureResourceTreeChangeHandler {
|
||||
public constructor(
|
||||
public readonly appContext: AppContext
|
||||
) {
|
||||
public isSystemInitialized: boolean = false;
|
||||
|
||||
private accountService: IAzureResourceAccountService;
|
||||
private accounts: azdata.Account[];
|
||||
private _onDidChangeTreeData = new EventEmitter<TreeNode>();
|
||||
private loadingAccountsPromise: Promise<void>;
|
||||
|
||||
public constructor(public readonly appContext: AppContext) {
|
||||
if (appContext) {
|
||||
this.hookAccountService(appContext);
|
||||
}
|
||||
}
|
||||
|
||||
private hookAccountService(appContext: AppContext): void {
|
||||
this.accountService = appContext.getService<IAzureResourceAccountService>(AzureResourceServiceNames.accountService);
|
||||
if (this.accountService) {
|
||||
this.accountService.onDidChangeAccounts((e: azdata.DidChangeAccountsParams) => {
|
||||
// the onDidChangeAccounts event will trigger in many cases where the accounts didn't actually change
|
||||
// the notifyNodeChanged event triggers a refresh which triggers a getChildren which can trigger this callback
|
||||
// this below check short-circuits the infinite callback loop
|
||||
this.setSystemInitialized();
|
||||
if (!equals(e.accounts, this.accounts)) {
|
||||
this.accounts = e.accounts;
|
||||
this.notifyNodeChanged(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async getChildren(element?: TreeNode): Promise<TreeNode[]> {
|
||||
@@ -32,34 +57,16 @@ export class AzureResourceTreeProvider implements TreeDataProvider<TreeNode>, IA
|
||||
return element.getChildren(true);
|
||||
}
|
||||
|
||||
if (!this.isSystemInitialized && !this._loadingTimer) {
|
||||
this._loadingTimer = setInterval(async () => {
|
||||
try {
|
||||
// Call azdata.accounts.getAllAccounts() to determine whether the system has been initialized.
|
||||
await this.appContext.getService<IAzureResourceAccountService>(AzureResourceServiceNames.accountService).getAccounts();
|
||||
|
||||
// System has been initialized
|
||||
this.isSystemInitialized = true;
|
||||
|
||||
if (this._loadingTimer) {
|
||||
clearInterval(this._loadingTimer);
|
||||
}
|
||||
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
} catch (error) {
|
||||
// System not initialized yet
|
||||
this.isSystemInitialized = false;
|
||||
}
|
||||
}, AzureResourceTreeProvider.loadingTimerInterval);
|
||||
|
||||
return [AzureResourceMessageTreeNode.create(AzureResourceTreeProvider.loadingLabel, undefined)];
|
||||
if (!this.isSystemInitialized) {
|
||||
if (!this.loadingAccountsPromise) {
|
||||
this.loadingAccountsPromise = this.loadAccounts();
|
||||
}
|
||||
return [AzureResourceMessageTreeNode.create(localize('azure.resource.tree.treeProvider.loadingLabel', 'Loading ...'), undefined)];
|
||||
}
|
||||
|
||||
try {
|
||||
const accounts = await this.appContext.getService<IAzureResourceAccountService>(AzureResourceServiceNames.accountService).getAccounts();
|
||||
|
||||
if (accounts && accounts.length > 0) {
|
||||
return accounts.map((account) => new AzureResourceAccountTreeNode(account, this.appContext, this));
|
||||
if (this.accounts && this.accounts.length > 0) {
|
||||
return this.accounts.map((account) => new AzureResourceAccountTreeNode(account, this.appContext, this));
|
||||
} else {
|
||||
return [new AzureResourceAccountNotSignedInTreeNode()];
|
||||
}
|
||||
@@ -68,6 +75,23 @@ export class AzureResourceTreeProvider implements TreeDataProvider<TreeNode>, IA
|
||||
}
|
||||
}
|
||||
|
||||
private async loadAccounts(): Promise<void> {
|
||||
try {
|
||||
this.accounts = await this.appContext.getService<IAzureResourceAccountService>(AzureResourceServiceNames.accountService).getAccounts();
|
||||
// System has been initialized
|
||||
this.setSystemInitialized();
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
} catch (err) {
|
||||
// Skip for now, we can assume that the accounts changed event will eventually notify instead
|
||||
this.isSystemInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
private setSystemInitialized(): void {
|
||||
this.isSystemInitialized = true;
|
||||
this.loadingAccountsPromise = undefined;
|
||||
}
|
||||
|
||||
public get onDidChangeTreeData(): Event<TreeNode> {
|
||||
return this._onDidChangeTreeData.event;
|
||||
}
|
||||
@@ -89,12 +113,4 @@ export class AzureResourceTreeProvider implements TreeDataProvider<TreeNode>, IA
|
||||
public getTreeItem(element: TreeNode): TreeItem | Thenable<TreeItem> {
|
||||
return element.getTreeItem();
|
||||
}
|
||||
|
||||
public isSystemInitialized: boolean = false;
|
||||
|
||||
private _loadingTimer: NodeJS.Timer = undefined;
|
||||
private _onDidChangeTreeData = new EventEmitter<TreeNode>();
|
||||
|
||||
private static readonly loadingLabel = localize('azure.resource.tree.treeProvider.loadingLabel', 'Loading ...');
|
||||
private static readonly loadingTimerInterval = 5000;
|
||||
}
|
||||
|
||||
@@ -1,62 +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 ControllerBase from './controllerBase';
|
||||
import { DidChangeAccountsParams, Account } from 'azdata';
|
||||
|
||||
import {
|
||||
IAzureResourceCacheService,
|
||||
IAzureResourceAccountService,
|
||||
IAzureResourceSubscriptionService,
|
||||
IAzureResourceSubscriptionFilterService,
|
||||
IAzureResourceTenantService
|
||||
} from '../azureResource/interfaces';
|
||||
import { AzureResourceServiceNames } from '../azureResource/constants';
|
||||
import { AzureResourceTreeProvider } from '../azureResource/tree/treeProvider';
|
||||
import { registerAzureResourceCommands } from '../azureResource/commands';
|
||||
import { AzureResourceAccountService } from '../azureResource/services/accountService';
|
||||
import { AzureResourceSubscriptionService } from '../azureResource/services/subscriptionService';
|
||||
import { AzureResourceSubscriptionFilterService } from '../azureResource/services/subscriptionFilterService';
|
||||
import { AzureResourceCacheService } from '../azureResource/services/cacheService';
|
||||
import { AzureResourceTenantService } from '../azureResource/services/tenantService';
|
||||
|
||||
import { registerAzureResourceDatabaseServerCommands } from '../azureResource/providers/databaseServer/commands';
|
||||
import { registerAzureResourceDatabaseCommands } from '../azureResource/providers/database/commands';
|
||||
import { equals } from '../azureResource/utils';
|
||||
|
||||
export default class AzureResourceController extends ControllerBase {
|
||||
public activate(): Promise<boolean> {
|
||||
this.appContext.registerService<IAzureResourceCacheService>(AzureResourceServiceNames.cacheService, new AzureResourceCacheService(this.extensionContext));
|
||||
this.appContext.registerService<IAzureResourceAccountService>(AzureResourceServiceNames.accountService, new AzureResourceAccountService(this.apiWrapper));
|
||||
this.appContext.registerService<IAzureResourceSubscriptionService>(AzureResourceServiceNames.subscriptionService, new AzureResourceSubscriptionService());
|
||||
this.appContext.registerService<IAzureResourceSubscriptionFilterService>(AzureResourceServiceNames.subscriptionFilterService, new AzureResourceSubscriptionFilterService(new AzureResourceCacheService(this.extensionContext)));
|
||||
this.appContext.registerService<IAzureResourceTenantService>(AzureResourceServiceNames.tenantService, new AzureResourceTenantService());
|
||||
|
||||
const azureResourceTree = new AzureResourceTreeProvider(this.appContext);
|
||||
this.extensionContext.subscriptions.push(this.apiWrapper.registerTreeDataProvider('azureResourceExplorer', azureResourceTree));
|
||||
|
||||
let previousAccounts: Array<Account> = undefined;
|
||||
this.appContext.getService<IAzureResourceAccountService>(AzureResourceServiceNames.accountService).onDidChangeAccounts((e: DidChangeAccountsParams) => {
|
||||
// the onDidChangeAccounts event will trigger in many cases where the accounts didn't actually change
|
||||
// the notifyNodeChanged event triggers a refresh which triggers a getChildren which can trigger this callback
|
||||
// this below check short-circuits the infinite callback loop
|
||||
if (!equals(e.accounts, previousAccounts)) {
|
||||
azureResourceTree.notifyNodeChanged(undefined);
|
||||
}
|
||||
previousAccounts = e.accounts;
|
||||
});
|
||||
|
||||
registerAzureResourceCommands(this.appContext, azureResourceTree);
|
||||
|
||||
registerAzureResourceDatabaseServerCommands(this.appContext);
|
||||
|
||||
registerAzureResourceDatabaseCommands(this.appContext);
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
public deactivate(): void {
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { AppContext } from '../appContext';
|
||||
import { ApiWrapper } from '../apiWrapper';
|
||||
|
||||
export default abstract class ControllerBase implements vscode.Disposable {
|
||||
|
||||
public constructor(protected appContext: AppContext) {
|
||||
}
|
||||
|
||||
protected get apiWrapper(): ApiWrapper {
|
||||
return this.appContext.apiWrapper;
|
||||
}
|
||||
|
||||
public get extensionContext(): vscode.ExtensionContext {
|
||||
return this.appContext && this.appContext.extensionContext;
|
||||
}
|
||||
|
||||
abstract activate(): Promise<boolean>;
|
||||
|
||||
abstract deactivate(): void;
|
||||
|
||||
public dispose(): void {
|
||||
this.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,7 @@ import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as constants from './constants';
|
||||
|
||||
import AzureResourceController from './controllers/azureResourceController';
|
||||
import { AppContext } from './appContext';
|
||||
import ControllerBase from './controllers/controllerBase';
|
||||
import { ApiWrapper } from './apiWrapper';
|
||||
import { AzureAccountProviderService } from './account-provider/azureAccountProviderService';
|
||||
|
||||
@@ -19,9 +17,20 @@ import { AzureResourceDatabaseServerProvider } from './azureResource/providers/d
|
||||
import { AzureResourceDatabaseServerService } from './azureResource/providers/databaseServer/databaseServerService';
|
||||
import { AzureResourceDatabaseProvider } from './azureResource/providers/database/databaseProvider';
|
||||
import { AzureResourceDatabaseService } from './azureResource/providers/database/databaseService';
|
||||
import { AzureResourceService } from './azureResource/resourceService';
|
||||
import { IAzureResourceCacheService, IAzureResourceAccountService, IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService, IAzureResourceTenantService } from './azureResource/interfaces';
|
||||
import { AzureResourceServiceNames } from './azureResource/constants';
|
||||
import { AzureResourceAccountService } from './azureResource/services/accountService';
|
||||
import { AzureResourceSubscriptionService } from './azureResource/services/subscriptionService';
|
||||
import { AzureResourceSubscriptionFilterService } from './azureResource/services/subscriptionFilterService';
|
||||
import { AzureResourceCacheService } from './azureResource/services/cacheService';
|
||||
import { AzureResourceTenantService } from './azureResource/services/tenantService';
|
||||
import { registerAzureResourceCommands } from './azureResource/commands';
|
||||
import { registerAzureResourceDatabaseServerCommands } from './azureResource/providers/databaseServer/commands';
|
||||
import { registerAzureResourceDatabaseCommands } from './azureResource/providers/database/commands';
|
||||
import { AzureResourceTreeProvider } from './azureResource/tree/treeProvider';
|
||||
|
||||
let controllers: ControllerBase[] = [];
|
||||
|
||||
let extensionContext: vscode.ExtensionContext;
|
||||
|
||||
// The function is a duplicate of \src\paths.js. IT would be better to import path.js but it doesn't
|
||||
// work for now because the extension is running in different process.
|
||||
@@ -39,36 +48,29 @@ export function getDefaultLogLocation() {
|
||||
return path.join(getAppDataPath(), 'azuredatastudio');
|
||||
}
|
||||
|
||||
function pushDisposable(disposable: vscode.Disposable): void {
|
||||
extensionContext.subscriptions.push(disposable);
|
||||
}
|
||||
|
||||
// this method is called when your extension is activated
|
||||
// your extension is activated the very first time the command is executed
|
||||
export function activate(extensionContext: vscode.ExtensionContext) {
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
extensionContext = context;
|
||||
const apiWrapper = new ApiWrapper();
|
||||
let appContext = new AppContext(extensionContext, apiWrapper);
|
||||
let activations: Thenable<boolean>[] = [];
|
||||
|
||||
// Create the folder for storing the token caches
|
||||
let storagePath = path.join(getDefaultLogLocation(), constants.extensionName);
|
||||
try {
|
||||
if (!fs.existsSync(storagePath)) {
|
||||
fs.mkdirSync(storagePath);
|
||||
console.log('Initialized Azure account extension storage.');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Initialization of Azure account extension storage failed: ${e}`);
|
||||
console.error('Azure accounts will not be available');
|
||||
let storagePath = findOrMakeStoragePath();
|
||||
if (!storagePath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Create the provider service and activate
|
||||
const accountProviderService = new AzureAccountProviderService(extensionContext, storagePath);
|
||||
extensionContext.subscriptions.push(accountProviderService);
|
||||
accountProviderService.activate();
|
||||
initAzureAccountProvider(extensionContext, storagePath);
|
||||
|
||||
const azureResourceController = new AzureResourceController(appContext);
|
||||
controllers.push(azureResourceController);
|
||||
extensionContext.subscriptions.push(azureResourceController);
|
||||
activations.push(azureResourceController.activate());
|
||||
registerAzureServices(appContext);
|
||||
const azureResourceTree = new AzureResourceTreeProvider(appContext);
|
||||
pushDisposable(apiWrapper.registerTreeDataProvider('azureResourceExplorer', azureResourceTree));
|
||||
registerCommands(appContext, azureResourceTree);
|
||||
|
||||
return {
|
||||
provideResources() {
|
||||
@@ -80,9 +82,46 @@ export function activate(extensionContext: vscode.ExtensionContext) {
|
||||
};
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export function deactivate() {
|
||||
for (let controller of controllers) {
|
||||
controller.deactivate();
|
||||
// Create the folder for storing the token caches
|
||||
function findOrMakeStoragePath() {
|
||||
let storagePath = path.join(getDefaultLogLocation(), constants.extensionName);
|
||||
try {
|
||||
if (!fs.existsSync(storagePath)) {
|
||||
fs.mkdirSync(storagePath);
|
||||
console.log('Initialized Azure account extension storage.');
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(`Initialization of Azure account extension storage failed: ${e}`);
|
||||
console.error('Azure accounts will not be available');
|
||||
}
|
||||
return storagePath;
|
||||
}
|
||||
|
||||
async function initAzureAccountProvider(extensionContext: vscode.ExtensionContext, storagePath: string): Promise<void> {
|
||||
try {
|
||||
const accountProviderService = new AzureAccountProviderService(extensionContext, storagePath);
|
||||
extensionContext.subscriptions.push(accountProviderService);
|
||||
await accountProviderService.activate();
|
||||
} catch (err) {
|
||||
console.log('Unexpected error starting account provider: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
function registerAzureServices(appContext: AppContext): void {
|
||||
appContext.registerService<AzureResourceService>(AzureResourceServiceNames.resourceService, new AzureResourceService());
|
||||
appContext.registerService<IAzureResourceAccountService>(AzureResourceServiceNames.accountService, new AzureResourceAccountService(appContext.apiWrapper));
|
||||
appContext.registerService<IAzureResourceCacheService>(AzureResourceServiceNames.cacheService, new AzureResourceCacheService(extensionContext));
|
||||
appContext.registerService<IAzureResourceSubscriptionService>(AzureResourceServiceNames.subscriptionService, new AzureResourceSubscriptionService());
|
||||
appContext.registerService<IAzureResourceSubscriptionFilterService>(AzureResourceServiceNames.subscriptionFilterService, new AzureResourceSubscriptionFilterService(new AzureResourceCacheService(extensionContext)));
|
||||
appContext.registerService<IAzureResourceTenantService>(AzureResourceServiceNames.tenantService, new AzureResourceTenantService());
|
||||
}
|
||||
|
||||
function registerCommands(appContext: AppContext, azureResourceTree: AzureResourceTreeProvider): void {
|
||||
registerAzureResourceCommands(appContext, azureResourceTree);
|
||||
|
||||
registerAzureResourceDatabaseServerCommands(appContext);
|
||||
|
||||
registerAzureResourceDatabaseCommands(appContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void
|
||||
should(child.tenantId).equal(mockTenantId);
|
||||
should(child.treeItem.id).equal(`databaseServer_${database.serverFullName}.database_${database.name}`);
|
||||
should(child.treeItem.label).equal(`${database.name} (${database.serverName})`);
|
||||
should(child.treeItem.collapsibleState).equal(vscode.TreeItemCollapsibleState.None);
|
||||
should(child.treeItem.collapsibleState).equal(vscode.TreeItemCollapsibleState.Collapsed);
|
||||
should(child.treeItem.contextValue).equal(AzureResourceItemType.database);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -142,7 +142,7 @@ describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function ():
|
||||
should(child.tenantId).equal(mockTenantId);
|
||||
should(child.treeItem.id).equal(`databaseServer_${databaseServer.name}`);
|
||||
should(child.treeItem.label).equal(databaseServer.name);
|
||||
should(child.treeItem.collapsibleState).equal(vscode.TreeItemCollapsibleState.None);
|
||||
should(child.treeItem.collapsibleState).equal(vscode.TreeItemCollapsibleState.Collapsed);
|
||||
should(child.treeItem.contextValue).equal(AzureResourceItemType.databaseServer);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ let mockResourceProvider1: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
|
||||
let mockResourceTreeDataProvider2: TypeMoq.IMock<azureResource.IAzureResourceTreeDataProvider>;
|
||||
let mockResourceProvider2: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
|
||||
|
||||
const resourceService: AzureResourceService = AzureResourceService.getInstance();
|
||||
let resourceService: AzureResourceService;
|
||||
|
||||
describe('AzureResourceService.listResourceProviderIds', function(): void {
|
||||
beforeEach(() => {
|
||||
@@ -61,6 +61,7 @@ describe('AzureResourceService.listResourceProviderIds', function(): void {
|
||||
mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2');
|
||||
mockResourceProvider2.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider2.object);
|
||||
|
||||
resourceService = new AzureResourceService();
|
||||
resourceService.clearResourceProviders();
|
||||
resourceService.areResourceProvidersLoaded = true;
|
||||
});
|
||||
|
||||
@@ -14,8 +14,9 @@ import 'mocha';
|
||||
import { azureResource } from '../../azureResource/azure-resource';
|
||||
import { AzureResourceService } from '../../azureResource/resourceService';
|
||||
import { AzureResourceResourceTreeNode } from '../../azureResource/resourceTreeNode';
|
||||
|
||||
const resourceService = AzureResourceService.getInstance();
|
||||
import { AppContext } from '../../appContext';
|
||||
import { ApiWrapper } from '../../apiWrapper';
|
||||
import { AzureResourceServiceNames } from '../../azureResource/constants';
|
||||
|
||||
// Mock test data
|
||||
const mockAccount: azdata.Account = {
|
||||
@@ -85,6 +86,8 @@ const mockResourceNodes: azureResource.IAzureResourceNode[] = [mockResourceNode1
|
||||
|
||||
let mockResourceTreeDataProvider: TypeMoq.IMock<azureResource.IAzureResourceTreeDataProvider>;
|
||||
let mockResourceProvider: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
|
||||
let resourceService: AzureResourceService;
|
||||
let appContext: AppContext;
|
||||
|
||||
describe('AzureResourceResourceTreeNode.info', function(): void {
|
||||
beforeEach(() => {
|
||||
@@ -95,18 +98,20 @@ describe('AzureResourceResourceTreeNode.info', function(): void {
|
||||
mockResourceProvider = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
|
||||
mockResourceProvider.setup((o) => o.providerId).returns(() => mockResourceProviderId);
|
||||
mockResourceProvider.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider.object);
|
||||
|
||||
resourceService = new AzureResourceService();
|
||||
resourceService.clearResourceProviders();
|
||||
resourceService.registerResourceProvider(mockResourceProvider.object);
|
||||
|
||||
resourceService.areResourceProvidersLoaded = true;
|
||||
|
||||
appContext = new AppContext(undefined, new ApiWrapper());
|
||||
appContext.registerService(AzureResourceServiceNames.resourceService, resourceService);
|
||||
});
|
||||
|
||||
it('Should be correct when created.', async function(): Promise<void> {
|
||||
const resourceTreeNode = new AzureResourceResourceTreeNode({
|
||||
resourceProviderId: mockResourceProviderId,
|
||||
resourceNode: mockResourceRootNode
|
||||
}, undefined);
|
||||
}, undefined, appContext);
|
||||
|
||||
should(resourceTreeNode.nodePathValue).equal(mockResourceRootNode.treeItem.id);
|
||||
|
||||
@@ -133,17 +138,21 @@ describe('AzureResourceResourceTreeNode.getChildren', function(): void {
|
||||
mockResourceProvider.setup((o) => o.providerId).returns(() => mockResourceProviderId);
|
||||
mockResourceProvider.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider.object);
|
||||
|
||||
resourceService = new AzureResourceService();
|
||||
resourceService.clearResourceProviders();
|
||||
resourceService.registerResourceProvider(mockResourceProvider.object);
|
||||
|
||||
resourceService.areResourceProvidersLoaded = true;
|
||||
|
||||
appContext = new AppContext(undefined, new ApiWrapper());
|
||||
appContext.registerService(AzureResourceServiceNames.resourceService, resourceService);
|
||||
});
|
||||
|
||||
it('Should return resource nodes when it is container node.', async function(): Promise<void> {
|
||||
const resourceTreeNode = new AzureResourceResourceTreeNode({
|
||||
resourceProviderId: mockResourceProviderId,
|
||||
resourceNode: mockResourceRootNode
|
||||
}, undefined);
|
||||
},
|
||||
undefined, appContext);
|
||||
|
||||
const children = await resourceTreeNode.getChildren();
|
||||
|
||||
@@ -173,7 +182,7 @@ describe('AzureResourceResourceTreeNode.getChildren', function(): void {
|
||||
const resourceTreeNode = new AzureResourceResourceTreeNode({
|
||||
resourceProviderId: mockResourceProviderId,
|
||||
resourceNode: mockResourceNode1
|
||||
}, undefined);
|
||||
}, undefined, appContext);
|
||||
|
||||
const children = await resourceTreeNode.getChildren();
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { IAzureResourceCacheService } from '../../../azureResource/interfaces';
|
||||
import { generateGuid } from '../../../azureResource/utils';
|
||||
|
||||
// Mock services
|
||||
let mockAppContext: AppContext;
|
||||
let appContext: AppContext;
|
||||
|
||||
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
||||
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||
@@ -60,7 +60,7 @@ let mockResourceProvider1: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
|
||||
let mockResourceTreeDataProvider2: TypeMoq.IMock<azureResource.IAzureResourceTreeDataProvider>;
|
||||
let mockResourceProvider2: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
|
||||
|
||||
const resourceService: AzureResourceService = AzureResourceService.getInstance();
|
||||
const resourceService: AzureResourceService = new AzureResourceService();
|
||||
|
||||
describe('AzureResourceSubscriptionTreeNode.info', function(): void {
|
||||
beforeEach(() => {
|
||||
@@ -68,10 +68,6 @@ describe('AzureResourceSubscriptionTreeNode.info', function(): void {
|
||||
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||
mockCacheService = TypeMoq.Mock.ofType<IAzureResourceCacheService>();
|
||||
|
||||
mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object);
|
||||
|
||||
mockAppContext.registerService<IAzureResourceCacheService>(AzureResourceServiceNames.cacheService, mockCacheService.object);
|
||||
|
||||
mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid());
|
||||
|
||||
mockTreeChangeHandler = TypeMoq.Mock.ofType<IAzureResourceTreeChangeHandler>();
|
||||
@@ -93,12 +89,16 @@ describe('AzureResourceSubscriptionTreeNode.info', function(): void {
|
||||
resourceService.clearResourceProviders();
|
||||
resourceService.registerResourceProvider(mockResourceProvider1.object);
|
||||
resourceService.registerResourceProvider(mockResourceProvider2.object);
|
||||
|
||||
resourceService.areResourceProvidersLoaded = true;
|
||||
|
||||
appContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object);
|
||||
appContext.registerService<IAzureResourceCacheService>(AzureResourceServiceNames.cacheService, mockCacheService.object);
|
||||
appContext.registerService(AzureResourceServiceNames.resourceService, resourceService);
|
||||
|
||||
});
|
||||
|
||||
it('Should be correct when created.', async function(): Promise<void> {
|
||||
const subscriptionTreeNode = new AzureResourceSubscriptionTreeNode(mockAccount, mockSubscription, mockTenantId, mockAppContext, mockTreeChangeHandler.object, undefined);
|
||||
const subscriptionTreeNode = new AzureResourceSubscriptionTreeNode(mockAccount, mockSubscription, mockTenantId, appContext, mockTreeChangeHandler.object, undefined);
|
||||
|
||||
should(subscriptionTreeNode.nodePathValue).equal(`account_${mockAccount.key.accountId}.subscription_${mockSubscription.id}.tenant_${mockTenantId}`);
|
||||
|
||||
@@ -121,9 +121,6 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void {
|
||||
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||
mockCacheService = TypeMoq.Mock.ofType<IAzureResourceCacheService>();
|
||||
|
||||
mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object);
|
||||
mockAppContext.registerService<IAzureResourceCacheService>(AzureResourceServiceNames.cacheService, mockCacheService.object);
|
||||
|
||||
mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid());
|
||||
|
||||
mockTreeChangeHandler = TypeMoq.Mock.ofType<IAzureResourceTreeChangeHandler>();
|
||||
@@ -145,12 +142,16 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void {
|
||||
resourceService.clearResourceProviders();
|
||||
resourceService.registerResourceProvider(mockResourceProvider1.object);
|
||||
resourceService.registerResourceProvider(mockResourceProvider2.object);
|
||||
|
||||
resourceService.areResourceProvidersLoaded = true;
|
||||
|
||||
appContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object);
|
||||
appContext.registerService<IAzureResourceCacheService>(AzureResourceServiceNames.cacheService, mockCacheService.object);
|
||||
appContext.registerService(AzureResourceServiceNames.resourceService, resourceService);
|
||||
|
||||
});
|
||||
|
||||
it('Should return resource containers.', async function(): Promise<void> {
|
||||
const subscriptionTreeNode = new AzureResourceSubscriptionTreeNode(mockAccount, mockSubscription, mockTenantId, mockAppContext, mockTreeChangeHandler.object, undefined);
|
||||
const subscriptionTreeNode = new AzureResourceSubscriptionTreeNode(mockAccount, mockSubscription, mockTenantId, appContext, mockTreeChangeHandler.object, undefined);
|
||||
const children = await subscriptionTreeNode.getChildren();
|
||||
|
||||
mockResourceTreeDataProvider1.verify((o) => o.getChildren(), TypeMoq.Times.once());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1
extensions/big-data-cluster/.gitignore
vendored
Normal file
1
extensions/big-data-cluster/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.vsix
|
||||
2
extensions/big-data-cluster/.vscodeignore
Normal file
2
extensions/big-data-cluster/.vscodeignore
Normal file
@@ -0,0 +1,2 @@
|
||||
src/**
|
||||
tsconfig.json
|
||||
1
extensions/cms/.gitignore
vendored
Normal file
1
extensions/cms/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.vsix
|
||||
@@ -0,0 +1,3 @@
|
||||
out/test/**
|
||||
src/**
|
||||
tsconfig.json
|
||||
|
||||
28
extensions/cms/README.md
Normal file
28
extensions/cms/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Microsoft Central Management Servers for Azure Data Studio
|
||||
|
||||
## Central Management Servers *(preview)*
|
||||
The Central Management Servers extension allows users to store a list of instances of SQL Server that is organized into one or more groups. Actions that are taken using a CMS group act on all servers in the server group.
|
||||
|
||||
This experience is currently in its initial preview. Please report issues and feature requests [here.](https://github.com/microsoft/azuredatastudio/issues)
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/30873802/57338070-eba5af00-70e0-11e9-911c-ddaa166b59b8.png" width="800px" />
|
||||
|
||||
### How do I start Central Management Servers?
|
||||
Central Management Servers can be viewed by clicking on the Connections icon (Ctrl/Cmd + G). The first time you download the extension, the CMS view will be minimized, and you can open it by click on **Central Management Servers**
|
||||
|
||||
### Where can I learn more about Central Management Servers
|
||||
To learn more conceptually about Central Management Servers, [you can read more here.](https://docs.microsoft.com/sql/ssms/register-servers/create-a-central-management-server-and-server-group)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement) describes the privacy statement of this software.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt).
|
||||
BIN
extensions/cms/images/sqlserver.png
Normal file
BIN
extensions/cms/images/sqlserver.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
@@ -2,12 +2,14 @@
|
||||
"name": "cms",
|
||||
"displayName": "%cms.displayName%",
|
||||
"description": "%cms.description%",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
|
||||
"icon": "images/sqlserver.png",
|
||||
"engines": {
|
||||
"vscode": "^1.25.0",
|
||||
"sqlops": "*"
|
||||
"azdata": ">=1.8.0"
|
||||
},
|
||||
"activationEvents": [
|
||||
"*"
|
||||
@@ -22,8 +24,8 @@
|
||||
"type": "object",
|
||||
"title": "%cms.title%",
|
||||
"properties": {
|
||||
"cms.cmsServers": {
|
||||
"type": "object"
|
||||
"centralManagementServers.servers": {
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -84,17 +86,9 @@
|
||||
"defaultValue": null,
|
||||
"objectType": null,
|
||||
"categoryValues": [
|
||||
{
|
||||
"displayName": "%cms.connectionOptions.authType.categoryValues.sqlLogin%",
|
||||
"name": "SqlLogin"
|
||||
},
|
||||
{
|
||||
"displayName": "%cms.connectionOptions.authType.categoryValues.integrated%",
|
||||
"name": "Integrated"
|
||||
},
|
||||
{
|
||||
"displayName": "%cms.connectionOptions.authType.categoryValues.azureMFA%",
|
||||
"name": "AzureMFA"
|
||||
}
|
||||
],
|
||||
"isRequired": true,
|
||||
@@ -516,7 +510,7 @@
|
||||
{
|
||||
"command": "cms.resource.deleteRegisteredServer",
|
||||
"title": "%cms.resource.deleteRegisteredServer.title%",
|
||||
"when": "viewItem == cms.resource.itemType.registeredServer"
|
||||
"when": "viewItem == cms.resource.itemType.databaseServer"
|
||||
},
|
||||
{
|
||||
"command": "cms.resource.addRegisteredServer",
|
||||
@@ -534,18 +528,8 @@
|
||||
"when": "viewItem == cms.resource.itemType.serverGroup"
|
||||
},
|
||||
{
|
||||
"command": "cms.resource.refreshServerGroup",
|
||||
"title": "%cms.resource.refreshServerGroup.title%",
|
||||
"when": "viewItem == cms.resource.itemType.serverGroup",
|
||||
"icon": {
|
||||
"light": "resources/light/refresh.svg",
|
||||
"dark": "resources/dark/refresh_inverse.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "cms.resource.refreshCmsServer",
|
||||
"title": "%cms.resource.refreshCmsServer.title%",
|
||||
"when": "viewItem == cms.resource.itemType.cmsNodeContainer",
|
||||
"command": "cms.resource.refresh",
|
||||
"title": "%cms.resource.refresh.title%",
|
||||
"icon": {
|
||||
"light": "resources/light/refresh.svg",
|
||||
"dark": "resources/dark/refresh_inverse.svg"
|
||||
@@ -583,7 +567,7 @@
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "cms.resource.deleteRegisteredServer",
|
||||
"when": "viewItem == cms.resource.itemType.registeredServer",
|
||||
"when": "viewItem == cms.resource.itemType.databaseServer",
|
||||
"group": "navigation@2"
|
||||
},
|
||||
{
|
||||
@@ -598,7 +582,7 @@
|
||||
},
|
||||
{
|
||||
"command": "cms.resource.addRegisteredServer",
|
||||
"when": "viewItem == cms.resource.itemType.cmsNodeContainer",
|
||||
"when": "viewItem == cms.resource.itemType.databaseServerContainer",
|
||||
"group": "navigation@10"
|
||||
},
|
||||
{
|
||||
@@ -608,22 +592,22 @@
|
||||
},
|
||||
{
|
||||
"command": "cms.resource.addServerGroup",
|
||||
"when": "viewItem == cms.resource.itemType.cmsNodeContainer",
|
||||
"when": "viewItem == cms.resource.itemType.databaseServerContainer",
|
||||
"group": "navigation@10"
|
||||
},
|
||||
{
|
||||
"command": "cms.resource.refreshServerGroup",
|
||||
"command": "cms.resource.refresh",
|
||||
"when": "viewItem == cms.resource.itemType.serverGroup",
|
||||
"group": "navigation@10"
|
||||
},
|
||||
{
|
||||
"command": "cms.resource.refreshCmsServer",
|
||||
"when": "viewItem == cms.resource.itemType.cmsNodeContainer",
|
||||
"command": "cms.resource.refresh",
|
||||
"when": "viewItem == cms.resource.itemType.databaseServerContainer",
|
||||
"group": "navigation@10"
|
||||
},
|
||||
{
|
||||
"command": "cms.resource.deleteCmsServer",
|
||||
"when": "viewItem == cms.resource.itemType.cmsNodeContainer",
|
||||
"when": "viewItem == cms.resource.itemType.databaseServerContainer",
|
||||
"group": "navigation@10"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"cms.displayName": "CMS",
|
||||
"cms.description": "Central Management Servers",
|
||||
"cms.displayName": "SQL Server Central Management Servers",
|
||||
"cms.description": "Support for managing SQL Server Central Management Servers",
|
||||
"cms.title": "Central Management Servers",
|
||||
"cms.connectionProvider.displayName": "Microsoft SQL Server - CMS",
|
||||
"cms.connectionProvider.displayName": "Microsoft SQL Server",
|
||||
"cms.resource.explorer.title": "Central Management Servers",
|
||||
"cms.resource.refresh.title": "Refresh",
|
||||
"cms.resource.refreshServerGroup.title": "Refresh Server Group",
|
||||
"cms.resource.refreshCmsServer.title": "Refresh Central Management Server",
|
||||
"cms.resource.deleteRegisteredServer.title": "Delete Registered Server",
|
||||
"cms.resource.addRegisteredServer.title": "Add Registered Server",
|
||||
"cms.resource.deleteServerGroup.title": "Delete Server Group",
|
||||
"cms.resource.addServerGroup.title": "Add Server Group",
|
||||
"cms.resource.deleteRegisteredServer.title": "Delete",
|
||||
"cms.resource.addRegisteredServer.title": "New Server Registration...",
|
||||
"cms.resource.deleteServerGroup.title": "Delete",
|
||||
"cms.resource.addServerGroup.title": "New Server Group...",
|
||||
"cms.resource.registerCmsServer.title": "Add Central Management Server",
|
||||
"cms.resource.deleteCmsServer.title": "Delete Central Management Server",
|
||||
"cms.resource.deleteCmsServer.title": "Delete",
|
||||
|
||||
"cms.configuration.title": "MSSQL configuration",
|
||||
"cms.query.displayBitAsNumber": "Should BIT columns be displayed as numbers (1 or 0)? If false, BIT columns will be displayed as 'true' or 'false'",
|
||||
|
||||
1
extensions/cms/resources/dark/folder_inverse.svg
Normal file
1
extensions/cms/resources/dark/folder_inverse.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#231f20;}.cls-2{fill:#fff;}</style></defs><title>folder_inverse_16x16</title><polygon class="cls-1" points="13.59 2.34 13.58 2.35 13.58 2.33 13.59 2.34"/><text></text><path class="cls-2" d="M16,14.13H0v-12a1,1,0,0,1,.08-.39,1,1,0,0,1,.53-.53A1,1,0,0,1,1,1.13H4.75a2.16,2.16,0,0,1,.61.07,2.26,2.26,0,0,1,.45.18,2.14,2.14,0,0,1,.36.24l.32.24a1.8,1.8,0,0,0,.34.18,1.12,1.12,0,0,0,.43.07H15a1,1,0,0,1,.39.08,1,1,0,0,1,.53.53,1,1,0,0,1,.08.39ZM1,2.13v1H4.75a1.36,1.36,0,0,0,.33,0A1,1,0,0,0,5.34,3l.23-.16.25-.21-.25-.21-.23-.16a1,1,0,0,0-.26-.1,1.36,1.36,0,0,0-.33,0Zm14,11v-10H7.25a1.12,1.12,0,0,0-.43.07,1.8,1.8,0,0,0-.34.18l-.32.24a2.14,2.14,0,0,1-.36.24,2.26,2.26,0,0,1-.45.18,2.16,2.16,0,0,1-.61.07H1v9Z"/></svg>
|
||||
|
After Width: | Height: | Size: 830 B |
@@ -4,14 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
import * as mssql from '../../mssql/src/api/mssqlapis';
|
||||
import * as Utils from './cmsResource/utils';
|
||||
import { ICmsResourceNodeInfo } from './cmsResource/tree/baseTreeNodes';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
/**
|
||||
* Wrapper class to act as a facade over VSCode and Data APIs and allow us to test / mock callbacks into
|
||||
@@ -22,9 +16,6 @@ const localize = nls.loadMessageBundle();
|
||||
*/
|
||||
export class ApiWrapper {
|
||||
|
||||
private _cmsService: mssql.CmsService;
|
||||
private _registeredCmsServers: ICmsResourceNodeInfo[];
|
||||
|
||||
// Data APIs
|
||||
public registerConnectionProvider(provider: azdata.ConnectionProvider): vscode.Disposable {
|
||||
return azdata.dataprotocol.registerConnectionProvider(provider);
|
||||
@@ -102,11 +93,11 @@ export class ApiWrapper {
|
||||
* @param resource The optional URI, as a URI object or a string, to use to get resource-scoped configurations
|
||||
*/
|
||||
public getConfiguration(): vscode.WorkspaceConfiguration {
|
||||
return vscode.workspace.getConfiguration('cms');
|
||||
return vscode.workspace.getConfiguration('centralManagementServers');
|
||||
}
|
||||
|
||||
public async setConfiguration(value: any): Promise<void> {
|
||||
await vscode.workspace.getConfiguration('cms').update('cmsServers', value, true);
|
||||
await vscode.workspace.getConfiguration('centralManagementServers').update('servers', value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +147,7 @@ export class ApiWrapper {
|
||||
}
|
||||
|
||||
public showWarningMessage(message: string, ...items: string[]): Thenable<string | undefined> {
|
||||
return vscode.window.showWarningMessage(message, ...items);
|
||||
return vscode.window.showWarningMessage(message, { modal: true }, ...items);
|
||||
}
|
||||
|
||||
public showInformationMessage(message: string, ...items: string[]): Thenable<string | undefined> {
|
||||
@@ -178,153 +169,4 @@ export class ApiWrapper {
|
||||
public registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable {
|
||||
return vscode.languages.registerCompletionItemProvider(selector, provider, ...triggerCharacters);
|
||||
}
|
||||
|
||||
// Connection APIs
|
||||
public openConnectionDialog(providers: string[], initialConnectionProfile?: azdata.IConnectionProfile, connectionCompletionOptions?: azdata.IConnectionCompletionOptions): Thenable<azdata.connection.Connection> {
|
||||
return azdata.connection.openConnectionDialog(providers, initialConnectionProfile, connectionCompletionOptions);
|
||||
}
|
||||
|
||||
// CMS APIs
|
||||
public async getCmsService(): Promise<mssql.CmsService> {
|
||||
if (!this._cmsService) {
|
||||
let extensionApi: mssql.MssqlExtensionApi = vscode.extensions.getExtension('Microsoft.mssql').exports;
|
||||
this._cmsService = await extensionApi.getCmsServiceProvider();
|
||||
}
|
||||
return this._cmsService;
|
||||
}
|
||||
|
||||
public async getRegisteredServers(ownerUri: string, relativePath: string): Promise<mssql.ListRegisteredServersResult> {
|
||||
return this.getCmsService().then((service) => {
|
||||
return service.getRegisteredServers(ownerUri, relativePath).then((result) => {
|
||||
if (result && result.registeredServersList && result.registeredServersList) {
|
||||
return result;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async createCmsServer(connection: azdata.connection.Connection,
|
||||
name: string, description: string): Promise<mssql.ListRegisteredServersResult> {
|
||||
let provider = await this.getCmsService();
|
||||
connection.providerName = connection.providerName === 'MSSQL-CMS' ? 'MSSQL' : connection.providerName;
|
||||
let ownerUri = await azdata.connection.getUriForConnection(connection.connectionId);
|
||||
if (!ownerUri) {
|
||||
// Make a connection if it's not already connected
|
||||
await azdata.connection.connect(Utils.toConnectionProfile(connection), false, false).then(async (result) => {
|
||||
ownerUri = await azdata.connection.getUriForConnection(result.connectionId);
|
||||
});
|
||||
}
|
||||
return provider.createCmsServer(name, description, connection, ownerUri).then((result) => {
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async deleteCmsServer(cmsServer: any) {
|
||||
let config = this.getConfiguration();
|
||||
if (config && config.cmsServers) {
|
||||
let newServers = config.cmsServers.filter((cachedServer) => {
|
||||
return cachedServer.name !== cmsServer;
|
||||
});
|
||||
await this.setConfiguration(newServers);
|
||||
this._registeredCmsServers = this._registeredCmsServers.filter((cachedServer) => {
|
||||
return cachedServer.name !== cmsServer;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public cacheRegisteredCmsServer(name: string, description: string, ownerUri: string, connection: azdata.connection.Connection): void {
|
||||
if (!this._registeredCmsServers) {
|
||||
this._registeredCmsServers = [];
|
||||
}
|
||||
let cmsServerNode: ICmsResourceNodeInfo = {
|
||||
name: name,
|
||||
description: description,
|
||||
ownerUri: ownerUri,
|
||||
connection: connection
|
||||
};
|
||||
this._registeredCmsServers.push(cmsServerNode);
|
||||
}
|
||||
|
||||
public async addRegisteredServer(relativePath: string, ownerUri: string,
|
||||
parentServerName?: string): Promise<any> {
|
||||
let provider = await this.getCmsService();
|
||||
// Initial profile to disallow SQL Login without
|
||||
// changing provider.
|
||||
let initialProfile: azdata.IConnectionProfile = {
|
||||
connectionName: undefined,
|
||||
serverName: undefined,
|
||||
databaseName: undefined,
|
||||
userName: undefined,
|
||||
password: undefined,
|
||||
authenticationType: undefined,
|
||||
savePassword: undefined,
|
||||
groupFullName: undefined,
|
||||
groupId: undefined,
|
||||
providerName: undefined,
|
||||
saveProfile: undefined,
|
||||
id: undefined,
|
||||
options: {
|
||||
authTypeChanged: true
|
||||
}
|
||||
};
|
||||
return this.openConnectionDialog(['MSSQL-CMS'], initialProfile, undefined).then((connection) => {
|
||||
if (connection && connection.options) {
|
||||
if (connection.options.server === parentServerName) {
|
||||
// error out for same server registration
|
||||
let errorText = localize('cms.errors.sameServerUnderCms', 'You cannot add a shared registered server with the same name as the Configuration Server');
|
||||
this.showErrorMessage(errorText);
|
||||
return;
|
||||
} else {
|
||||
return provider.addRegisteredServer(ownerUri, relativePath,
|
||||
connection.options.registeredServerName, connection.options.registeredServerDescription, connection).then((result) => {
|
||||
if (result) {
|
||||
return connection.options.server;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async removeRegisteredServer(registeredServerName: string, relativePath: string, ownerUri: string): Promise<boolean> {
|
||||
let provider = await this.getCmsService();
|
||||
return provider.removeRegisteredServer(ownerUri, relativePath, registeredServerName).then((result) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public async addServerGroup(groupName: string, groupDescription: string, relativePath: string, ownerUri: string): Promise<boolean> {
|
||||
let provider = await this.getCmsService();
|
||||
return provider.addServerGroup(ownerUri, relativePath, groupName, groupDescription).then((result) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public async removeServerGroup(groupName: string, relativePath: string, ownerUri: string): Promise<boolean> {
|
||||
let provider = await this.getCmsService();
|
||||
return provider.removeServerGroup(ownerUri, relativePath, groupName).then((result) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
public get registeredCmsServers(): ICmsResourceNodeInfo[] {
|
||||
return this._registeredCmsServers;
|
||||
}
|
||||
|
||||
public get connection(): Thenable<azdata.connection.Connection> {
|
||||
return this.openConnectionDialog(['MSSQL-CMS'], undefined, undefined).then((connection) => {
|
||||
if (connection) {
|
||||
// remove group ID from connection if a user chose connection
|
||||
// from the recent connections list
|
||||
connection.options['groupId'] = null;
|
||||
connection.providerName = 'MSSQL';
|
||||
return connection;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,19 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { ApiWrapper } from './apiWrapper';
|
||||
import { CmsUtils } from './cmsUtils';
|
||||
|
||||
/**
|
||||
* Global context for the application
|
||||
*/
|
||||
export class AppContext {
|
||||
|
||||
constructor(public readonly extensionContext: vscode.ExtensionContext, public readonly apiWrapper: ApiWrapper) {
|
||||
constructor(
|
||||
public readonly extensionContext: vscode.ExtensionContext,
|
||||
public readonly apiWrapper: ApiWrapper,
|
||||
public readonly cmsUtils: CmsUtils
|
||||
) {
|
||||
this.apiWrapper = apiWrapper || new ApiWrapper();
|
||||
this.cmsUtils = cmsUtils || new CmsUtils();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,12 @@ export function registerCmsServerCommand(appContext: AppContext, tree: CmsResour
|
||||
if (node && !(node instanceof CmsResourceEmptyTreeNode)) {
|
||||
return;
|
||||
}
|
||||
await appContext.apiWrapper.connection.then(async (connection) => {
|
||||
await appContext.cmsUtils.connection.then(async (connection) => {
|
||||
if (connection && connection.options) {
|
||||
let registeredCmsServerName = connection.options.registeredServerName ?
|
||||
connection.options.registeredServerName : connection.options.server;
|
||||
// check if a CMS with the same name is registered or not
|
||||
let cachedServers = appContext.apiWrapper.registeredCmsServers;
|
||||
let cachedServers = appContext.cmsUtils.registeredCmsServers;
|
||||
let serverExists: boolean = false;
|
||||
if (cachedServers) {
|
||||
serverExists = cachedServers.some((server) => {
|
||||
@@ -42,7 +42,7 @@ export function registerCmsServerCommand(appContext: AppContext, tree: CmsResour
|
||||
// remove server description from connection uri
|
||||
connection.options.registeredCmsServerDescription = null;
|
||||
let ownerUri = await azdata.connection.getUriForConnection(connection.connectionId);
|
||||
appContext.apiWrapper.cacheRegisteredCmsServer(registeredCmsServerName, registeredCmsServerDescription, ownerUri, connection);
|
||||
appContext.cmsUtils.cacheRegisteredCmsServer(registeredCmsServerName, registeredCmsServerDescription, ownerUri, connection);
|
||||
tree.notifyNodeChanged(undefined);
|
||||
} else {
|
||||
// error out for same server name
|
||||
@@ -61,7 +61,7 @@ export function deleteCmsServerCommand(appContext: AppContext, tree: CmsResource
|
||||
if (!(node instanceof CmsResourceTreeNode)) {
|
||||
return;
|
||||
}
|
||||
await appContext.apiWrapper.deleteCmsServer(node.name);
|
||||
await appContext.cmsUtils.deleteCmsServer(node.name);
|
||||
tree.isSystemInitialized = false;
|
||||
tree.notifyNodeChanged(undefined);
|
||||
});
|
||||
@@ -74,11 +74,17 @@ export function addRegisteredServerCommand(appContext: AppContext, tree: CmsReso
|
||||
return;
|
||||
}
|
||||
let relativePath = node instanceof CmsResourceTreeNode ? '' : node.relativePath;
|
||||
let serverName = node instanceof CmsResourceTreeNode ? node.connection.options.server : null;
|
||||
await appContext.apiWrapper.addRegisteredServer(relativePath, node.ownerUri, serverName).then((result) => {
|
||||
let serverName = node instanceof CmsResourceTreeNode ? node.connection.options.registeredServerName === ''
|
||||
? node.connection.options.server : node.connection.options.registeredServerName : null;
|
||||
await appContext.cmsUtils.addRegisteredServer(relativePath, node.ownerUri, serverName).then((result) => {
|
||||
if (result) {
|
||||
tree.notifyNodeChanged(undefined);
|
||||
tree.notifyNodeChanged(node);
|
||||
}
|
||||
}, (error) => {
|
||||
// error out
|
||||
let errorText = localize('cms.errors.addRegisterServerFail', 'Could not add the Registered Server {0}', error);
|
||||
appContext.apiWrapper.showErrorMessage(errorText);
|
||||
return;
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -89,11 +95,18 @@ export function deleteRegisteredServerCommand(appContext: AppContext, tree: CmsR
|
||||
if (!(node instanceof RegisteredServerTreeNode)) {
|
||||
return;
|
||||
}
|
||||
appContext.apiWrapper.removeRegisteredServer(node.name, node.relativePath, node.ownerUri).then((result) => {
|
||||
if (result) {
|
||||
tree.notifyNodeChanged(undefined);
|
||||
}
|
||||
});
|
||||
appContext.apiWrapper.showWarningMessage(
|
||||
`${localize('cms.confirmDeleteServer', 'Are you sure you want to delete')} ${node.name}?`,
|
||||
localize('cms.yes', 'Yes'),
|
||||
localize('cms.no', 'No')).then((result) => {
|
||||
if (result && result === localize('cms.yes', 'Yes')) {
|
||||
appContext.cmsUtils.removeRegisteredServer(node.name, node.relativePath, node.ownerUri).then((result) => {
|
||||
if (result) {
|
||||
tree.notifyNodeChanged(node.parent);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -144,9 +157,9 @@ export function addServerGroupCommand(appContext: AppContext, tree: CmsResourceT
|
||||
groupExists = true;
|
||||
}
|
||||
if (!groupExists) {
|
||||
appContext.apiWrapper.addServerGroup(serverGroupName, serverDescription, path, node.ownerUri).then((result) => {
|
||||
appContext.cmsUtils.addServerGroup(serverGroupName, serverDescription, path, node.ownerUri).then((result) => {
|
||||
if (result) {
|
||||
tree.notifyNodeChanged(undefined);
|
||||
tree.notifyNodeChanged(node);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -165,11 +178,18 @@ export function deleteServerGroupCommand(appContext: AppContext, tree: CmsResour
|
||||
if (!(node instanceof ServerGroupTreeNode)) {
|
||||
return;
|
||||
}
|
||||
appContext.apiWrapper.removeServerGroup(node.name, node.relativePath, node.ownerUri).then((result) => {
|
||||
if (result) {
|
||||
tree.notifyNodeChanged(undefined);
|
||||
}
|
||||
});
|
||||
appContext.apiWrapper.showWarningMessage(
|
||||
`${localize('cms.confirmDeleteGroup', 'Are you sure you want to delete')} ${node.name}?`,
|
||||
localize('cms.yes', 'Yes'),
|
||||
localize('cms.no', 'No')).then((result) => {
|
||||
if (result && result === localize('cms.yes', 'Yes')) {
|
||||
appContext.cmsUtils.removeServerGroup(node.name, node.relativePath, node.ownerUri).then((result) => {
|
||||
if (result) {
|
||||
tree.notifyNodeChanged(node.parent);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -179,7 +199,7 @@ export function refreshCommand(appContext: AppContext, tree: CmsResourceTreeProv
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
tree.notifyNodeChanged(undefined);
|
||||
tree.notifyNodeChanged(node);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
export enum CmsResourceItemType {
|
||||
cmsMessageNodeContainer = 'cms.resource.itemType.cmsMessageNodeContainer',
|
||||
cmsEmptyNodeContainer = 'cms.resource.itemType.cmsEmptyNodeContainer',
|
||||
cmsNodeContainer = 'cms.resource.itemType.cmsNodeContainer',
|
||||
registeredServer = 'cms.resource.itemType.registeredServer',
|
||||
cmsNodeContainer = 'cms.resource.itemType.databaseServerContainer',
|
||||
registeredServer = 'cms.resource.itemType.databaseServer',
|
||||
serverGroup = 'cms.resource.itemType.serverGroup'
|
||||
}
|
||||
@@ -8,19 +8,23 @@ import * as azdata from 'azdata';
|
||||
import { AppContext } from '../../appContext';
|
||||
import { TreeNode } from '../treeNode';
|
||||
import { ICmsResourceTreeChangeHandler } from './treeChangeHandler';
|
||||
import { generateGuid } from '../utils';
|
||||
|
||||
export abstract class CmsResourceTreeNodeBase extends TreeNode {
|
||||
|
||||
protected _id: string = undefined;
|
||||
|
||||
public constructor(
|
||||
private _name: string,
|
||||
private _description: string,
|
||||
private _ownerUri: string,
|
||||
protected _ownerUri: string,
|
||||
public readonly appContext: AppContext,
|
||||
public readonly treeChangeHandler: ICmsResourceTreeChangeHandler,
|
||||
parent: TreeNode
|
||||
) {
|
||||
super();
|
||||
this.parent = parent;
|
||||
this._id = generateGuid();
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
|
||||
@@ -20,7 +20,6 @@ const localize = nls.loadMessageBundle();
|
||||
|
||||
export class CmsResourceTreeNode extends CmsResourceTreeNodeBase {
|
||||
|
||||
private _id: string = undefined;
|
||||
private _serverGroupNodes: ServerGroupTreeNode[] = [];
|
||||
|
||||
public constructor(
|
||||
@@ -33,13 +32,15 @@ export class CmsResourceTreeNode extends CmsResourceTreeNodeBase {
|
||||
parent: TreeNode
|
||||
) {
|
||||
super(name, description, ownerUri, appContext, treeChangeHandler, parent);
|
||||
this._id = `cms_cmsServer_${this.name}`;
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
try {
|
||||
let nodes: TreeNode[] = [];
|
||||
return this.appContext.apiWrapper.createCmsServer(this.connection, this.name, this.description).then(async (result) => {
|
||||
let nodes: CmsResourceTreeNodeBase[] = [];
|
||||
if (!this.ownerUri) {
|
||||
this._ownerUri = await this.appContext.cmsUtils.getUriForConnection(this.connection);
|
||||
}
|
||||
return this.appContext.cmsUtils.createCmsServer(this.connection, this.name, this.description).then((result) => {
|
||||
if (result) {
|
||||
if (result.registeredServersList) {
|
||||
result.registeredServersList.forEach((registeredServer) => {
|
||||
@@ -70,12 +71,15 @@ export class CmsResourceTreeNode extends CmsResourceTreeNodeBase {
|
||||
}
|
||||
}
|
||||
if (nodes.length > 0) {
|
||||
return nodes;
|
||||
return nodes.sort((node1, node2) => node1.name > node2.name ? 1 : -1);
|
||||
} else {
|
||||
return [CmsResourceMessageTreeNode.create(CmsResourceTreeNode.noResourcesLabel, undefined)];
|
||||
}
|
||||
|
||||
}
|
||||
}, (error) => {
|
||||
let errorText = localize('cms.errors.expandCmsFail', 'The Central Management Server {0} could not be found or is offline', this.name);
|
||||
this.appContext.apiWrapper.showErrorMessage(error ? error : errorText);
|
||||
return [];
|
||||
});
|
||||
} catch {
|
||||
return [];
|
||||
|
||||
@@ -11,12 +11,9 @@ import { CmsResourceItemType } from '../constants';
|
||||
import { CmsResourceTreeNodeBase } from './baseTreeNodes';
|
||||
import { AppContext } from '../../appContext';
|
||||
import { ICmsResourceTreeChangeHandler } from './treeChangeHandler';
|
||||
import { generateGuid } from '../utils';
|
||||
|
||||
export class RegisteredServerTreeNode extends CmsResourceTreeNodeBase {
|
||||
|
||||
private _id: string = undefined;
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
description: string,
|
||||
@@ -28,7 +25,6 @@ export class RegisteredServerTreeNode extends CmsResourceTreeNodeBase {
|
||||
parent: TreeNode
|
||||
) {
|
||||
super(name, description, ownerUri, appContext, treeChangeHandler, parent);
|
||||
this._id = `cms_registeredServer_${this.name ? this.name : this.serverName}`;
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
@@ -37,7 +33,7 @@ export class RegisteredServerTreeNode extends CmsResourceTreeNodeBase {
|
||||
|
||||
public getTreeItem(): azdata.TreeItem | Promise<azdata.TreeItem> {
|
||||
let payload = {
|
||||
id: generateGuid(),
|
||||
id: this._id,
|
||||
connectionName: this.name ? this.name : this.serverName,
|
||||
serverName: this.serverName,
|
||||
databaseName: '',
|
||||
|
||||
@@ -18,7 +18,6 @@ import { CmsResourceTreeNode } from './cmsResourceTreeNode';
|
||||
|
||||
export class ServerGroupTreeNode extends CmsResourceTreeNodeBase {
|
||||
|
||||
private _id: string = undefined;
|
||||
private _serverGroupNodes: ServerGroupTreeNode[] = [];
|
||||
|
||||
constructor(
|
||||
@@ -31,12 +30,11 @@ export class ServerGroupTreeNode extends CmsResourceTreeNodeBase {
|
||||
parent: TreeNode
|
||||
) {
|
||||
super(name, description, ownerUri, appContext, treeChangeHandler, parent);
|
||||
this._id = `cms_serverGroup_${this.name}`;
|
||||
}
|
||||
public getChildren(): TreeNode[] | Promise<TreeNode[]> {
|
||||
try {
|
||||
let nodes = [];
|
||||
return this.appContext.apiWrapper.getRegisteredServers(this.ownerUri, this.relativePath).then((result) => {
|
||||
return this.appContext.cmsUtils.getRegisteredServers(this.ownerUri, this.relativePath).then((result) => {
|
||||
if (result) {
|
||||
if (result.registeredServersList) {
|
||||
result.registeredServersList.forEach((registeredServer) => {
|
||||
@@ -86,7 +84,7 @@ export class ServerGroupTreeNode extends CmsResourceTreeNodeBase {
|
||||
item.id = this._id;
|
||||
item.tooltip = this.description;
|
||||
item.iconPath = {
|
||||
dark: this.appContext.extensionContext.asAbsolutePath('resources/light/folder.svg'),
|
||||
dark: this.appContext.extensionContext.asAbsolutePath('resources/dark/folder_inverse.svg'),
|
||||
light: this.appContext.extensionContext.asAbsolutePath('resources/light/folder.svg')
|
||||
};
|
||||
return item;
|
||||
|
||||
@@ -34,19 +34,19 @@ export class CmsResourceTreeProvider implements TreeDataProvider<TreeNode>, ICms
|
||||
try {
|
||||
// Call to collect all locally saved CMS servers
|
||||
// to determine whether the system has been initialized.
|
||||
let cmsConfig = this._appContext.apiWrapper.getConfiguration();
|
||||
let cachedServers = cmsConfig ? cmsConfig.cmsServers : [];
|
||||
let cmsConfig = this._appContext.cmsUtils.getConfiguration();
|
||||
let cachedServers = cmsConfig.servers ? cmsConfig.servers : [];
|
||||
if (cachedServers && cachedServers.length > 0) {
|
||||
let servers = [];
|
||||
cachedServers.forEach((server) => {
|
||||
cachedServers.forEach(async (server) => {
|
||||
servers.push(new CmsResourceTreeNode(
|
||||
server.name,
|
||||
server.description,
|
||||
server.ownerUri,
|
||||
undefined,
|
||||
server.connection,
|
||||
this._appContext, this, null));
|
||||
this.appContext.apiWrapper.cacheRegisteredCmsServer(server.name, server.description,
|
||||
server.ownerUri, server.connection);
|
||||
this.appContext.cmsUtils.cacheRegisteredCmsServer(server.name, server.description,
|
||||
undefined, server.connection);
|
||||
});
|
||||
return servers;
|
||||
}
|
||||
@@ -59,11 +59,16 @@ export class CmsResourceTreeProvider implements TreeDataProvider<TreeNode>, ICms
|
||||
return [CmsResourceMessageTreeNode.create(CmsResourceTreeProvider.loadingLabel, undefined)];
|
||||
}
|
||||
try {
|
||||
let registeredCmsServers = this.appContext.apiWrapper.registeredCmsServers;
|
||||
let registeredCmsServers = this.appContext.cmsUtils.registeredCmsServers;
|
||||
if (registeredCmsServers && registeredCmsServers.length > 0) {
|
||||
this.isSystemInitialized = true;
|
||||
// save the CMS Servers for future use
|
||||
await this._appContext.apiWrapper.setConfiguration(registeredCmsServers);
|
||||
let toSaveCmsServers = JSON.parse(JSON.stringify(registeredCmsServers));
|
||||
toSaveCmsServers.forEach(server => {
|
||||
server.ownerUri = undefined,
|
||||
server.connection.options.password = '';
|
||||
});
|
||||
await this._appContext.cmsUtils.setConfiguration(toSaveCmsServers);
|
||||
return registeredCmsServers.map((server) => {
|
||||
return new CmsResourceTreeNode(
|
||||
server.name,
|
||||
@@ -71,10 +76,9 @@ export class CmsResourceTreeProvider implements TreeDataProvider<TreeNode>, ICms
|
||||
server.ownerUri,
|
||||
server.connection,
|
||||
this._appContext, this, null);
|
||||
});
|
||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||
} else {
|
||||
return [new CmsResourceEmptyTreeNode()];
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
return [new CmsResourceEmptyTreeNode()];
|
||||
|
||||
205
extensions/cms/src/cmsUtils.ts
Normal file
205
extensions/cms/src/cmsUtils.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
import * as mssql from '../../mssql/src/api/mssqlapis';
|
||||
import * as Utils from './cmsResource/utils';
|
||||
import { ICmsResourceNodeInfo } from './cmsResource/tree/baseTreeNodes';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
const cmsProvider: string = 'MSSQL-CMS';
|
||||
const mssqlProvider: string = 'MSSQL';
|
||||
|
||||
/**
|
||||
* 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
|
||||
* ApiWrapper
|
||||
*/
|
||||
export class CmsUtils {
|
||||
|
||||
private _cmsService: mssql.CmsService;
|
||||
private _registeredCmsServers: ICmsResourceNodeInfo[];
|
||||
|
||||
public showErrorMessage(message: string, ...items: string[]): Thenable<string | undefined> {
|
||||
return vscode.window.showErrorMessage(message, ...items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration for a extensionName
|
||||
* @param extensionName The string name of the extension to get the configuration for
|
||||
* @param resource The optional URI, as a URI object or a string, to use to get resource-scoped configurations
|
||||
*/
|
||||
public getConfiguration(): vscode.WorkspaceConfiguration {
|
||||
return vscode.workspace.getConfiguration('centralManagementServers');
|
||||
}
|
||||
|
||||
public async setConfiguration(value: any): Promise<void> {
|
||||
await vscode.workspace.getConfiguration('centralManagementServers').update('servers', value, true);
|
||||
}
|
||||
|
||||
// Connection APIs
|
||||
public openConnectionDialog(providers: string[], initialConnectionProfile?: azdata.IConnectionProfile, connectionCompletionOptions?: azdata.IConnectionCompletionOptions): Thenable<azdata.connection.Connection> {
|
||||
return azdata.connection.openConnectionDialog(providers, initialConnectionProfile, connectionCompletionOptions);
|
||||
}
|
||||
|
||||
public async getUriForConnection(connection: azdata.connection.Connection): Promise<string> {
|
||||
let ownerUri = await azdata.connection.getUriForConnection(connection.connectionId);
|
||||
if (!ownerUri) {
|
||||
// Make a connection if it's not already connected
|
||||
await azdata.connection.connect(Utils.toConnectionProfile(connection), false, false).then(async (result) => {
|
||||
ownerUri = await azdata.connection.getUriForConnection(result.connectionId);
|
||||
});
|
||||
}
|
||||
return ownerUri;
|
||||
}
|
||||
|
||||
// CMS APIs
|
||||
public async getCmsService(): Promise<mssql.CmsService> {
|
||||
if (!this._cmsService) {
|
||||
let extensionApi: mssql.MssqlExtensionApi = vscode.extensions.getExtension('Microsoft.mssql').exports;
|
||||
this._cmsService = await extensionApi.getCmsServiceProvider();
|
||||
}
|
||||
return this._cmsService;
|
||||
}
|
||||
|
||||
public async getRegisteredServers(ownerUri: string, relativePath: string): Promise<mssql.ListRegisteredServersResult> {
|
||||
return this.getCmsService().then((service) => {
|
||||
return service.getRegisteredServers(ownerUri, relativePath).then((result) => {
|
||||
if (result && result.registeredServersList && result.registeredServersList) {
|
||||
return result;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async createCmsServer(connection: azdata.connection.Connection,
|
||||
name: string, description: string): Promise<mssql.ListRegisteredServersResult> {
|
||||
let provider = await this.getCmsService();
|
||||
connection.providerName = connection.providerName === cmsProvider ? mssqlProvider : connection.providerName;
|
||||
let ownerUri = await azdata.connection.getUriForConnection(connection.connectionId);
|
||||
if (!ownerUri) {
|
||||
// Make a connection if it's not already connected
|
||||
await azdata.connection.connect(Utils.toConnectionProfile(connection), false, false).then(async (result) => {
|
||||
ownerUri = await azdata.connection.getUriForConnection(result.connectionId);
|
||||
});
|
||||
}
|
||||
return provider.createCmsServer(name, description, connection, ownerUri).then((result) => {
|
||||
if (result) {
|
||||
return Promise.resolve(result);
|
||||
} else {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async deleteCmsServer(cmsServer: any): Promise<void> {
|
||||
let config = this.getConfiguration();
|
||||
if (config && config.servers) {
|
||||
let newServers = config.servers.filter((cachedServer) => {
|
||||
return cachedServer.name !== cmsServer;
|
||||
});
|
||||
await this.setConfiguration(newServers);
|
||||
this._registeredCmsServers = this._registeredCmsServers.filter((cachedServer) => {
|
||||
return cachedServer.name !== cmsServer;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public cacheRegisteredCmsServer(name: string, description: string, ownerUri: string, connection: azdata.connection.Connection): void {
|
||||
if (!this._registeredCmsServers) {
|
||||
this._registeredCmsServers = [];
|
||||
}
|
||||
let cmsServerNode: ICmsResourceNodeInfo = {
|
||||
name: name,
|
||||
description: description,
|
||||
connection: connection,
|
||||
ownerUri: ownerUri
|
||||
};
|
||||
this._registeredCmsServers.push(cmsServerNode);
|
||||
}
|
||||
|
||||
public async addRegisteredServer(relativePath: string, ownerUri: string,
|
||||
parentServerName?: string): Promise<boolean> {
|
||||
let provider = await this.getCmsService();
|
||||
// Initial profile to disallow SQL Login without
|
||||
// changing provider.
|
||||
let initialProfile: azdata.IConnectionProfile = {
|
||||
connectionName: undefined,
|
||||
serverName: undefined,
|
||||
databaseName: undefined,
|
||||
userName: undefined,
|
||||
password: undefined,
|
||||
authenticationType: undefined,
|
||||
savePassword: undefined,
|
||||
groupFullName: undefined,
|
||||
groupId: undefined,
|
||||
providerName: undefined,
|
||||
saveProfile: undefined,
|
||||
id: undefined,
|
||||
options: {}
|
||||
};
|
||||
return this.openConnectionDialog([cmsProvider], initialProfile, { saveConnection: false }).then(async (connection) => {
|
||||
if (connection && connection.options) {
|
||||
if (connection.options.server === parentServerName) {
|
||||
// error out for same server registration
|
||||
let errorText = localize('cms.errors.sameServerUnderCms', 'You cannot add a shared registered server with the same name as the Configuration Server');
|
||||
this.showErrorMessage(errorText);
|
||||
return false;
|
||||
} else {
|
||||
let registeredServerName = connection.options.registeredServerName === '' ? connection.options.server : connection.options.registeredServerName;
|
||||
let result = await provider.addRegisteredServer(ownerUri, relativePath, registeredServerName, connection.options.registeredServerDescription, connection);
|
||||
if (result) {
|
||||
return Promise.resolve(result);
|
||||
} else {
|
||||
return Promise.reject(registeredServerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async removeRegisteredServer(registeredServerName: string, relativePath: string, ownerUri: string): Promise<boolean> {
|
||||
let provider = await this.getCmsService();
|
||||
return provider.removeRegisteredServer(ownerUri, relativePath, registeredServerName).then((result) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public async addServerGroup(groupName: string, groupDescription: string, relativePath: string, ownerUri: string): Promise<boolean> {
|
||||
let provider = await this.getCmsService();
|
||||
return provider.addServerGroup(ownerUri, relativePath, groupName, groupDescription).then((result) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public async removeServerGroup(groupName: string, relativePath: string, ownerUri: string): Promise<boolean> {
|
||||
let provider = await this.getCmsService();
|
||||
return provider.removeServerGroup(ownerUri, relativePath, groupName).then((result) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// Getters
|
||||
public get registeredCmsServers(): ICmsResourceNodeInfo[] {
|
||||
return this._registeredCmsServers;
|
||||
}
|
||||
|
||||
public get connection(): Thenable<azdata.connection.Connection> {
|
||||
return this.openConnectionDialog([cmsProvider], undefined, { saveConnection: false }).then((connection) => {
|
||||
if (connection) {
|
||||
// remove group ID from connection if a user chose connection
|
||||
// from the recent connections list
|
||||
connection.options['groupId'] = null;
|
||||
connection.providerName = mssqlProvider;
|
||||
return connection;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import CmsResourceController from './controllers/cmsResourceController';
|
||||
import { AppContext } from './appContext';
|
||||
import ControllerBase from './controllers/controllerBase';
|
||||
import { ApiWrapper } from './apiWrapper';
|
||||
import { CmsUtils } from './cmsUtils';
|
||||
|
||||
let controllers: ControllerBase[] = [];
|
||||
|
||||
@@ -17,7 +18,8 @@ let controllers: ControllerBase[] = [];
|
||||
// your extension is activated the very first time the command is executed
|
||||
export function activate(extensionContext: vscode.ExtensionContext) {
|
||||
const apiWrapper = new ApiWrapper();
|
||||
let appContext = new AppContext(extensionContext, apiWrapper);
|
||||
const cmsUtils = new CmsUtils();
|
||||
let appContext = new AppContext(extensionContext, apiWrapper, cmsUtils);
|
||||
let activations: Thenable<boolean>[] = [];
|
||||
|
||||
const cmsResourceController = new CmsResourceController(appContext);
|
||||
|
||||
@@ -15,12 +15,14 @@ import { CmsResourceItemType } from '../../../cmsResource/constants';
|
||||
import { ServerGroupTreeNode } from '../../../cmsResource/tree/serverGroupTreeNode';
|
||||
import { ICmsResourceTreeChangeHandler } from '../../../cmsResource/tree/treeChangeHandler';
|
||||
import { cmsResource } from '../../../cmsResource/cms-resource';
|
||||
import { CmsUtils } from '../../../cmsUtils';
|
||||
|
||||
// Mock services
|
||||
let mockAppContext: AppContext;
|
||||
|
||||
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
||||
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||
let mockCmsUtils: TypeMoq.IMock<CmsUtils>;
|
||||
let mockTreeChangeHandler: TypeMoq.IMock<ICmsResourceTreeChangeHandler>;
|
||||
|
||||
let mockResourceTreeDataProvider1: TypeMoq.IMock<cmsResource.ICmsResourceTreeDataProvider>;
|
||||
@@ -30,7 +32,7 @@ describe('ServerGroupTreeNode.info', function(): void {
|
||||
beforeEach(() => {
|
||||
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||
mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object);
|
||||
mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object, mockCmsUtils.object);
|
||||
mockTreeChangeHandler = TypeMoq.Mock.ofType<ICmsResourceTreeChangeHandler>();
|
||||
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<cmsResource.ICmsResourceTreeDataProvider>();
|
||||
mockResourceTreeDataProvider1.setup((o) => o.getChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<cmsResource.ICmsResourceNode>().object]));
|
||||
|
||||
@@ -10,17 +10,18 @@ import * as vscode from 'vscode';
|
||||
import 'mocha';
|
||||
import { AppContext } from '../../../appContext';
|
||||
import { ApiWrapper } from '../../../apiWrapper';
|
||||
|
||||
import { CmsResourceItemType } from '../../../cmsResource/constants';
|
||||
import { RegisteredServerTreeNode } from '../../../cmsResource/tree/registeredServerTreeNode';
|
||||
import { ICmsResourceTreeChangeHandler } from '../../../cmsResource/tree/treeChangeHandler';
|
||||
import { cmsResource } from '../../../cmsResource/cms-resource';
|
||||
import { CmsUtils } from '../../../cmsUtils';
|
||||
|
||||
// Mock services
|
||||
let mockAppContext: AppContext;
|
||||
|
||||
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
||||
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||
let mockCmsUtils: TypeMoq.IMock<CmsUtils>;
|
||||
let mockTreeChangeHandler: TypeMoq.IMock<ICmsResourceTreeChangeHandler>;
|
||||
|
||||
let mockResourceTreeDataProvider1: TypeMoq.IMock<cmsResource.ICmsResourceTreeDataProvider>;
|
||||
@@ -30,7 +31,7 @@ describe('RegisteredServerTreeNode.info', function(): void {
|
||||
beforeEach(() => {
|
||||
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||
mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object);
|
||||
mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object, mockCmsUtils.object);
|
||||
mockTreeChangeHandler = TypeMoq.Mock.ofType<ICmsResourceTreeChangeHandler>();
|
||||
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<cmsResource.ICmsResourceTreeDataProvider>();
|
||||
mockResourceTreeDataProvider1.setup((o) => o.getChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<cmsResource.ICmsResourceNode>().object]));
|
||||
|
||||
@@ -10,17 +10,18 @@ import * as vscode from 'vscode';
|
||||
import 'mocha';
|
||||
import { AppContext } from '../../../appContext';
|
||||
import { ApiWrapper } from '../../../apiWrapper';
|
||||
|
||||
import { CmsResourceItemType } from '../../../cmsResource/constants';
|
||||
import { ServerGroupTreeNode } from '../../../cmsResource/tree/serverGroupTreeNode';
|
||||
import { ICmsResourceTreeChangeHandler } from '../../../cmsResource/tree/treeChangeHandler';
|
||||
import { cmsResource } from '../../../cmsResource/cms-resource';
|
||||
import { CmsUtils } from '../../../cmsUtils';
|
||||
|
||||
// Mock services
|
||||
let mockAppContext: AppContext;
|
||||
|
||||
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
||||
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||
let mockCmsUtils: TypeMoq.IMock<CmsUtils>;
|
||||
let mockTreeChangeHandler: TypeMoq.IMock<ICmsResourceTreeChangeHandler>;
|
||||
|
||||
let mockResourceTreeDataProvider1: TypeMoq.IMock<cmsResource.ICmsResourceTreeDataProvider>;
|
||||
@@ -30,7 +31,7 @@ describe('ServerGroupTreeNode.info', function(): void {
|
||||
beforeEach(() => {
|
||||
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||
mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object);
|
||||
mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object, mockCmsUtils.object);
|
||||
mockTreeChangeHandler = TypeMoq.Mock.ofType<ICmsResourceTreeChangeHandler>();
|
||||
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<cmsResource.ICmsResourceTreeDataProvider>();
|
||||
mockResourceTreeDataProvider1.setup((o) => o.getChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<cmsResource.ICmsResourceNode>().object]));
|
||||
|
||||
@@ -4,22 +4,22 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'mocha';
|
||||
import * as vscode from 'vscode';
|
||||
import * as should from 'should';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import 'mocha';
|
||||
import { AppContext } from '../../../appContext';
|
||||
import { ApiWrapper } from '../../../apiWrapper';
|
||||
|
||||
import { CmsResourceTreeProvider } from '../../../cmsResource/tree/treeProvider';
|
||||
import { CmsResourceMessageTreeNode } from '../../../cmsResource/messageTreeNode';
|
||||
import { CmsResourceEmptyTreeNode } from '../../../cmsResource/tree/cmsResourceEmptyTreeNode';
|
||||
import { CmsResourceTreeNode } from '../../../cmsResource/tree/cmsResourceTreeNode';
|
||||
import { CmsUtils } from '../../../cmsUtils';
|
||||
|
||||
// Mock services
|
||||
let mockAppContext: AppContext;
|
||||
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
||||
let mockCmsUtils: TypeMoq.IMock<CmsUtils>;
|
||||
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('CmsResourceTreeProvider.getChildren', function (): void {
|
||||
beforeEach(() => {
|
||||
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||
mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object);
|
||||
mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object, mockCmsUtils.object);
|
||||
});
|
||||
|
||||
it('Should not be initialized.', async function (): Promise<void> {
|
||||
@@ -39,19 +39,19 @@ describe('CmsResourceTreeProvider.getChildren', function (): void {
|
||||
should.equal(children[0] instanceof CmsResourceMessageTreeNode, true);
|
||||
});
|
||||
|
||||
it('Should not be loading after initialized.'), async function (): Promise<void> {
|
||||
it('Should not be loading after initialized.', async function (): Promise<void> {
|
||||
const treeProvider = new CmsResourceTreeProvider(mockAppContext);
|
||||
treeProvider.isSystemInitialized = true;
|
||||
should.equal(true, treeProvider.isSystemInitialized);
|
||||
mockApiWrapper.setup(x => x.registeredCmsServers).returns(null);
|
||||
mockCmsUtils.setup(x => x.registeredCmsServers).returns(null);
|
||||
const children = await treeProvider.getChildren(undefined);
|
||||
should.equal(children[0] instanceof CmsResourceEmptyTreeNode, true);
|
||||
};
|
||||
});
|
||||
|
||||
it('Should show CMS nodes if there are cached servers'), async function (): Promise<void> {
|
||||
it('Should show CMS nodes if there are cached servers', async function (): Promise<void> {
|
||||
const treeProvider = new CmsResourceTreeProvider(mockAppContext);
|
||||
treeProvider.isSystemInitialized = true;
|
||||
mockApiWrapper.setup(x => x.registeredCmsServers).returns(() => {
|
||||
mockCmsUtils.setup(x => x.registeredCmsServers).returns(() => {
|
||||
return [{
|
||||
name: 'name',
|
||||
description: 'description',
|
||||
@@ -61,5 +61,5 @@ describe('CmsResourceTreeProvider.getChildren', function (): void {
|
||||
});
|
||||
const children = await treeProvider.getChildren(undefined);
|
||||
should.equal(children[0] instanceof CmsResourceTreeNode, true);
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -96,6 +96,14 @@
|
||||
{
|
||||
"fileMatch": "/.vscode/extensions.json",
|
||||
"url": "vscode://schemas/extensions"
|
||||
},
|
||||
{
|
||||
"fileMatch": "/.devcontainer/devcontainer.json",
|
||||
"url": "./schemas/devContainer.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": "/.devcontainer.json",
|
||||
"url": "./schemas/devContainer.schema.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"description": "Defines a dev container",
|
||||
"allowComments": true,
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"devContainerCommon": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "A name to show for the workspace folder."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"description": "An array of extensions that should be installed into the container.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"type": "object",
|
||||
"description": "Machine specific settings that should be copied into the container."
|
||||
},
|
||||
"postCreateCommand": {
|
||||
"type": ["string", "array"],
|
||||
"description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"devPort": {
|
||||
"type": "integer",
|
||||
"description": "The port VS Code can use to connect to its backend."
|
||||
}
|
||||
}
|
||||
},
|
||||
"nonComposeBase": {
|
||||
"properties": {
|
||||
"appPort": {
|
||||
"type": ["integer", "string", "array"],
|
||||
"description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".",
|
||||
"items": {
|
||||
"type": ["integer", "string"]
|
||||
}
|
||||
},
|
||||
"runArgs": {
|
||||
"type": "array",
|
||||
"description": "The arguments required when starting in the container.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"shutdownAction": {
|
||||
"type": "string",
|
||||
"enum": ["none", "stopContainer"],
|
||||
"description": "Action to take when VS Code is shutting down. The default is to stop the container."
|
||||
},
|
||||
"overrideCommand": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to overwrite the command specified in the image. The default is true."
|
||||
},
|
||||
"workspaceFolder": {
|
||||
"type": "string",
|
||||
"description": "The path of the workspace folder inside the container."
|
||||
},
|
||||
"workspaceMount": {
|
||||
"type": "string",
|
||||
"description": "The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project."
|
||||
}
|
||||
}
|
||||
},
|
||||
"dockerFileContainer": {
|
||||
"properties": {
|
||||
"dockerFile": {
|
||||
"type": "string",
|
||||
"description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file."
|
||||
},
|
||||
"context": {
|
||||
"type": "string",
|
||||
"description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file."
|
||||
}
|
||||
},
|
||||
"required": ["dockerFile"]
|
||||
},
|
||||
"imageContainer": {
|
||||
"properties": {
|
||||
"image": {
|
||||
"type": "string",
|
||||
"description": "The docker image that will be used to create the container."
|
||||
}
|
||||
},
|
||||
"required": ["image"]
|
||||
},
|
||||
"composeContainer": {
|
||||
"properties": {
|
||||
"dockerComposeFile": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The name of the docker-compose file(s) used to start the services.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service you want to work on."
|
||||
},
|
||||
"workspaceFolder": {
|
||||
"type": "string",
|
||||
"description": "The path of the workspace folder inside the container."
|
||||
},
|
||||
"shutdownAction": {
|
||||
"type": "string",
|
||||
"enum": ["none", "stopCompose"],
|
||||
"description": "Action to take when VS Code is shutting down. The default is to stop the containers."
|
||||
}
|
||||
},
|
||||
"required": ["dockerComposeFile", "service", "workspaceFolder"]
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/dockerFileContainer"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/imageContainer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/nonComposeBase"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/composeContainer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/devContainerCommon"
|
||||
}
|
||||
]
|
||||
}
|
||||
2
extensions/dacpac/.gitignore
vendored
Normal file
2
extensions/dacpac/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
coverage
|
||||
*.vsix
|
||||
4
extensions/dacpac/.vscodeignore
Normal file
4
extensions/dacpac/.vscodeignore
Normal file
@@ -0,0 +1,4 @@
|
||||
coverage/**
|
||||
coverageConfig.json
|
||||
src/**
|
||||
tsconfig.json
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user