mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-23 18:47:06 -05:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c2d79e9cf | ||
|
|
cd140b5527 | ||
|
|
a0456bf4f7 | ||
|
|
55e3947cf7 | ||
|
|
db5156e4cd | ||
|
|
eece0677a7 | ||
|
|
24e8c20511 | ||
|
|
886717d330 | ||
|
|
26b27a616a | ||
|
|
0c663e5555 | ||
|
|
0f087915f6 | ||
|
|
a78fa9c0f2 | ||
|
|
b1752ea635 | ||
|
|
ec150917c2 | ||
|
|
7a9a69c439 | ||
|
|
9e9862c6f0 | ||
|
|
7b76d929cd | ||
|
|
1811dfa423 | ||
|
|
3abbc8fd97 | ||
|
|
4b88b67bed | ||
|
|
a2734807ca | ||
|
|
d0d4df313e | ||
|
|
1efd5e6502 | ||
|
|
b12cac0ac3 | ||
|
|
578aa6ccd2 |
2
.yarnrc
2
.yarnrc
@@ -1,3 +1,3 @@
|
|||||||
disturl "https://atom.io/download/electron"
|
disturl "https://atom.io/download/electron"
|
||||||
target "1.7.11"
|
target "1.7.12"
|
||||||
runtime "electron"
|
runtime "electron"
|
||||||
|
|||||||
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,5 +1,20 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## Version 0.28.6
|
||||||
|
* Release date: April 25, 2018
|
||||||
|
* Release status: Public Preview
|
||||||
|
|
||||||
|
## What's new in this version
|
||||||
|
The April Public Preview release contains some of the following highlights.
|
||||||
|
|
||||||
|
* Improvements to SQL Agent *Preview* extension
|
||||||
|
* Accessibility improvements for keyboard navigation, screen reader support and high-contrast mode.
|
||||||
|
* Improved large and protected file support for saving Admin protected and >256M files within SQL Ops Studio
|
||||||
|
* Integrated Terminal splitting to work with multiple open terminals at once
|
||||||
|
* Reduced installation on-disk file count foot print for faster installs and startup times
|
||||||
|
* Improvements to Server Reports extension
|
||||||
|
* Continue to fix GitHub issues
|
||||||
|
|
||||||
## Version 0.27.3
|
## Version 0.27.3
|
||||||
* Release date: March 28, 2017
|
* Release date: March 28, 2017
|
||||||
* Release status: Public Preview
|
* Release status: Public Preview
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -8,12 +8,12 @@ SQL Operations Studio is a data management tool that enables you to work with SQ
|
|||||||
|
|
||||||
Platform | Link
|
Platform | Link
|
||||||
-- | --
|
-- | --
|
||||||
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=870837
|
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=872717
|
||||||
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=870838
|
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=872718
|
||||||
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=870839
|
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=872719
|
||||||
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=870840
|
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=872720
|
||||||
Linux DEB | https://go.microsoft.com/fwlink/?linkid=870842
|
Linux DEB | https://go.microsoft.com/fwlink/?linkid=872722
|
||||||
Linux RPM | https://go.microsoft.com/fwlink/?linkid=870841
|
Linux RPM | https://go.microsoft.com/fwlink/?linkid=872721
|
||||||
|
|
||||||
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
|
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
|
||||||
|
|
||||||
@@ -21,14 +21,6 @@ Try out the latest insiders build from `master` at https://github.com/Microsoft/
|
|||||||
|
|
||||||
See the [change log](https://github.com/Microsoft/sqlopsstudio/blob/master/CHANGELOG.md) for additional details of what's in this release.
|
See the [change log](https://github.com/Microsoft/sqlopsstudio/blob/master/CHANGELOG.md) for additional details of what's in this release.
|
||||||
|
|
||||||
**Design Discussions**
|
|
||||||
|
|
||||||
The SQL Operations Studio team would like to incorporate community feedback earlier in the development process. To facilitate this, we'd like to share our designs while features are actively being built.
|
|
||||||
|
|
||||||
We're currently collecting input on the **SQL Agent** experience and enhancements to the Manage Dashboard that we're calling **"Command Center"**. We'll add additional design feedback requests below as we start work in new feature areas. Please leave comments on these issues to help us understand your requirements and shape feature development.
|
|
||||||
|
|
||||||
* [#750 Seeking community feedback on SQL Agent UX prototype](https://github.com/Microsoft/sqlopsstudio/issues/750)
|
|
||||||
|
|
||||||
**Feature Highlights**
|
**Feature Highlights**
|
||||||
|
|
||||||
- Cross-Platform DB management for Windows, macOS and Linux with simple XCopy deployment
|
- Cross-Platform DB management for Windows, macOS and Linux with simple XCopy deployment
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||||
"version": "1.4.0-alpha.23",
|
"version": "1.4.0-alpha.25",
|
||||||
"downloadFileNames": {
|
"downloadFileNames": {
|
||||||
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
||||||
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sqlops",
|
"name": "sqlops",
|
||||||
"version": "0.28.5",
|
"version": "0.29.1",
|
||||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Microsoft Corporation"
|
"name": "Microsoft Corporation"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Package: @@NAME@@
|
Package: @@NAME@@
|
||||||
Version: @@VERSION@@
|
Version: @@VERSION@@
|
||||||
Section: devel
|
Section: devel
|
||||||
Depends: libnotify4, libnss3, gnupg, apt, libxkbfile1, libgconf-2-4, libsecret-1-0
|
Depends: libnotify4, libnss3, gnupg, apt, libxkbfile1, libgconf-2-4, libsecret-1-0, libunwind8
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: @@ARCHITECTURE@@
|
Architecture: @@ARCHITECTURE@@
|
||||||
Maintainer: Microsoft Corporation
|
Maintainer: Microsoft Corporation
|
||||||
|
|||||||
4133
samples/extensionSamples/package-lock.json
generated
4133
samples/extensionSamples/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
65
samples/serverReports/.vscode/launch.json
vendored
65
samples/serverReports/.vscode/launch.json
vendored
@@ -1,28 +1,57 @@
|
|||||||
|
// A launch configuration that launches the extension inside a new window
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
|
||||||
|
// To debug the extension:
|
||||||
|
// 1. please install the "SQL Operations Studio Debug" extension into VSCode
|
||||||
|
// 2. Ensure sqlops is added to your path:
|
||||||
|
// - open SQL Operations Studio
|
||||||
|
// - run the command "Install 'sqlops' command in PATH"
|
||||||
{
|
{
|
||||||
// Use IntelliSense to learn about possible attributes.
|
"version": "0.2.0",
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
|
||||||
{
|
{
|
||||||
"type": "node",
|
"name": "Debug in SqlOps install",
|
||||||
|
"type": "sqlopsExtensionHost",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Launch Program",
|
"runtimeExecutable": "sqlops",
|
||||||
"program": "${workspaceFolder}\\out\\src\\extension"
|
"args": [
|
||||||
|
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "node",
|
"type": "node",
|
||||||
"request": "attach",
|
"request": "attach",
|
||||||
"name": "Attach to Ops Studio",
|
"name": "Attach to Ops Studio",
|
||||||
"protocol": "inspector",
|
"protocol": "inspector",
|
||||||
"port": 5870,
|
"port": 5870,
|
||||||
"restart": true,
|
"restart": true,
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"outFiles": [
|
"outFiles": [
|
||||||
"${workspaceRoot}/out/**/*.js"
|
"${workspaceRoot}/out/**/*.js"
|
||||||
],
|
],
|
||||||
"preLaunchTask": "",
|
"preLaunchTask": "",
|
||||||
"timeout": 25000
|
"timeout": 25000
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug in enlistment",
|
||||||
|
"type": "sqlopsExtensionHost",
|
||||||
|
"request": "launch",
|
||||||
|
"windows": {
|
||||||
|
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.bat"
|
||||||
|
},
|
||||||
|
"osx": {
|
||||||
|
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.sh"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.sh"
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||||
|
],
|
||||||
|
"timeout": 20000
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -22,4 +22,22 @@ See the [Server Reports Extension Project] in the SQL Operations Studio reposito
|
|||||||
|
|
||||||
|
|
||||||
## Contributions and "thank you"
|
## Contributions and "thank you"
|
||||||
Special thank to Paul Randal, Aaron Bertrand, and Glenn Berry for providing useful queries.
|
Special thanks to our Microsoft MVPs for providing useful queries.
|
||||||
|
* Paul Randal:
|
||||||
|
https://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts/
|
||||||
|
|
||||||
|
See [Paul Randal's wait types library] for more information about each wait type in the Wait Counts widget.
|
||||||
|
|
||||||
|
[Paul Randal's wait types library]:https://www.sqlskills.com/help/waits
|
||||||
|
|
||||||
|
* Glenn Berry: https://gallery.technet.microsoft.com/scriptcenter/All-Databases-Data-log-a36da95d
|
||||||
|
|
||||||
|
* Aaron Bertrand: https://www.mssqltips.com/sqlservertip/2393/determine-sql-server-memory-use-by-database-and-object/
|
||||||
|
|
||||||
|
|
||||||
|
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
|
||||||
|
* flyfishingdba for Add square brackets for ms_foreachdb call (#1023)
|
||||||
|
|
||||||
|
## What's new in Server Reports v1.1?
|
||||||
|
* Fixed DB Space Usage where it threw an error when database names contain special characters
|
||||||
|
* Changed DB Space Usage and DB Buffer Usage to show only top 10 data
|
||||||
537
samples/serverReports/package-lock.json
generated
537
samples/serverReports/package-lock.json
generated
@@ -625,6 +625,7 @@
|
|||||||
"anymatch": "2.0.0",
|
"anymatch": "2.0.0",
|
||||||
"async-each": "1.0.1",
|
"async-each": "1.0.1",
|
||||||
"braces": "2.3.1",
|
"braces": "2.3.1",
|
||||||
|
"fsevents": "1.2.2",
|
||||||
"glob-parent": "3.1.0",
|
"glob-parent": "3.1.0",
|
||||||
"inherits": "2.0.3",
|
"inherits": "2.0.3",
|
||||||
"is-binary-path": "1.0.1",
|
"is-binary-path": "1.0.1",
|
||||||
@@ -1730,6 +1731,535 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||||
},
|
},
|
||||||
|
"fsevents": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-iownA+hC4uHFp+7gwP/y5SzaiUo7m2vpa0dhpzw8YuKtiZsz7cIXsFbXpLEeBM6WuCQyw1MH4RRe6XI8GFUctQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"nan": "2.10.0",
|
||||||
|
"node-pre-gyp": "0.9.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"aproba": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"are-we-there-yet": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"delegates": "1.0.0",
|
||||||
|
"readable-stream": "2.3.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"balanced-match": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"balanced-match": "1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chownr": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"code-point-at": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"console-control-strings": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"core-util-is": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"delegates": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"detect-libc": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"fs-minipass": {
|
||||||
|
"version": "1.2.5",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"minipass": "2.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"gauge": {
|
||||||
|
"version": "2.7.4",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"aproba": "1.2.0",
|
||||||
|
"console-control-strings": "1.1.0",
|
||||||
|
"has-unicode": "2.0.1",
|
||||||
|
"object-assign": "4.1.1",
|
||||||
|
"signal-exit": "3.0.2",
|
||||||
|
"string-width": "1.0.2",
|
||||||
|
"strip-ansi": "3.0.1",
|
||||||
|
"wide-align": "1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"glob": {
|
||||||
|
"version": "7.1.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "1.0.0",
|
||||||
|
"inflight": "1.0.6",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"minimatch": "3.0.4",
|
||||||
|
"once": "1.4.0",
|
||||||
|
"path-is-absolute": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-unicode": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.4.21",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ignore-walk": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"minimatch": "3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"once": "1.4.0",
|
||||||
|
"wrappy": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"ini": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"number-is-nan": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"minimatch": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"brace-expansion": "1.1.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimist": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"minipass": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.1",
|
||||||
|
"yallist": "3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minizlib": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"minipass": "2.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "0.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"needle": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"iconv-lite": "0.4.21",
|
||||||
|
"sax": "1.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node-pre-gyp": {
|
||||||
|
"version": "0.9.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"detect-libc": "1.0.3",
|
||||||
|
"mkdirp": "0.5.1",
|
||||||
|
"needle": "2.2.0",
|
||||||
|
"nopt": "4.0.1",
|
||||||
|
"npm-packlist": "1.1.10",
|
||||||
|
"npmlog": "4.1.2",
|
||||||
|
"rc": "1.2.6",
|
||||||
|
"rimraf": "2.6.2",
|
||||||
|
"semver": "5.5.0",
|
||||||
|
"tar": "4.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nopt": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"abbrev": "1.1.1",
|
||||||
|
"osenv": "0.1.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm-bundled": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"npm-packlist": {
|
||||||
|
"version": "1.1.10",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"ignore-walk": "3.0.1",
|
||||||
|
"npm-bundled": "1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npmlog": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"are-we-there-yet": "1.1.4",
|
||||||
|
"console-control-strings": "1.1.0",
|
||||||
|
"gauge": "2.7.4",
|
||||||
|
"set-blocking": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"number-is-nan": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"wrappy": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"os-homedir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"osenv": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"os-homedir": "1.0.2",
|
||||||
|
"os-tmpdir": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"process-nextick-args": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"rc": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"deep-extend": "0.4.2",
|
||||||
|
"ini": "1.3.5",
|
||||||
|
"minimist": "1.2.0",
|
||||||
|
"strip-json-comments": "2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "2.3.6",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"core-util-is": "1.0.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"isarray": "1.0.0",
|
||||||
|
"process-nextick-args": "2.0.0",
|
||||||
|
"safe-buffer": "5.1.1",
|
||||||
|
"string_decoder": "1.1.1",
|
||||||
|
"util-deprecate": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rimraf": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"glob": "7.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sax": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"signal-exit": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"code-point-at": "1.1.0",
|
||||||
|
"is-fullwidth-code-point": "1.0.0",
|
||||||
|
"strip-ansi": "3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-json-comments": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"tar": {
|
||||||
|
"version": "4.4.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"chownr": "1.0.1",
|
||||||
|
"fs-minipass": "1.2.5",
|
||||||
|
"minipass": "2.2.4",
|
||||||
|
"minizlib": "1.1.0",
|
||||||
|
"mkdirp": "0.5.1",
|
||||||
|
"safe-buffer": "5.1.1",
|
||||||
|
"yallist": "3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"wide-align": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"string-width": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"fstream": {
|
"fstream": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
|
||||||
@@ -4105,6 +4635,13 @@
|
|||||||
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
|
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"nan": {
|
||||||
|
"version": "2.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||||
|
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"nanomatch": {
|
"nanomatch": {
|
||||||
"version": "1.2.9",
|
"version": "1.2.9",
|
||||||
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz",
|
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "server-report",
|
"name": "server-report",
|
||||||
"displayName": "Server Reports",
|
"displayName": "Server Reports",
|
||||||
"description": "Server Reports",
|
"description": "Server Reports",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -126,15 +126,25 @@
|
|||||||
"container": {
|
"container": {
|
||||||
"widgets-container": [
|
"widgets-container": [
|
||||||
{
|
{
|
||||||
"name": "DB Space Usage",
|
"name": "Top 10 DB Space Usage",
|
||||||
"gridItemConfig": {
|
"gridItemConfig": {
|
||||||
"sizex": 2,
|
"sizex": 2,
|
||||||
"sizey": 1
|
"sizey": 2
|
||||||
},
|
},
|
||||||
"widget": {
|
"widget": {
|
||||||
"extension-dbspace-usage": {}
|
"extension-dbspace-usage": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Top 10 DB Buffer Usage",
|
||||||
|
"gridItemConfig": {
|
||||||
|
"sizex": 2,
|
||||||
|
"sizey": 2
|
||||||
|
},
|
||||||
|
"widget": {
|
||||||
|
"extension-dbbuffer-usage": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "CPU Utilization",
|
"name": "CPU Utilization",
|
||||||
"gridItemConfig": {
|
"gridItemConfig": {
|
||||||
@@ -164,16 +174,6 @@
|
|||||||
"widget": {
|
"widget": {
|
||||||
"extension-wait-counts-by-Paul-Randal": {}
|
"extension-wait-counts-by-Paul-Randal": {}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "DB Buffer Usage",
|
|
||||||
"gridItemConfig": {
|
|
||||||
"sizex": 2,
|
|
||||||
"sizey": 2
|
|
||||||
},
|
|
||||||
"widget": {
|
|
||||||
"extension-dbbuffer-usage": {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ FROM (
|
|||||||
ON p.object_id = it.object_id
|
ON p.object_id = it.object_id
|
||||||
) AS partitions'
|
) AS partitions'
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
select
|
select TOP 10
|
||||||
d.Dbname,
|
d.Dbname,
|
||||||
--(file_size_mb + log_file_size_mb) as DBsize,
|
--(file_size_mb + log_file_size_mb) as DBsize,
|
||||||
--d.file_Size_MB,
|
--d.file_Size_MB,
|
||||||
@@ -100,4 +100,4 @@ select
|
|||||||
--l.log_Free_Space_MB,
|
--l.log_Free_Space_MB,
|
||||||
--fs.Freespace as DB_Freespace
|
--fs.Freespace as DB_Freespace
|
||||||
from @dbsize d join @logsize l on d.Dbname=l.Dbname join @dbfreesize fs on d.Dbname=fs.name
|
from @dbsize d join @logsize l on d.Dbname=l.Dbname join @dbfreesize fs on d.Dbname=fs.name
|
||||||
order by Dbname
|
order by d.Space_Used_MB DESC
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ FROM sys.dm_os_buffer_descriptors
|
|||||||
--WHERE database_id BETWEEN 5 AND 32766
|
--WHERE database_id BETWEEN 5 AND 32766
|
||||||
GROUP BY database_id
|
GROUP BY database_id
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT TOP 10
|
||||||
[db_name] = CASE [database_id] WHEN 32767
|
[db_name] = CASE [database_id] WHEN 32767
|
||||||
THEN 'Resource DB'
|
THEN 'Resource DB'
|
||||||
ELSE DB_NAME([database_id]) END,
|
ELSE DB_NAME([database_id]) END,
|
||||||
|
|||||||
2
samples/sp_whoIsActive/package-lock.json
generated
2
samples/sp_whoIsActive/package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "whoisactive",
|
"name": "whoisactive",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "whoisactive",
|
"name": "whoisactive",
|
||||||
"displayName": "whoisactive",
|
"displayName": "whoisactive",
|
||||||
"description": "sp_whoisactive for SQL Operations Studio",
|
"description": "sp_whoisactive for SQL Operations Studio",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"command": "sp_whoisactive.install",
|
"command": "sp_whoisactive.install",
|
||||||
"title": "Install sp_whoisactive",
|
"title": "Whoisactive: Install sp_whoisactive",
|
||||||
"icon": {
|
"icon": {
|
||||||
"light": "./out/src/media/download.svg",
|
"light": "./out/src/media/download.svg",
|
||||||
"dark": "./out/src/media/download_inverse.svg"
|
"dark": "./out/src/media/download_inverse.svg"
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "sp_whoisactive.findBlockLeaders",
|
"command": "sp_whoisactive.findBlockLeaders",
|
||||||
"title": "Find leader of block",
|
"title": "Whoisactive: Find leader of block",
|
||||||
"icon": {
|
"icon": {
|
||||||
"light": "./out/src/media/blocker.svg",
|
"light": "./out/src/media/blocker.svg",
|
||||||
"dark": "./out/src/media/blocker_inverse.svg"
|
"dark": "./out/src/media/blocker_inverse.svg"
|
||||||
@@ -40,11 +40,19 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "sp_whoisactive.getPlans",
|
"command": "sp_whoisactive.getPlans",
|
||||||
"title": "Get plans",
|
"title": "Whoisactive: Get plans",
|
||||||
"icon": {
|
"icon": {
|
||||||
"light": "./out/src/media/monitor.svg",
|
"light": "./out/src/media/monitor.svg",
|
||||||
"dark": "./out/src/media/monitor_inverse.svg"
|
"dark": "./out/src/media/monitor_inverse.svg"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sp_whoisactive.documentation",
|
||||||
|
"title": "Whoisactive: Documentation",
|
||||||
|
"icon": {
|
||||||
|
"light": "./out/src/media/documentation.svg",
|
||||||
|
"dark": "./out/src/media/documentation_inverse.svg"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"views": {},
|
"views": {},
|
||||||
@@ -55,30 +63,7 @@
|
|||||||
"title": "sp_whoisactive",
|
"title": "sp_whoisactive",
|
||||||
"description": "Extension for checking who is active.",
|
"description": "Extension for checking who is active.",
|
||||||
"container": {
|
"container": {
|
||||||
"nav-section": [
|
"sp_whoisactive-insights": {}
|
||||||
{
|
|
||||||
"id": "sp_whoisactive_insights",
|
|
||||||
"title": "Insights",
|
|
||||||
"icon": {
|
|
||||||
"light": "./out/src/media/insights.svg",
|
|
||||||
"dark": "./out/src/media/insights_inverse.svg"
|
|
||||||
},
|
|
||||||
"container": {
|
|
||||||
"sp_whoisactive-insights": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sp_whoisactive_documentation",
|
|
||||||
"title": "Documentation",
|
|
||||||
"icon": {
|
|
||||||
"light": "./out/src/media/documentation.svg",
|
|
||||||
"dark": "./out/src/media/documentation_inverse.svg"
|
|
||||||
},
|
|
||||||
"container": {
|
|
||||||
"webview-container": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,7 +77,8 @@
|
|||||||
"dataType": "number",
|
"dataType": "number",
|
||||||
"legendPosition": "none",
|
"legendPosition": "none",
|
||||||
"labelFirstColumn": false,
|
"labelFirstColumn": false,
|
||||||
"columnsAsLabels": true
|
"columnsAsLabels": true,
|
||||||
|
"showTopNData": 5
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"queryFile": "./out/src/sql/cpuUsage.sql"
|
"queryFile": "./out/src/sql/cpuUsage.sql"
|
||||||
@@ -107,7 +93,8 @@
|
|||||||
"dataType": "number",
|
"dataType": "number",
|
||||||
"legendPosition": "none",
|
"legendPosition": "none",
|
||||||
"labelFirstColumn": false,
|
"labelFirstColumn": false,
|
||||||
"columnsAsLabels": true
|
"columnsAsLabels": true,
|
||||||
|
"showTopNData": 5
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"queryFile": "./out/src/sql/cpuDelta.sql"
|
"queryFile": "./out/src/sql/cpuDelta.sql"
|
||||||
@@ -122,7 +109,8 @@
|
|||||||
"dataType": "number",
|
"dataType": "number",
|
||||||
"legendPosition": "none",
|
"legendPosition": "none",
|
||||||
"labelFirstColumn": false,
|
"labelFirstColumn": false,
|
||||||
"columnsAsLabels": true
|
"columnsAsLabels": true,
|
||||||
|
"showTopNData": 5
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"queryFile": "./out/src/sql/memoryUsage.sql"
|
"queryFile": "./out/src/sql/memoryUsage.sql"
|
||||||
@@ -137,11 +125,21 @@
|
|||||||
"dataType": "number",
|
"dataType": "number",
|
||||||
"legendPosition": "none",
|
"legendPosition": "none",
|
||||||
"labelFirstColumn": false,
|
"labelFirstColumn": false,
|
||||||
"columnsAsLabels": true
|
"columnsAsLabels": true,
|
||||||
|
"showTopNData": 5
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"queryFile": "./out/src/sql/memoryDelta.sql"
|
"queryFile": "./out/src/sql/memoryDelta.sql"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sp_whoisactive-blocking_sessions",
|
||||||
|
"contrib": {
|
||||||
|
"type": {
|
||||||
|
"table": null
|
||||||
|
},
|
||||||
|
"queryFile": "./out/src/sql/blockingSessions.sql"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dashboard.containers": [
|
"dashboard.containers": [
|
||||||
@@ -155,12 +153,13 @@
|
|||||||
"tasks-widget": [
|
"tasks-widget": [
|
||||||
"sp_whoisactive.getPlans",
|
"sp_whoisactive.getPlans",
|
||||||
"sp_whoisactive.findBlockLeaders",
|
"sp_whoisactive.findBlockLeaders",
|
||||||
|
"sp_whoisactive.documentation",
|
||||||
"sp_whoisactive.install"
|
"sp_whoisactive.install"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "CPU Usage",
|
"name": "Top 5 CPU Usage",
|
||||||
"gridItemConfig": {
|
"gridItemConfig": {
|
||||||
"sizex": 2,
|
"sizex": 2,
|
||||||
"sizey": 1
|
"sizey": 1
|
||||||
@@ -170,7 +169,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "CPU Delta",
|
"name": "Top 5 CPU Delta",
|
||||||
"gridItemConfig": {
|
"gridItemConfig": {
|
||||||
"sizex": 2,
|
"sizex": 2,
|
||||||
"sizey": 1
|
"sizey": 1
|
||||||
@@ -180,7 +179,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Memory Usage",
|
"name": "Top 5 Memory Usage",
|
||||||
"gridItemConfig": {
|
"gridItemConfig": {
|
||||||
"sizex": 2,
|
"sizex": 2,
|
||||||
"sizey": 1
|
"sizey": 1
|
||||||
@@ -190,7 +189,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Memory Delta",
|
"name": "Top 5 Memory Delta",
|
||||||
"gridItemConfig": {
|
"gridItemConfig": {
|
||||||
"sizex": 2,
|
"sizex": 2,
|
||||||
"sizey": 1
|
"sizey": 1
|
||||||
@@ -198,6 +197,16 @@
|
|||||||
"widget": {
|
"widget": {
|
||||||
"sp_whoisactive-memory-delta": {}
|
"sp_whoisactive-memory-delta": {}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Blocking Sessions",
|
||||||
|
"gridItemConfig": {
|
||||||
|
"sizex": 2,
|
||||||
|
"sizey": 1
|
||||||
|
},
|
||||||
|
"widget": {
|
||||||
|
"sp_whoisactive-blocking_sessions": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,23 +29,16 @@ export default class MainController extends ControllerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public activate(): Promise<boolean> {
|
public activate(): Promise<boolean> {
|
||||||
sqlops.dashboard.registerWebviewProvider('sp_whoisactive_documentation', webview => {
|
sqlops.tasks.registerTask('sp_whoisactive.install', e => this.openurl('http://whoisactive.com/downloads/'));
|
||||||
let templateValues = {url: 'http://whoisactive.com/docs/'};
|
sqlops.tasks.registerTask('sp_whoisactive.documentation', e => this.openurl('http://whoisactive.com/docs/'));
|
||||||
Utils.renderTemplateHtml(path.join(__dirname, '..'), 'templateTab.html', templateValues)
|
|
||||||
.then(html => {
|
|
||||||
webview.html = html;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
sqlops.tasks.registerTask('sp_whoisactive.install', e => this.onInstall(e));
|
|
||||||
sqlops.tasks.registerTask('sp_whoisactive.findBlockLeaders', e => this.onExecute(e, 'findBlockLeaders.sql'));
|
sqlops.tasks.registerTask('sp_whoisactive.findBlockLeaders', e => this.onExecute(e, 'findBlockLeaders.sql'));
|
||||||
sqlops.tasks.registerTask('sp_whoisactive.getPlans', e => this.onExecute(e, 'getPlans.sql'));
|
sqlops.tasks.registerTask('sp_whoisactive.getPlans', e => this.onExecute(e, 'getPlans.sql'));
|
||||||
|
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onInstall(connection: sqlops.IConnectionProfile): void {
|
private openurl(link: string): void {
|
||||||
openurl.open('http://whoisactive.com/downloads/');
|
openurl.open(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onExecute(connection: sqlops.IConnectionProfile, fileName: string): void {
|
private onExecute(connection: sqlops.IConnectionProfile, fileName: string): void {
|
||||||
|
|||||||
4
samples/sp_whoIsActive/src/sql/blockingSessions.sql
Normal file
4
samples/sp_whoIsActive/src/sql/blockingSessions.sql
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
SELECT blocking_session_id
|
||||||
|
FROM sys.dm_os_waiting_tasks
|
||||||
|
WHERE
|
||||||
|
blocking_session_id IS NOT NULL
|
||||||
75
src/sql/base/browser/ui/scrollable/scrollable.directive.ts
Normal file
75
src/sql/base/browser/ui/scrollable/scrollable.directive.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { Directive, Inject, forwardRef, ElementRef } from '@angular/core';
|
||||||
|
|
||||||
|
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||||
|
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||||
|
import { getContentHeight, addDisposableListener, EventType } from 'vs/base/browser/dom';
|
||||||
|
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[scrollable]'
|
||||||
|
})
|
||||||
|
export class ScrollableDirective extends AngularDisposable {
|
||||||
|
private scrollableElement: ScrollableElement;
|
||||||
|
private parent: HTMLElement;
|
||||||
|
private scrolled: HTMLElement;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.scrolled = this._el.nativeElement as HTMLElement;
|
||||||
|
this.parent = this.scrolled.parentElement;
|
||||||
|
this.parent.removeChild(this.scrolled);
|
||||||
|
|
||||||
|
this.scrolled.style.position = 'relative';
|
||||||
|
|
||||||
|
this.scrollableElement = new ScrollableElement(this.scrolled, {
|
||||||
|
horizontal: ScrollbarVisibility.Hidden,
|
||||||
|
vertical: ScrollbarVisibility.Auto,
|
||||||
|
useShadows: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.scrollableElement.onScroll(e => {
|
||||||
|
this.scrolled.style.bottom = e.scrollTop + 'px';
|
||||||
|
});
|
||||||
|
|
||||||
|
this.parent.appendChild(this.scrollableElement.getDomNode());
|
||||||
|
const initialHeight = getContentHeight(this.scrolled);
|
||||||
|
this.scrollableElement.setScrollDimensions({
|
||||||
|
scrollHeight: getContentHeight(this.scrolled),
|
||||||
|
height: getContentHeight(this.parent)
|
||||||
|
});
|
||||||
|
|
||||||
|
this._register(addDisposableListener(window, EventType.RESIZE, () => {
|
||||||
|
this.resetScrollDimensions();
|
||||||
|
}));
|
||||||
|
|
||||||
|
// unforunately because of angular rendering behavior we need to do a double check to make sure nothing changed after this point
|
||||||
|
setTimeout(() => {
|
||||||
|
let currentheight = getContentHeight(this.scrolled);
|
||||||
|
if (initialHeight !== currentheight) {
|
||||||
|
this.scrollableElement.setScrollDimensions({
|
||||||
|
scrollHeight: currentheight,
|
||||||
|
height: getContentHeight(this.parent)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private resetScrollDimensions() {
|
||||||
|
this.scrollableElement.setScrollDimensions({
|
||||||
|
scrollHeight: getContentHeight(this.scrolled),
|
||||||
|
height: getContentHeight(this.parent)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowdetailview.js
|
// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowdetailview.js
|
||||||
// heavily modified
|
// heavily modified
|
||||||
import { mixin } from 'vs/base/common/objects';
|
import { mixin } from 'vs/base/common/objects';
|
||||||
|
import * as nls from 'vs/nls';
|
||||||
|
|
||||||
export class RowDetailView {
|
export class RowDetailView {
|
||||||
|
|
||||||
@@ -277,7 +278,7 @@ export class RowDetailView {
|
|||||||
item._parent = parent;
|
item._parent = parent;
|
||||||
item._offset = offset;
|
item._offset = offset;
|
||||||
item.jobId = parent.jobId;
|
item.jobId = parent.jobId;
|
||||||
item.name = parent.message ? parent.message : 'Error';
|
item.name = parent.message ? parent.message : nls.localize('rowDetailView.loadError','Loading Error...');
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,19 +88,6 @@ export function parseNumAsTimeString(value: number): string {
|
|||||||
return tempVal > 0 ? rs + '.' + mss : rs;
|
return tempVal > 0 ? rs + '.' + mss : rs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts <, >, &, ", ', and any characters that are outside \u00A0 to numeric HTML entity values
|
|
||||||
* like {
|
|
||||||
* (Adapted from http://stackoverflow.com/a/18750001)
|
|
||||||
* @param str String to convert
|
|
||||||
* @return String with characters replaced.
|
|
||||||
*/
|
|
||||||
export function htmlEntities(str: string): string {
|
|
||||||
return typeof (str) === 'string'
|
|
||||||
? str.replace(/[\u00A0-\u9999<>\&"']/gim, (i) => { return `&#${i.charCodeAt(0)};`; })
|
|
||||||
: undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateUri(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): string {
|
export function generateUri(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): string {
|
||||||
let prefix = purpose ? uriPrefixes[purpose] : uriPrefixes.default;
|
let prefix = purpose ? uriPrefixes[purpose] : uriPrefixes.default;
|
||||||
let uri = generateUriWithPrefix(connection, prefix);
|
let uri = generateUriWithPrefix(connection, prefix);
|
||||||
|
|||||||
@@ -5,33 +5,37 @@
|
|||||||
|
|
||||||
import 'vs/css!./dashboardHomeContainer';
|
import 'vs/css!./dashboardHomeContainer';
|
||||||
|
|
||||||
import { Component, forwardRef, Input, ChangeDetectorRef, Inject, ViewChild } from '@angular/core';
|
import { Component, forwardRef, Input, ChangeDetectorRef, Inject, ViewChild, ContentChild } from '@angular/core';
|
||||||
|
|
||||||
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
|
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
|
||||||
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||||
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { AngularEventType } from '../../../services/angularEventing/angularEventingService';
|
import { AngularEventType } from 'sql/services/angularEventing/angularEventingService';
|
||||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
|
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
|
||||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||||
|
import { ScrollableDirective } from 'sql/base/browser/ui/scrollable/scrollable.directive';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'dashboard-home-container',
|
selector: 'dashboard-home-container',
|
||||||
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardHomeContainer) }],
|
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardHomeContainer) }],
|
||||||
template: `
|
template: `
|
||||||
<div class="fullsize" style="display: flex; flex-direction: column">
|
<div class="fullsize" style="display: flex; flex-direction: column">
|
||||||
<dashboard-widget-wrapper #propertiesClass *ngIf="properties" [collapsable]="true" [_config]="properties"
|
<div scrollable>
|
||||||
style="padding-left: 10px; padding-right: 10px; display: block; flex: 0" [style.height.px]="_propertiesClass?.collapsed ? '30' : '90'">
|
<dashboard-widget-wrapper #propertiesClass *ngIf="properties" [collapsable]="true" [_config]="properties"
|
||||||
</dashboard-widget-wrapper>
|
style="padding-left: 10px; padding-right: 10px; display: block; flex: 0" [style.height.px]="_propertiesClass?.collapsed ? '30' : '90'">
|
||||||
<widget-content style="flex: 1" [widgets]="widgets" [originalConfig]="tab.originalConfig" [context]="tab.context">
|
</dashboard-widget-wrapper>
|
||||||
</widget-content>
|
<widget-content style="flex: 1" [scrollContent]="false" [widgets]="widgets" [originalConfig]="tab.originalConfig" [context]="tab.context">
|
||||||
|
</widget-content>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class DashboardHomeContainer extends DashboardWidgetContainer {
|
export class DashboardHomeContainer extends DashboardWidgetContainer {
|
||||||
@Input() private properties: WidgetConfig;
|
@Input() private properties: WidgetConfig;
|
||||||
@ViewChild('propertiesClass') private _propertiesClass: DashboardWidgetWrapper;
|
@ViewChild('propertiesClass') private _propertiesClass: DashboardWidgetWrapper;
|
||||||
|
@ContentChild(ScrollableDirective) private _scrollable;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef,
|
||||||
@@ -56,4 +60,9 @@ export class DashboardHomeContainer extends DashboardWidgetContainer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public layout() {
|
||||||
|
super.layout();
|
||||||
|
this._scrollable.layout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ export class WidgetContent extends AngularDisposable implements AfterViewInit {
|
|||||||
@Input() private widgets: WidgetConfig[];
|
@Input() private widgets: WidgetConfig[];
|
||||||
@Input() private originalConfig: WidgetConfig[];
|
@Input() private originalConfig: WidgetConfig[];
|
||||||
@Input() private context: string;
|
@Input() private context: string;
|
||||||
|
@Input() private scrollContent = true;
|
||||||
|
|
||||||
private _scrollableElement: ScrollableElement;
|
private _scrollableElement: ScrollableElement;
|
||||||
|
|
||||||
@@ -123,41 +124,43 @@ export class WidgetContent extends AngularDisposable implements AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
let container = this._scrollContainer.nativeElement as HTMLElement;
|
if (this.scrollContent) {
|
||||||
let scrollable = this._scrollable.nativeElement as HTMLElement;
|
let container = this._scrollContainer.nativeElement as HTMLElement;
|
||||||
container.removeChild(scrollable);
|
let scrollable = this._scrollable.nativeElement as HTMLElement;
|
||||||
|
container.removeChild(scrollable);
|
||||||
|
|
||||||
this._scrollableElement = new ScrollableElement(scrollable, {
|
this._scrollableElement = new ScrollableElement(scrollable, {
|
||||||
horizontal: ScrollbarVisibility.Hidden,
|
horizontal: ScrollbarVisibility.Hidden,
|
||||||
vertical: ScrollbarVisibility.Auto,
|
vertical: ScrollbarVisibility.Auto,
|
||||||
useShadows: false
|
useShadows: false
|
||||||
});
|
});
|
||||||
|
|
||||||
this._scrollableElement.onScroll(e => {
|
this._scrollableElement.onScroll(e => {
|
||||||
scrollable.style.bottom = e.scrollTop + 'px';
|
scrollable.style.bottom = e.scrollTop + 'px';
|
||||||
});
|
});
|
||||||
|
|
||||||
container.appendChild(this._scrollableElement.getDomNode());
|
container.appendChild(this._scrollableElement.getDomNode());
|
||||||
let initalHeight = getContentHeight(scrollable);
|
let initalHeight = getContentHeight(scrollable);
|
||||||
this._scrollableElement.setScrollDimensions({
|
this._scrollableElement.setScrollDimensions({
|
||||||
scrollHeight: getContentHeight(scrollable),
|
scrollHeight: getContentHeight(scrollable),
|
||||||
height: getContentHeight(container)
|
height: getContentHeight(container)
|
||||||
});
|
});
|
||||||
|
|
||||||
this._register(addDisposableListener(window, EventType.RESIZE, () => {
|
this._register(addDisposableListener(window, EventType.RESIZE, () => {
|
||||||
this.resetScrollDimensions();
|
this.resetScrollDimensions();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// unforunately because of angular rendering behavior we need to do a double check to make sure nothing changed after this point
|
// unforunately because of angular rendering behavior we need to do a double check to make sure nothing changed after this point
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let currentheight = getContentHeight(scrollable);
|
let currentheight = getContentHeight(scrollable);
|
||||||
if (initalHeight !== currentheight) {
|
if (initalHeight !== currentheight) {
|
||||||
this._scrollableElement.setScrollDimensions({
|
this._scrollableElement.setScrollDimensions({
|
||||||
scrollHeight: currentheight,
|
scrollHeight: currentheight,
|
||||||
height: getContentHeight(container)
|
height: getContentHeight(container)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public layout() {
|
public layout() {
|
||||||
@@ -167,7 +170,9 @@ export class WidgetContent extends AngularDisposable implements AfterViewInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._grid.triggerResize();
|
this._grid.triggerResize();
|
||||||
this.resetScrollDimensions();
|
if (this.scrollContent) {
|
||||||
|
this.resetScrollDimensions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private resetScrollDimensions() {
|
private resetScrollDimensions() {
|
||||||
|
|||||||
@@ -54,9 +54,10 @@ import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.comp
|
|||||||
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
||||||
|
|
||||||
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
||||||
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
|
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
|
||||||
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
|
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
|
||||||
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper];
|
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper,
|
||||||
|
ScrollableDirective];
|
||||||
|
|
||||||
/* Panel */
|
/* Panel */
|
||||||
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
||||||
@@ -74,6 +75,7 @@ import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.compo
|
|||||||
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
|
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
|
||||||
import { WebviewWidget } from 'sql/parts/dashboard/widgets/webview/webviewWidget.component';
|
import { WebviewWidget } from 'sql/parts/dashboard/widgets/webview/webviewWidget.component';
|
||||||
import { JobStepsViewComponent } from '../jobManagement/views/jobStepsView.component';
|
import { JobStepsViewComponent } from '../jobManagement/views/jobStepsView.component';
|
||||||
|
import { ScrollableDirective } from 'sql/base/browser/ui/scrollable/scrollable.directive';
|
||||||
|
|
||||||
let widgetComponents = [
|
let widgetComponents = [
|
||||||
PropertiesWidgetComponent,
|
PropertiesWidgetComponent,
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as Utils from 'sql/parts/connection/common/utils';
|
import * as Strings from 'vs/base/common/strings';
|
||||||
|
|
||||||
export class DBCellValue {
|
export class DBCellValue {
|
||||||
displayValue: string;
|
displayValue: string;
|
||||||
isNull: boolean;
|
isNull: boolean;
|
||||||
|
|
||||||
public static isDBCellValue(object: any): boolean {
|
public static isDBCellValue(object: any): boolean {
|
||||||
return (object !== undefined && object.displayValue !== undefined && object.isNull !== undefined);
|
return (object !== undefined && object.displayValue !== undefined && object.isNull !== undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ export function hyperLinkFormatter(row: number, cell: any, value: any, columnDef
|
|||||||
valueToDisplay = 'NULL';
|
valueToDisplay = 'NULL';
|
||||||
if (!value.isNull) {
|
if (!value.isNull) {
|
||||||
cellClasses += ' xmlLink';
|
cellClasses += ' xmlLink';
|
||||||
valueToDisplay = Utils.htmlEntities(value.displayValue);
|
valueToDisplay = Strings.escape(value.displayValue);
|
||||||
return `<a class="${cellClasses}" href="#" >${valueToDisplay}</a>`;
|
return `<a class="${cellClasses}" href="#" >${valueToDisplay}</a>`;
|
||||||
} else {
|
} else {
|
||||||
cellClasses += ' missing-value';
|
cellClasses += ' missing-value';
|
||||||
@@ -44,13 +44,12 @@ export function textFormatter(row: number, cell: any, value: any, columnDef: any
|
|||||||
if (DBCellValue.isDBCellValue(value)) {
|
if (DBCellValue.isDBCellValue(value)) {
|
||||||
valueToDisplay = 'NULL';
|
valueToDisplay = 'NULL';
|
||||||
if (!value.isNull) {
|
if (!value.isNull) {
|
||||||
valueToDisplay = Utils.htmlEntities(value.displayValue.replace(/(\r\n|\n|\r)/g, ' '));
|
valueToDisplay = Strings.escape(value.displayValue.replace(/(\r\n|\n|\r)/g, ' '));
|
||||||
} else {
|
} else {
|
||||||
cellClasses += ' missing-value';
|
cellClasses += ' missing-value';
|
||||||
}
|
}
|
||||||
} else if (typeof value === 'string'){
|
} else if (typeof value === 'string') {
|
||||||
valueToDisplay = value;
|
valueToDisplay = Strings.escape(value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<span title="${valueToDisplay}" class="${cellClasses}">${valueToDisplay}</span>`;
|
return `<span title="${valueToDisplay}" class="${cellClasses}">${valueToDisplay}</span>`;
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import * as nls from 'vs/nls';
|
|||||||
|
|
||||||
export class AgentJobUtilities {
|
export class AgentJobUtilities {
|
||||||
|
|
||||||
|
public static startIconClass: string = 'icon-start';
|
||||||
|
public static stopIconClass: string = 'icon-stop';
|
||||||
|
|
||||||
public static convertToStatusString(status: number): string {
|
public static convertToStatusString(status: number): string {
|
||||||
switch(status) {
|
switch(status) {
|
||||||
case(0): return nls.localize('agentUtilities.failed','Failed');
|
case(0): return nls.localize('agentUtilities.failed','Failed');
|
||||||
@@ -51,4 +54,41 @@ export class AgentJobUtilities {
|
|||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static setRunnable(icon: HTMLElement, index: number) {
|
||||||
|
if (icon.className.includes('non-runnable')) {
|
||||||
|
icon.className = icon.className.slice(0, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getActionIconClassName(startIcon: HTMLElement, stopIcon: HTMLElement, executionStatus: number) {
|
||||||
|
this.setRunnable(startIcon, AgentJobUtilities.startIconClass.length);
|
||||||
|
this.setRunnable(stopIcon, AgentJobUtilities.stopIconClass.length);
|
||||||
|
switch (executionStatus) {
|
||||||
|
case(1): // executing
|
||||||
|
startIcon.className += ' non-runnable';
|
||||||
|
return;
|
||||||
|
case(2): // Waiting for thread
|
||||||
|
startIcon.className += ' non-runnable';
|
||||||
|
return;
|
||||||
|
case(3): // Between retries
|
||||||
|
startIcon.className += ' non-runnable';
|
||||||
|
return;
|
||||||
|
case(4): //Idle
|
||||||
|
stopIcon.className += ' non-runnable';
|
||||||
|
return;
|
||||||
|
case(5): // Suspended
|
||||||
|
stopIcon.className += ' non-runnable';
|
||||||
|
return;
|
||||||
|
case(6): //obsolete
|
||||||
|
startIcon.className += ' non-runnable';
|
||||||
|
stopIcon.className += ' non-runnable';
|
||||||
|
return;
|
||||||
|
case(7): //Performing Completion Actions
|
||||||
|
startIcon.className += ' non-runnable';
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -22,8 +22,9 @@ jobhistory-component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.vs-dark .job-heading-container {
|
.vs-dark .job-heading-container {
|
||||||
height: 32px;
|
height: 49px;
|
||||||
border-bottom: 3px solid #444444;
|
border-bottom: 3px solid #444444;
|
||||||
|
display: -webkit-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
#jobsDiv .jobview-grid {
|
#jobsDiv .jobview-grid {
|
||||||
@@ -182,4 +183,11 @@ jobsview-component .jobview-grid > .monaco-table .slick-viewport > .grid-canvas
|
|||||||
|
|
||||||
.vs-dark .jobview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
|
.vs-dark .jobview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
|
||||||
border-left: 1px dotted white;
|
border-left: 1px dotted white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-heading-container > .icon.in-progress {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-left: 15px;
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,10 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
-->
|
-->
|
||||||
|
<div class="jobhistory-heading-container">
|
||||||
<h1 class="job-heading">Jobs | {{this._agentJobInfo?.name}} </h1>
|
<h1 class="job-heading">Jobs | {{this._agentJobInfo?.name}} </h1>
|
||||||
|
<div class="icon in-progress" *ngIf="showProgressWheel()"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Back -->
|
<!-- Back -->
|
||||||
<div class="all-jobs">
|
<div class="all-jobs">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import 'vs/css!./jobHistory';
|
import 'vs/css!./jobHistory';
|
||||||
|
import 'vs/css!sql/media/icons/common-icons';
|
||||||
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
||||||
import { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops';
|
import { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops';
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
@@ -60,6 +61,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
private _jobCacheObject: JobCacheObject;
|
private _jobCacheObject: JobCacheObject;
|
||||||
private _notificationService: INotificationService;
|
private _notificationService: INotificationService;
|
||||||
private _agentJobInfo: AgentJobInfo;
|
private _agentJobInfo: AgentJobInfo;
|
||||||
|
private _noJobsAvailable: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(BOOTSTRAP_SERVICE_ID) private bootstrapService: IBootstrapService,
|
@Inject(BOOTSTRAP_SERVICE_ID) private bootstrapService: IBootstrapService,
|
||||||
@@ -85,9 +87,6 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
this._jobCacheObject.serverName = serverName;
|
this._jobCacheObject.serverName = serverName;
|
||||||
this._jobManagementService.addToCache(serverName, this._jobCacheObject);
|
this._jobManagementService.addToCache(serverName, this._jobCacheObject);
|
||||||
}
|
}
|
||||||
$('#accordion').keypress(e => {
|
|
||||||
let meme = e;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -134,6 +133,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
this._agentJobInfo = this._agentViewComponent.agentJobInfo;
|
this._agentJobInfo = this._agentViewComponent.agentJobInfo;
|
||||||
if (!this.agentJobInfo) {
|
if (!this.agentJobInfo) {
|
||||||
this.agentJobInfo = this._agentJobInfo;
|
this.agentJobInfo = this._agentJobInfo;
|
||||||
|
this.setActions();
|
||||||
}
|
}
|
||||||
if (this._isVisible === false && this._tableContainer.nativeElement.offsetParent !== null) {
|
if (this._isVisible === false && this._tableContainer.nativeElement.offsetParent !== null) {
|
||||||
this._isVisible = true;
|
this._isVisible = true;
|
||||||
@@ -148,7 +148,10 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
} else if (jobHistories && jobHistories.length === 0 ){
|
} else if (jobHistories && jobHistories.length === 0 ){
|
||||||
this._showPreviousRuns = false;
|
this._showPreviousRuns = false;
|
||||||
this._showSteps = false;
|
this._showSteps = false;
|
||||||
|
this._noJobsAvailable = true;
|
||||||
this._cd.detectChanges();
|
this._cd.detectChanges();
|
||||||
|
} else {
|
||||||
|
this.loadHistory();
|
||||||
}
|
}
|
||||||
this._jobCacheObject.prevJobID = this._agentViewComponent.jobId;
|
this._jobCacheObject.prevJobID = this._agentViewComponent.jobId;
|
||||||
} else if (this._isVisible === true && this._agentViewComponent.refresh) {
|
} else if (this._isVisible === true && this._agentViewComponent.refresh) {
|
||||||
@@ -276,6 +279,16 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
return time.replace('T', ' ');
|
return time.replace('T', ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private showProgressWheel(): boolean {
|
||||||
|
return this._showPreviousRuns !== true && this._noJobsAvailable === false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setActions(): void {
|
||||||
|
let startIcon: HTMLElement = $('.icon-start').get(0);
|
||||||
|
let stopIcon: HTMLElement = $('.icon-stop').get(0);
|
||||||
|
AgentJobUtilities.getActionIconClassName(startIcon, stopIcon, this.agentJobInfo.currentExecutionStatus);
|
||||||
|
}
|
||||||
|
|
||||||
public get showSteps(): boolean {
|
public get showSteps(): boolean {
|
||||||
return this._showSteps;
|
return this._showSteps;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ ul.action-buttons li {
|
|||||||
padding-right: 25px;
|
padding-right: 25px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.overview-container .overview-tab .resultsViewCollapsible {
|
.overview-container .overview-tab .resultsViewCollapsible {
|
||||||
@@ -134,6 +133,7 @@ input#accordion:checked ~ .accordion-content {
|
|||||||
width: 20px;
|
width: 20px;
|
||||||
background-image: url('../common/media/start.svg');
|
background-image: url('../common/media/start.svg');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vs ul.action-buttons .icon-stop,
|
.vs ul.action-buttons .icon-stop,
|
||||||
@@ -144,6 +144,17 @@ input#accordion:checked ~ .accordion-content {
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.action-buttons div.icon-start.non-runnable {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.action-buttons div.icon-stop.non-runnable {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.accordion-content #col1,
|
.accordion-content #col1,
|
||||||
@@ -240,4 +251,15 @@ table.step-list tr.step-row td {
|
|||||||
|
|
||||||
jobhistory-component .history-details .step-table.prev-run-list .monaco-scrollable-element {
|
jobhistory-component .history-details .step-table.prev-run-list .monaco-scrollable-element {
|
||||||
overflow-y: scroll !important;
|
overflow-y: scroll !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobhistory-component .jobhistory-heading-container {
|
||||||
|
display: -webkit-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobhistory-component > .jobhistory-heading-container > .icon.in-progress {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
<div class="job-heading-container">
|
<div class="job-heading-container">
|
||||||
<h1 class="job-heading" *ngIf="_isCloud === false">Jobs</h1>
|
<h1 class="job-heading" *ngIf="_isCloud === false">Jobs</h1>
|
||||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Jobs Available</h1>
|
<h1 class="job-heading" *ngIf="_isCloud === true">No Jobs Available</h1>
|
||||||
|
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div #jobsgrid class="jobview-grid"></div>
|
<div #jobsgrid class="jobview-grid"></div>
|
||||||
@@ -9,6 +9,7 @@ import 'vs/css!sql/parts/grid/media/styles';
|
|||||||
import 'vs/css!sql/parts/grid/media/slick.grid';
|
import 'vs/css!sql/parts/grid/media/slick.grid';
|
||||||
import 'vs/css!sql/parts/grid/media/slickGrid';
|
import 'vs/css!sql/parts/grid/media/slickGrid';
|
||||||
import 'vs/css!../common/media/jobs';
|
import 'vs/css!../common/media/jobs';
|
||||||
|
import 'vs/css!sql/media/icons/common-icons';
|
||||||
|
|
||||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core';
|
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core';
|
||||||
import * as Utils from 'sql/parts/connection/common/utils';
|
import * as Utils from 'sql/parts/connection/common/utils';
|
||||||
@@ -72,6 +73,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
public jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = Object.create(null);
|
public jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = Object.create(null);
|
||||||
private _serverName: string;
|
private _serverName: string;
|
||||||
private _isCloud: boolean;
|
private _isCloud: boolean;
|
||||||
|
private _showProgressWheel: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(BOOTSTRAP_SERVICE_ID) private bootstrapService: IBootstrapService,
|
@Inject(BOOTSTRAP_SERVICE_ID) private bootstrapService: IBootstrapService,
|
||||||
@@ -99,18 +101,22 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
this.isVisible = true;
|
this.isVisible = true;
|
||||||
if (!this.isInitialized) {
|
if (!this.isInitialized) {
|
||||||
if (this._jobCacheObject.serverName === this._serverName && this._jobCacheObject.jobs.length > 0) {
|
if (this._jobCacheObject.serverName === this._serverName && this._jobCacheObject.jobs.length > 0) {
|
||||||
|
this._showProgressWheel = true;
|
||||||
this.jobs = this._jobCacheObject.jobs;
|
this.jobs = this._jobCacheObject.jobs;
|
||||||
this.onFirstVisible(true);
|
this.onFirstVisible(true);
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
} else {
|
} else {
|
||||||
|
this._showProgressWheel = true;
|
||||||
this.onFirstVisible(false);
|
this.onFirstVisible(false);
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (this.isVisible === true && this._agentViewComponent.refresh === true) {
|
} else if (this.isVisible === true && this._agentViewComponent.refresh === true) {
|
||||||
|
this._showProgressWheel = true;
|
||||||
this.onFirstVisible(false);
|
this.onFirstVisible(false);
|
||||||
this._agentViewComponent.refresh = false;
|
this._agentViewComponent.refresh = false;
|
||||||
} else if (this.isVisible === true && this._agentViewComponent.refresh === false) {
|
} else if (this.isVisible === true && this._agentViewComponent.refresh === false) {
|
||||||
|
this._showProgressWheel = true;
|
||||||
this.onFirstVisible(true);
|
this.onFirstVisible(true);
|
||||||
} else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) {
|
} else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) {
|
||||||
this.isVisible = false;
|
this.isVisible = false;
|
||||||
@@ -207,7 +213,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
|
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
|
||||||
this.expandJobRowDetails(i+expandedJobs.size);
|
this.expandJobRowDetails(i+expandedJobs.size);
|
||||||
this.addToStyleHash(i+expandedJobs.size);
|
this.addToStyleHash(i+expandedJobs.size);
|
||||||
this._agentViewComponent.setExpanded(job.jobId, 'temp');
|
this._agentViewComponent.setExpanded(job.jobId, 'Loading Error...');
|
||||||
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
|
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
|
||||||
this.expandJobRowDetails(i+expansions);
|
this.expandJobRowDetails(i+expansions);
|
||||||
this.addToStyleHash(i+expansions);
|
this.addToStyleHash(i+expansions);
|
||||||
@@ -219,6 +225,8 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
let currentTarget = e.currentTarget;
|
let currentTarget = e.currentTarget;
|
||||||
currentTarget.title = currentTarget.innerText;
|
currentTarget.title = currentTarget.innerText;
|
||||||
});
|
});
|
||||||
|
this._showProgressWheel = false;
|
||||||
|
this._cd.detectChanges();
|
||||||
this.loadJobHistories();
|
this.loadJobHistories();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +306,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
let item = self.dataView.getItemById(job.jobId + '.error');
|
let item = self.dataView.getItemById(job.jobId + '.error');
|
||||||
let noStepsMessage = nls.localize('jobsView.noSteps', 'No Steps available for this job.');
|
let noStepsMessage = nls.localize('jobsView.noSteps', 'No Steps available for this job.');
|
||||||
let errorMessage = jobHistory ? jobHistory.message: noStepsMessage;
|
let errorMessage = jobHistory ? jobHistory.message: noStepsMessage;
|
||||||
item['name'] = item['name'] + ': ' + errorMessage;
|
item['name'] = nls.localize('jobsView.error', 'Error: ') + errorMessage;
|
||||||
self._agentViewComponent.setExpanded(job.jobId, errorMessage);
|
self._agentViewComponent.setExpanded(job.jobId, errorMessage);
|
||||||
self.dataView.updateItem(job.jobId + '.error', item);
|
self.dataView.updateItem(job.jobId + '.error', item);
|
||||||
|
|
||||||
|
|||||||
97
src/sql/parts/modelComponents/button.component.ts
Normal file
97
src/sql/parts/modelComponents/button.component.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||||
|
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
|
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
|
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
|
import { attachButtonStyler } from 'sql/common/theme/styler';
|
||||||
|
import { Button } from 'sql/base/browser/ui/button/button';
|
||||||
|
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'button',
|
||||||
|
template: `
|
||||||
|
<div #input style="width: 100%"></div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export default class ButtonComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
|
@Input() descriptor: IComponentDescriptor;
|
||||||
|
@Input() modelStore: IModelStore;
|
||||||
|
private _button: Button;
|
||||||
|
|
||||||
|
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
|
||||||
|
super(changeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.baseInit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
if (this._inputContainer) {
|
||||||
|
|
||||||
|
|
||||||
|
this._button = new Button(this._inputContainer.nativeElement);
|
||||||
|
|
||||||
|
this._register(this._button);
|
||||||
|
this._register(attachButtonStyler(this._button, this._commonService.themeService, {
|
||||||
|
buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND
|
||||||
|
}));
|
||||||
|
this._register(this._button.onDidClick(e => {
|
||||||
|
this._onEventEmitter.fire({
|
||||||
|
eventType: ComponentEventType.onDidClick,
|
||||||
|
args: e
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.baseDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IComponent implementation
|
||||||
|
|
||||||
|
public layout(): void {
|
||||||
|
this._changeRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLayout(layout: any): void {
|
||||||
|
// TODO allow configuring the look and feel
|
||||||
|
this.layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setProperties(properties: { [key: string]: any; }): void {
|
||||||
|
super.setProperties(properties);
|
||||||
|
this._button.label = this.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSS-bound properties
|
||||||
|
|
||||||
|
private get label(): string {
|
||||||
|
return this.getPropertyOrDefault<sqlops.ButtonProperties, string>((props) => props.label, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
private set label(newValue: string) {
|
||||||
|
this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setValueProperties, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setValueProperties(properties: sqlops.ButtonProperties, label: string): void {
|
||||||
|
properties.label = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,7 +75,7 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
|||||||
return types.isUndefinedOrNull(property) ? defaultVal : property;
|
return types.isUndefinedOrNull(property) ? defaultVal : property;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected setProperty<TPropertyBag, TValue>(propertySetter: (TPropertyBag, TValue) => void, value: TValue) {
|
protected setPropertyFromUI<TPropertyBag, TValue>(propertySetter: (TPropertyBag, TValue) => void, value: TValue) {
|
||||||
propertySetter(this.getProperties<TPropertyBag>(), value);
|
propertySetter(this.getProperties<TPropertyBag>(), value);
|
||||||
this._onEventEmitter.fire({
|
this._onEventEmitter.fire({
|
||||||
eventType: ComponentEventType.PropertiesChanged,
|
eventType: ComponentEventType.PropertiesChanged,
|
||||||
@@ -86,6 +86,12 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
|||||||
public get onEvent(): Event<IComponentEventArgs> {
|
public get onEvent(): Event<IComponentEventArgs> {
|
||||||
return this._onEventEmitter.event;
|
return this._onEventEmitter.event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get title(): string {
|
||||||
|
let properties = this.getProperties();
|
||||||
|
let title = properties['title'];
|
||||||
|
return title ? <string>title : '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class ContainerBase<T> extends ComponentBase {
|
export abstract class ContainerBase<T> extends ComponentBase {
|
||||||
|
|||||||
@@ -4,16 +4,28 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import FlexContainer from './flexContainer.component';
|
import FlexContainer from './flexContainer.component';
|
||||||
|
import FormContainer from './formContainer.component';
|
||||||
import CardComponent from './card.component';
|
import CardComponent from './card.component';
|
||||||
import InputBoxComponent from './inputbox.component';
|
import InputBoxComponent from './inputbox.component';
|
||||||
|
import DropDownComponent from './dropdown.component';
|
||||||
|
import ButtonComponent from './button.component';
|
||||||
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||||
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
export const FLEX_CONTAINER = 'flex-container';
|
export const FLEX_CONTAINER = 'flex-container';
|
||||||
registerComponentType(FLEX_CONTAINER, ModelComponentTypes.FlexContainer, FlexContainer);
|
registerComponentType(FLEX_CONTAINER, ModelComponentTypes.FlexContainer, FlexContainer);
|
||||||
|
|
||||||
|
export const FORM_CONTAINER = 'form-container';
|
||||||
|
registerComponentType(FORM_CONTAINER, ModelComponentTypes.Form, FormContainer);
|
||||||
|
|
||||||
export const CARD_COMPONENT = 'card-component';
|
export const CARD_COMPONENT = 'card-component';
|
||||||
registerComponentType(CARD_COMPONENT, ModelComponentTypes.Card, CardComponent);
|
registerComponentType(CARD_COMPONENT, ModelComponentTypes.Card, CardComponent);
|
||||||
|
|
||||||
export const INPUTBOX_COMPONENT = 'inputbox-component';
|
export const INPUTBOX_COMPONENT = 'inputbox-component';
|
||||||
registerComponentType(INPUTBOX_COMPONENT, ModelComponentTypes.InputBox, InputBoxComponent);
|
registerComponentType(INPUTBOX_COMPONENT, ModelComponentTypes.InputBox, InputBoxComponent);
|
||||||
|
|
||||||
|
export const DROPDOWN_COMPONENT = 'dropdown-component';
|
||||||
|
registerComponentType(DROPDOWN_COMPONENT, ModelComponentTypes.DropDown, DropDownComponent);
|
||||||
|
|
||||||
|
export const BUTTON_COMPONENT = 'button-component';
|
||||||
|
registerComponentType(BUTTON_COMPONENT, ModelComponentTypes.Button, ButtonComponent);
|
||||||
|
|||||||
117
src/sql/parts/modelComponents/dropdown.component.ts
Normal file
117
src/sql/parts/modelComponents/dropdown.component.ts
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||||
|
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
|
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
|
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
|
import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown';
|
||||||
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
|
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'inputBox',
|
||||||
|
template: `
|
||||||
|
<div #input style="width: 100%"></div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export default class DropDownComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
|
@Input() descriptor: IComponentDescriptor;
|
||||||
|
@Input() modelStore: IModelStore;
|
||||||
|
private _dropdown: Dropdown;
|
||||||
|
|
||||||
|
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
|
||||||
|
super(changeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.baseInit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
if (this._inputContainer) {
|
||||||
|
let dropdownOptions: IDropdownOptions = {
|
||||||
|
values: [],
|
||||||
|
strictSelection: false,
|
||||||
|
placeholder: '',
|
||||||
|
maxHeight: 125,
|
||||||
|
ariaLabel: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
this._dropdown = new Dropdown(this._inputContainer.nativeElement, this._commonService.contextViewService, this._commonService.themeService,
|
||||||
|
dropdownOptions);
|
||||||
|
|
||||||
|
this._register(this._dropdown);
|
||||||
|
this._register(attachEditableDropdownStyler(this._dropdown, this._commonService.themeService));
|
||||||
|
this._register(this._dropdown.onValueChange(e => {
|
||||||
|
this.value = this._dropdown.value;
|
||||||
|
this._onEventEmitter.fire({
|
||||||
|
eventType: ComponentEventType.onDidChange,
|
||||||
|
args: e
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.baseDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IComponent implementation
|
||||||
|
|
||||||
|
public layout(): void {
|
||||||
|
this._changeRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLayout(layout: any): void {
|
||||||
|
// TODO allow configuring the look and feel
|
||||||
|
this.layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setProperties(properties: { [key: string]: any; }): void {
|
||||||
|
super.setProperties(properties);
|
||||||
|
this._dropdown.values = this.values ? this.values : [];
|
||||||
|
if (this.value) {
|
||||||
|
this._dropdown.value = this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSS-bound properties
|
||||||
|
|
||||||
|
private get value(): string {
|
||||||
|
return this.getPropertyOrDefault<sqlops.DropDownProperties, string>((props) => props.value, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
private set value(newValue: string) {
|
||||||
|
this.setPropertyFromUI<sqlops.DropDownProperties, string>(this.setValueProperties, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get values(): string[] {
|
||||||
|
return this.getPropertyOrDefault<sqlops.DropDownProperties, string[]>((props) => props.values, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
private set values(newValue: string[]) {
|
||||||
|
this.setPropertyFromUI<sqlops.DropDownProperties, string[]>(this.setValuesProperties, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setValueProperties(properties: sqlops.DropDownProperties, value: string): void {
|
||||||
|
properties.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setValuesProperties(properties: sqlops.DropDownProperties, values: string[]): void {
|
||||||
|
properties.values = values;
|
||||||
|
}
|
||||||
|
}
|
||||||
128
src/sql/parts/modelComponents/formContainer.component.ts
Normal file
128
src/sql/parts/modelComponents/formContainer.component.ts
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import 'vs/css!./formLayout';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||||
|
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
|
import { FormLayout, FormItemLayout } from 'sqlops';
|
||||||
|
|
||||||
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
|
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
|
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
||||||
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
|
|
||||||
|
export interface TitledFormItemLayout {
|
||||||
|
title: string;
|
||||||
|
actions?: string[];
|
||||||
|
isFormComponent: Boolean;
|
||||||
|
}
|
||||||
|
class FormItem {
|
||||||
|
constructor(public descriptor: IComponentDescriptor, public config: TitledFormItemLayout) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<div #container *ngIf="items" class="form-table"
|
||||||
|
[style.alignItems]="alignItems" [style.alignContent]="alignContent">
|
||||||
|
<div *ngFor="let item of items" class="form-row">
|
||||||
|
<ng-container *ngIf="isFormComponent(item)">
|
||||||
|
<div class="form-cell">{{getItemTitle(item)}}</div>
|
||||||
|
<div class="form-cell">
|
||||||
|
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
|
||||||
|
</model-component-wrapper>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="itemHasActions(item)" class="form-cell">
|
||||||
|
<div *ngFor="let actionItem of getActionComponents(item)" >
|
||||||
|
<model-component-wrapper [descriptor]="actionItem.descriptor" [modelStore]="modelStore">
|
||||||
|
</model-component-wrapper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export default class FormContainer extends ContainerBase<FormItemLayout> implements IComponent, OnDestroy, AfterViewInit {
|
||||||
|
@Input() descriptor: IComponentDescriptor;
|
||||||
|
@Input() modelStore: IModelStore;
|
||||||
|
|
||||||
|
private _alignItems: string;
|
||||||
|
private _alignContent: string;
|
||||||
|
|
||||||
|
@ViewChildren(ModelComponentWrapper) private _componentWrappers: QueryList<ModelComponentWrapper>;
|
||||||
|
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
@Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
|
||||||
|
super(changeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.baseInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.baseDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IComponent implementation
|
||||||
|
|
||||||
|
public layout(): void {
|
||||||
|
if (this._componentWrappers) {
|
||||||
|
this._componentWrappers.forEach(wrapper => {
|
||||||
|
wrapper.layout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get alignItems(): string {
|
||||||
|
return this._alignItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get alignContent(): string {
|
||||||
|
return this._alignContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getItemTitle(item: FormItem): string {
|
||||||
|
let itemConfig = item.config;
|
||||||
|
return itemConfig ? itemConfig.title : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private getActionComponents(item: FormItem): FormItem[]{
|
||||||
|
let items = this.items;
|
||||||
|
let itemConfig = item.config;
|
||||||
|
if (itemConfig && itemConfig.actions) {
|
||||||
|
let resultItems = itemConfig.actions.map(x => {
|
||||||
|
let actionComponent = items.find(i => i.descriptor.id === x);
|
||||||
|
return <FormItem>actionComponent;
|
||||||
|
});
|
||||||
|
|
||||||
|
return resultItems.filter(r => r && r.descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private isFormComponent(item: FormItem): Boolean {
|
||||||
|
return item && item.config && item.config.isFormComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private itemHasActions(item: FormItem): Boolean {
|
||||||
|
let itemConfig = item.config;
|
||||||
|
return itemConfig && itemConfig.actions !== undefined && itemConfig.actions.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLayout(layout: any): void {
|
||||||
|
this.layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/sql/parts/modelComponents/formLayout.css
Normal file
20
src/sql/parts/modelComponents/formLayout.css
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
.form-table {
|
||||||
|
width:400px;
|
||||||
|
display:table;
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: table-row;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-cell {
|
||||||
|
padding: 5px;
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-action {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
import {
|
||||||
|
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||||
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
|
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
|||||||
this._changeRef.detectChanges();
|
this._changeRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setLayout (layout: any): void {
|
public setLayout(layout: any): void {
|
||||||
// TODO allow configuring the look and feel
|
// TODO allow configuring the look and feel
|
||||||
this.layout();
|
this.layout();
|
||||||
}
|
}
|
||||||
@@ -87,7 +88,7 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
|||||||
}
|
}
|
||||||
|
|
||||||
public set value(newValue: string) {
|
public set value(newValue: string) {
|
||||||
this.setProperty<sqlops.InputBoxProperties, string>(this.setInputBoxProperties, newValue);
|
this.setPropertyFromUI<sqlops.InputBoxProperties, string>(this.setInputBoxProperties, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setInputBoxProperties(properties: sqlops.InputBoxProperties, value: string): void {
|
private setInputBoxProperties(properties: sqlops.InputBoxProperties, value: string): void {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export interface IComponent {
|
|||||||
addToContainer?: (componentDescriptor: IComponentDescriptor, config: any) => void;
|
addToContainer?: (componentDescriptor: IComponentDescriptor, config: any) => void;
|
||||||
setLayout?: (layout: any) => void;
|
setLayout?: (layout: any) => void;
|
||||||
setProperties?: (properties: { [key: string]: any; }) => void;
|
setProperties?: (properties: { [key: string]: any; }) => void;
|
||||||
|
title?: string;
|
||||||
onEvent?: Event<IComponentEventArgs>;
|
onEvent?: Event<IComponentEventArgs>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,11 +54,13 @@ export interface IComponentDescriptor {
|
|||||||
export interface IComponentEventArgs {
|
export interface IComponentEventArgs {
|
||||||
eventType: ComponentEventType;
|
eventType: ComponentEventType;
|
||||||
args: any;
|
args: any;
|
||||||
|
componentId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ComponentEventType {
|
export enum ComponentEventType {
|
||||||
PropertiesChanged,
|
PropertiesChanged,
|
||||||
onDidChange
|
onDidChange,
|
||||||
|
onDidClick
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IModelStore {
|
export interface IModelStore {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||||||
import nls = require('vs/nls');
|
import nls = require('vs/nls');
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { IModelStore, IComponentDescriptor, IComponent } from './interfaces';
|
import { IModelStore, IComponentDescriptor, IComponent, IComponentEventArgs } from './interfaces';
|
||||||
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
import { IModelView } from 'sql/services/model/modelViewService';
|
import { IModelView } from 'sql/services/model/modelViewService';
|
||||||
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||||
@@ -18,7 +18,7 @@ import { AngularDisposable } from 'sql/base/common/lifecycle';
|
|||||||
import { ModelStore } from 'sql/parts/modelComponents/modelStore';
|
import { ModelStore } from 'sql/parts/modelComponents/modelStore';
|
||||||
import Event, { Emitter } from 'vs/base/common/event';
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
const componentRegistry = <IComponentRegistry> Registry.as(Extensions.ComponentContribution);
|
const componentRegistry = <IComponentRegistry>Registry.as(Extensions.ComponentContribution);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides common logic required for any implementation that hooks to a model provided by
|
* Provides common logic required for any implementation that hooks to a model provided by
|
||||||
@@ -57,7 +57,7 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
this.setLayout(component.id, component.layout);
|
this.setLayout(component.id, component.layout);
|
||||||
this.registerEvent(component.id);
|
this.registerEvent(component.id);
|
||||||
if (component.itemConfigs) {
|
if (component.itemConfigs) {
|
||||||
for(let item of component.itemConfigs) {
|
for (let item of component.itemConfigs) {
|
||||||
this.addToContainer(component.id, item);
|
this.addToContainer(component.id, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,12 +66,12 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearContainer(componentId: string): void {
|
clearContainer(componentId: string): void {
|
||||||
this.queueAction(componentId, (component) => component.clearContainer());
|
this.queueAction(componentId, (component) => component.clearContainer());
|
||||||
}
|
}
|
||||||
|
|
||||||
addToContainer(containerId: string, itemConfig: IItemConfig): void {
|
addToContainer(containerId: string, itemConfig: IItemConfig): void {
|
||||||
// Do not return the promise as this should be non-blocking
|
// Do not return the promise as this should be non-blocking
|
||||||
this.queueAction(containerId, (component) => {
|
this.queueAction(containerId, (component) => {
|
||||||
let childDescriptor = this.defineComponent(itemConfig.componentShape);
|
let childDescriptor = this.defineComponent(itemConfig.componentShape);
|
||||||
component.addToContainer(childDescriptor, itemConfig.config);
|
component.addToContainer(childDescriptor, itemConfig.config);
|
||||||
});
|
});
|
||||||
@@ -81,14 +81,14 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
if (!layout) {
|
if (!layout) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.queueAction(componentId, (component) => component.setLayout(layout));
|
this.queueAction(componentId, (component) => component.setLayout(layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
setProperties(componentId: string, properties: { [key: string]: any; }): void {
|
setProperties(componentId: string, properties: { [key: string]: any; }): void {
|
||||||
if (!properties) {
|
if (!properties) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.queueAction(componentId, (component) => component.setProperties(properties));
|
this.queueAction(componentId, (component) => component.setProperties(properties));
|
||||||
}
|
}
|
||||||
|
|
||||||
private queueAction<T>(componentId: string, action: (component: IComponent) => T): void {
|
private queueAction<T>(componentId: string, action: (component: IComponent) => T): void {
|
||||||
@@ -98,16 +98,17 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerEvent(componentId: string) {
|
registerEvent(componentId: string) {
|
||||||
this.queueAction(componentId, (component) => {
|
this.queueAction(componentId, (component) => {
|
||||||
if (component.onEvent) {
|
if (component.onEvent) {
|
||||||
this._register(component.onEvent(e => {
|
this._register(component.onEvent(e => {
|
||||||
|
e.componentId = componentId;
|
||||||
this._onEventEmitter.fire(e);
|
this._onEventEmitter.fire(e);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get onEvent(): Event<any> {
|
public get onEvent(): Event<IComponentEventArgs> {
|
||||||
return this._onEventEmitter.event;
|
return this._onEventEmitter.event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ actionRegistry.registerWorkbenchAction(
|
|||||||
FocusOnCurrentQueryKeyboardAction,
|
FocusOnCurrentQueryKeyboardAction,
|
||||||
FocusOnCurrentQueryKeyboardAction.ID,
|
FocusOnCurrentQueryKeyboardAction.ID,
|
||||||
FocusOnCurrentQueryKeyboardAction.LABEL,
|
FocusOnCurrentQueryKeyboardAction.LABEL,
|
||||||
{ primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_Q }
|
{ primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_O }
|
||||||
),
|
),
|
||||||
FocusOnCurrentQueryKeyboardAction.LABEL
|
FocusOnCurrentQueryKeyboardAction.LABEL
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,14 +12,24 @@ import { Dialog } from 'sql/platform/dialog/dialogTypes';
|
|||||||
import { IModalOptions } from 'sql/base/browser/ui/modal/modal';
|
import { IModalOptions } from 'sql/base/browser/ui/modal/modal';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
|
||||||
const defaultOptions: IModalOptions = { hasBackButton: true, isWide: true };
|
const defaultOptions: IModalOptions = { hasBackButton: true, isWide: false };
|
||||||
|
|
||||||
export class CustomDialogService {
|
export class CustomDialogService {
|
||||||
|
private _dialogModals = new Map<Dialog, DialogModal>();
|
||||||
|
|
||||||
constructor( @IInstantiationService private _instantiationService: IInstantiationService) { }
|
constructor( @IInstantiationService private _instantiationService: IInstantiationService) { }
|
||||||
|
|
||||||
public showDialog(dialog: Dialog, options?: IModalOptions): void {
|
public showDialog(dialog: Dialog, options?: IModalOptions): void {
|
||||||
let optionsDialog = this._instantiationService.createInstance(DialogModal, dialog, 'CustomDialog', options || defaultOptions);
|
let dialogModal = this._instantiationService.createInstance(DialogModal, dialog, 'CustomDialog', options || defaultOptions);
|
||||||
optionsDialog.render();
|
this._dialogModals.set(dialog, dialogModal);
|
||||||
optionsDialog.open();
|
dialogModal.render();
|
||||||
|
dialogModal.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(dialog: Dialog): void {
|
||||||
|
let dialogModal = this._dialogModals.get(dialog);
|
||||||
|
if (dialogModal) {
|
||||||
|
dialogModal.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import 'vs/css!./media/dialogModal';
|
import 'vs/css!./media/dialogModal';
|
||||||
import { Modal, IModalOptions } from 'sql/base/browser/ui/modal/modal';
|
import { Modal, IModalOptions } from 'sql/base/browser/ui/modal/modal';
|
||||||
import { attachModalDialogStyler } from 'sql/common/theme/styler';
|
import { attachModalDialogStyler } from 'sql/common/theme/styler';
|
||||||
import { Dialog } from 'sql/platform/dialog/dialogTypes';
|
import { Dialog, DialogButton } from 'sql/platform/dialog/dialogTypes';
|
||||||
import { DialogPane } from 'sql/platform/dialog/dialogPane';
|
import { DialogPane } from 'sql/platform/dialog/dialogPane';
|
||||||
import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService';
|
import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService';
|
||||||
import { Builder } from 'vs/base/browser/builder';
|
import { Builder } from 'vs/base/browser/builder';
|
||||||
@@ -23,9 +23,6 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
|||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
|
|
||||||
export class DialogModal extends Modal {
|
export class DialogModal extends Modal {
|
||||||
private static readonly DONE_BUTTON_LABEL = localize('dialogModalDoneButtonLabel', 'Done');
|
|
||||||
private static readonly CANCEL_BUTTON_LABEL = localize('dialogModalCancelButtonLabel', 'Cancel');
|
|
||||||
|
|
||||||
private _dialogPane: DialogPane;
|
private _dialogPane: DialogPane;
|
||||||
|
|
||||||
// Wizard HTML elements
|
// Wizard HTML elements
|
||||||
@@ -61,10 +58,34 @@ export class DialogModal extends Modal {
|
|||||||
attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND });
|
attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND });
|
||||||
}
|
}
|
||||||
|
|
||||||
this._cancelButton = this.addFooterButton(DialogModal.CANCEL_BUTTON_LABEL, () => this.cancel());
|
if (this._dialog.customButtons) {
|
||||||
this._doneButton = this.addFooterButton(DialogModal.DONE_BUTTON_LABEL, () => this.done());
|
this._dialog.customButtons.forEach(button => {
|
||||||
attachButtonStyler(this._cancelButton, this._themeService);
|
let buttonElement = this.addDialogButton(button);
|
||||||
attachButtonStyler(this._doneButton, this._themeService);
|
this.updateButtonElement(buttonElement, button);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cancelButton = this.addDialogButton(this._dialog.cancelButton, () => this.cancel());
|
||||||
|
this.updateButtonElement(this._cancelButton, this._dialog.cancelButton);
|
||||||
|
this._doneButton = this.addDialogButton(this._dialog.okButton, () => this.done());
|
||||||
|
this.updateButtonElement(this._doneButton, this._dialog.okButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined): Button {
|
||||||
|
let buttonElement = this.addFooterButton(button.label, onSelect);
|
||||||
|
buttonElement.enabled = button.enabled;
|
||||||
|
button.registerClickEvent(buttonElement.onDidClick);
|
||||||
|
button.onUpdate(() => {
|
||||||
|
this.updateButtonElement(buttonElement, button);
|
||||||
|
});
|
||||||
|
attachButtonStyler(buttonElement, this._themeService);
|
||||||
|
return buttonElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateButtonElement(buttonElement: Button, dialogButton: DialogButton) {
|
||||||
|
buttonElement.label = dialogButton.label;
|
||||||
|
buttonElement.enabled = dialogButton.enabled;
|
||||||
|
dialogButton.hidden ? buttonElement.element.classList.add('dialogModal-hidden') : buttonElement.element.classList.remove('dialogModal-hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderBody(container: HTMLElement): void {
|
protected renderBody(container: HTMLElement): void {
|
||||||
|
|||||||
@@ -92,4 +92,9 @@ export class DialogPane extends Disposable implements IThemable {
|
|||||||
this._body.style.backgroundColor = styles.dialogBodyBackground ? styles.dialogBodyBackground.toString() : undefined;
|
this._body.style.backgroundColor = styles.dialogBodyBackground ? styles.dialogBodyBackground.toString() : undefined;
|
||||||
this._body.style.color = styles.dialogForeground ? styles.dialogForeground.toString() : undefined;
|
this._body.style.color = styles.dialogForeground ? styles.dialogForeground.toString() : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
super.dispose();
|
||||||
|
this._moduleRef.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
import Event, { Emitter } from 'vs/base/common/event';
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
export class DialogTab implements sqlops.window.modelviewdialog.DialogTab {
|
export class DialogTab implements sqlops.window.modelviewdialog.DialogTab {
|
||||||
@@ -16,40 +17,70 @@ export class DialogTab implements sqlops.window.modelviewdialog.DialogTab {
|
|||||||
this.content = content;
|
this.content = content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateContent(): void { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Dialog implements sqlops.window.modelviewdialog.Dialog {
|
export class Dialog implements sqlops.window.modelviewdialog.Dialog {
|
||||||
public content: string | DialogTab[];
|
private static readonly DONE_BUTTON_LABEL = localize('dialogModalDoneButtonLabel', 'Done');
|
||||||
public okTitle: string;
|
private static readonly CANCEL_BUTTON_LABEL = localize('dialogModalCancelButtonLabel', 'Cancel');
|
||||||
public cancelTitle: string;
|
|
||||||
public customButtons: DialogButton[];
|
|
||||||
|
|
||||||
private _onOk: Emitter<void> = new Emitter<void>();
|
public content: string | DialogTab[];
|
||||||
public readonly onOk: Event<void> = this._onOk.event;
|
public okButton: DialogButton = new DialogButton(Dialog.DONE_BUTTON_LABEL, true);
|
||||||
private _onCancel: Emitter<void> = new Emitter<void>();
|
public cancelButton: DialogButton = new DialogButton(Dialog.CANCEL_BUTTON_LABEL, true);
|
||||||
public readonly onCancel: Event<void> = this._onCancel.event;
|
public customButtons: DialogButton[];
|
||||||
|
|
||||||
constructor(public title: string, content?: string | DialogTab[]) {
|
constructor(public title: string, content?: string | DialogTab[]) {
|
||||||
if (content) {
|
if (content) {
|
||||||
this.content = content;
|
this.content = content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public open(): void { }
|
|
||||||
public close(): void { }
|
|
||||||
public updateContent(): void { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DialogButton implements sqlops.window.modelviewdialog.Button {
|
export class DialogButton implements sqlops.window.modelviewdialog.Button {
|
||||||
public label: string;
|
private _label: string;
|
||||||
public enabled: boolean;
|
private _enabled: boolean;
|
||||||
|
private _hidden: boolean;
|
||||||
private _onClick: Emitter<void> = new Emitter<void>();
|
private _onClick: Emitter<void> = new Emitter<void>();
|
||||||
public readonly onClick: Event<void> = this._onClick.event;
|
public readonly onClick: Event<void> = this._onClick.event;
|
||||||
|
private _onUpdate: Emitter<void> = new Emitter<void>();
|
||||||
|
public readonly onUpdate: Event<void> = this._onUpdate.event;
|
||||||
|
|
||||||
constructor(label: string, enabled: boolean) {
|
constructor(label: string, enabled: boolean) {
|
||||||
this.label = label;
|
this._label = label;
|
||||||
this.enabled = enabled;
|
this._enabled = enabled;
|
||||||
|
this._hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get label(): string {
|
||||||
|
return this._label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set label(label: string) {
|
||||||
|
this._label = label;
|
||||||
|
this._onUpdate.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get enabled(): boolean {
|
||||||
|
return this._enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set enabled(enabled: boolean) {
|
||||||
|
this._enabled = enabled;
|
||||||
|
this._onUpdate.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get hidden(): boolean {
|
||||||
|
return this._hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set hidden(hidden: boolean) {
|
||||||
|
this._hidden = hidden;
|
||||||
|
this._onUpdate.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an event that notifies the button that it has been clicked
|
||||||
|
*/
|
||||||
|
public registerClickEvent(clickEvent: Event<void>): void {
|
||||||
|
clickEvent(() => this._onClick.fire());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,6 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialogModal-pane.dialogModal-hidden {
|
.dialogModal-hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
120
src/sql/sqlops.proposed.d.ts
vendored
120
src/sql/sqlops.proposed.d.ts
vendored
@@ -20,23 +20,30 @@ declare module 'sqlops' {
|
|||||||
flexContainer(): FlexBuilder;
|
flexContainer(): FlexBuilder;
|
||||||
card(): ComponentBuilder<CardComponent>;
|
card(): ComponentBuilder<CardComponent>;
|
||||||
inputBox(): ComponentBuilder<InputBoxComponent>;
|
inputBox(): ComponentBuilder<InputBoxComponent>;
|
||||||
|
button(): ComponentBuilder<ButtonComponent>;
|
||||||
|
dropDown(): ComponentBuilder<DropDownComponent>;
|
||||||
dashboardWidget(widgetId: string): ComponentBuilder<WidgetComponent>;
|
dashboardWidget(widgetId: string): ComponentBuilder<WidgetComponent>;
|
||||||
dashboardWebview(webviewId: string): ComponentBuilder<WebviewComponent>;
|
dashboardWebview(webviewId: string): ComponentBuilder<WebviewComponent>;
|
||||||
|
formContainer(): FormBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ComponentBuilder<T extends Component> {
|
export interface ComponentBuilder<T extends Component> {
|
||||||
component(): T;
|
component(): T;
|
||||||
withProperties<U>(properties: U): ComponentBuilder<T>;
|
withProperties<U>(properties: U): ComponentBuilder<T>;
|
||||||
}
|
}
|
||||||
export interface ContainerBuilder<T extends Component, TLayout,TItemLayout> extends ComponentBuilder<T> {
|
export interface ContainerBuilder<T extends Component, TLayout, TItemLayout> extends ComponentBuilder<T> {
|
||||||
withLayout(layout: TLayout): ContainerBuilder<T, TLayout, TItemLayout>;
|
withLayout(layout: TLayout): ContainerBuilder<T, TLayout, TItemLayout>;
|
||||||
withItems(components: Array<Component>, itemLayout ?: TItemLayout): ContainerBuilder<T, TLayout, TItemLayout>;
|
withItems(components: Array<Component>, itemLayout?: TItemLayout): ContainerBuilder<T, TLayout, TItemLayout>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FlexBuilder extends ContainerBuilder<FlexContainer, FlexLayout, FlexItemLayout> {
|
export interface FlexBuilder extends ContainerBuilder<FlexContainer, FlexLayout, FlexItemLayout> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FormBuilder extends ContainerBuilder<FormContainer, FormLayout, FormItemLayout> {
|
||||||
|
withFormItems(components: FormComponent[], itemLayout?: FormItemLayout): ContainerBuilder<FormContainer, FormLayout, FormItemLayout>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Component {
|
export interface Component {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
|
|
||||||
@@ -50,10 +57,16 @@ declare module 'sqlops' {
|
|||||||
updateProperties(properties: { [key: string]: any }): Thenable<boolean>;
|
updateProperties(properties: { [key: string]: any }): Thenable<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FormComponent {
|
||||||
|
component: Component;
|
||||||
|
title: string;
|
||||||
|
actions?: Component[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component that contains other components
|
* A component that contains other components
|
||||||
*/
|
*/
|
||||||
export interface Container<TLayout,TItemLayout> extends Component {
|
export interface Container<TLayout, TItemLayout> extends Component {
|
||||||
/**
|
/**
|
||||||
* A copy of the child items array. This cannot be added to directly -
|
* A copy of the child items array. This cannot be added to directly -
|
||||||
* components must be created using the create methods instead
|
* components must be created using the create methods instead
|
||||||
@@ -70,7 +83,7 @@ declare module 'sqlops' {
|
|||||||
* @param itemConfigs the definitions
|
* @param itemConfigs the definitions
|
||||||
* @param {*} [itemLayout] Optional layout for the child items
|
* @param {*} [itemLayout] Optional layout for the child items
|
||||||
*/
|
*/
|
||||||
addItems(itemConfigs: Array<Component>, itemLayout ?: TItemLayout): void;
|
addItems(itemConfigs: Array<Component>, itemLayout?: TItemLayout): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a child component and adds it to this container.
|
* Creates a child component and adds it to this container.
|
||||||
@@ -78,7 +91,7 @@ declare module 'sqlops' {
|
|||||||
* @param {Component} component the component to be added
|
* @param {Component} component the component to be added
|
||||||
* @param {*} [itemLayout] Optional layout for this child item
|
* @param {*} [itemLayout] Optional layout for this child item
|
||||||
*/
|
*/
|
||||||
addItem(component: Component, itemLayout ?: TItemLayout): void;
|
addItem(component: Component, itemLayout?: TItemLayout): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the layout for this container
|
* Defines the layout for this container
|
||||||
@@ -130,9 +143,21 @@ declare module 'sqlops' {
|
|||||||
flex?: string;
|
flex?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FormItemLayout {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FormLayout {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export interface FlexContainer extends Container<FlexLayout, FlexItemLayout> {
|
export interface FlexContainer extends Container<FlexLayout, FlexItemLayout> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FormContainer extends Container<FormLayout, FormItemLayout> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes an action to be shown in the UI, with a user-readable label
|
* Describes an action to be shown in the UI, with a user-readable label
|
||||||
* and a callback to execute the action
|
* and a callback to execute the action
|
||||||
@@ -153,16 +178,25 @@ declare module 'sqlops' {
|
|||||||
* Properties representing the card component, can be used
|
* Properties representing the card component, can be used
|
||||||
* when using ModelBuilder to create the component
|
* when using ModelBuilder to create the component
|
||||||
*/
|
*/
|
||||||
export interface CardProperties {
|
export interface CardProperties {
|
||||||
label: string;
|
label: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
actions?: ActionDescriptor[];
|
actions?: ActionDescriptor[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InputBoxProperties {
|
export interface InputBoxProperties {
|
||||||
value?: string;
|
value?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DropDownProperties {
|
||||||
|
value?: string;
|
||||||
|
values?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ButtonProperties {
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CardComponent extends Component {
|
export interface CardComponent extends Component {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
@@ -174,6 +208,17 @@ declare module 'sqlops' {
|
|||||||
onTextChanged: vscode.Event<any>;
|
onTextChanged: vscode.Event<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DropDownComponent extends Component {
|
||||||
|
value: string;
|
||||||
|
values: string[];
|
||||||
|
onValueChanged: vscode.Event<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ButtonComponent extends Component {
|
||||||
|
label: string;
|
||||||
|
onDidClick: vscode.Event<any>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface WidgetComponent extends Component {
|
export interface WidgetComponent extends Component {
|
||||||
widgetId: string;
|
widgetId: string;
|
||||||
}
|
}
|
||||||
@@ -242,6 +287,16 @@ declare module 'sqlops' {
|
|||||||
*/
|
*/
|
||||||
export function createButton(label: string): Button;
|
export function createButton(label: string): Button;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given dialog if it is not already open
|
||||||
|
*/
|
||||||
|
export function openDialog(dialog: Dialog): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the given dialog if it is open
|
||||||
|
*/
|
||||||
|
export function closeDialog(dialog: Dialog): void;
|
||||||
|
|
||||||
// Model view dialog classes
|
// Model view dialog classes
|
||||||
export interface Dialog {
|
export interface Dialog {
|
||||||
/**
|
/**
|
||||||
@@ -252,79 +307,52 @@ declare module 'sqlops' {
|
|||||||
/**
|
/**
|
||||||
* The content of the dialog. If multiple tabs are given they will be displayed with tabs
|
* The content of the dialog. If multiple tabs are given they will be displayed with tabs
|
||||||
* If a string is given, it should be the ID of the dialog's model view content
|
* If a string is given, it should be the ID of the dialog's model view content
|
||||||
* TODO mairvine 4/18/18: use a model view content type
|
|
||||||
*/
|
*/
|
||||||
content: string | DialogTab[],
|
content: string | DialogTab[],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The caption of the OK button
|
* The ok button
|
||||||
*/
|
*/
|
||||||
okTitle: string;
|
okButton: Button;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The caption of the Cancel button
|
* The cancel button
|
||||||
*/
|
*/
|
||||||
cancelTitle: string;
|
cancelButton: Button;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any additional buttons that should be displayed
|
* Any additional buttons that should be displayed
|
||||||
*/
|
*/
|
||||||
customButtons: Button[];
|
customButtons: Button[];
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the dialog
|
|
||||||
*/
|
|
||||||
open(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the dialog
|
|
||||||
*/
|
|
||||||
close(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the dialog on screen to reflect changes to the buttons or content
|
|
||||||
*/
|
|
||||||
updateContent(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Raised when dialog's ok button is pressed
|
|
||||||
*/
|
|
||||||
readonly onOk: vscode.Event<void>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Raised when dialog is canceled
|
|
||||||
*/
|
|
||||||
readonly onCancel: vscode.Event<void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DialogTab {
|
export interface DialogTab {
|
||||||
/**
|
/**
|
||||||
* The title of the tab
|
* The title of the tab
|
||||||
*/
|
*/
|
||||||
title: string,
|
title: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string giving the ID of the tab's model view content
|
* A string giving the ID of the tab's model view content
|
||||||
* TODO mairvine 4/18/18: use a model view content type
|
|
||||||
*/
|
*/
|
||||||
content: string;
|
content: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the dialog on screen to reflect changes to the content
|
|
||||||
*/
|
|
||||||
updateContent(): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Button {
|
export interface Button {
|
||||||
/**
|
/**
|
||||||
* The label displayed on the button
|
* The label displayed on the button
|
||||||
*/
|
*/
|
||||||
label: string,
|
label: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the button is enabled
|
* Whether the button is enabled
|
||||||
*/
|
*/
|
||||||
enabled: boolean,
|
enabled: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the button is hidden
|
||||||
|
*/
|
||||||
|
hidden: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raised when the button is clicked
|
* Raised when the button is clicked
|
||||||
|
|||||||
@@ -68,14 +68,17 @@ export enum ModelComponentTypes {
|
|||||||
FlexContainer,
|
FlexContainer,
|
||||||
Card,
|
Card,
|
||||||
InputBox,
|
InputBox,
|
||||||
|
DropDown,
|
||||||
|
Button,
|
||||||
DashboardWidget,
|
DashboardWidget,
|
||||||
DashboardWebview
|
DashboardWebview,
|
||||||
|
Form
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IComponentShape {
|
export interface IComponentShape {
|
||||||
type: ModelComponentTypes;
|
type: ModelComponentTypes;
|
||||||
id: string;
|
id: string;
|
||||||
properties?: { [key: string]: any };
|
properties?: { [key: string]: any };
|
||||||
layout?: any;
|
layout?: any;
|
||||||
itemConfigs?: IItemConfig[];
|
itemConfigs?: IItemConfig[];
|
||||||
}
|
}
|
||||||
@@ -87,10 +90,30 @@ export interface IItemConfig {
|
|||||||
|
|
||||||
export enum ComponentEventType {
|
export enum ComponentEventType {
|
||||||
PropertiesChanged,
|
PropertiesChanged,
|
||||||
onDidChange
|
onDidChange,
|
||||||
|
onDidClick
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IComponentEventArgs {
|
export interface IComponentEventArgs {
|
||||||
eventType: ComponentEventType;
|
eventType: ComponentEventType;
|
||||||
args: any;
|
args: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IModelViewDialogDetails {
|
||||||
|
title: string;
|
||||||
|
content: string | number[];
|
||||||
|
okButton: number;
|
||||||
|
cancelButton: number;
|
||||||
|
customButtons: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IModelViewTabDetails {
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IModelViewButtonDetails {
|
||||||
|
label: string;
|
||||||
|
enabled: boolean;
|
||||||
|
hidden: boolean;
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
|||||||
return new ContainerBuilderImpl<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
|
return new ContainerBuilderImpl<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formContainer(): sqlops.FormBuilder {
|
||||||
|
let id = this.getNextComponentId();
|
||||||
|
return new FormContainerBuilder(this._proxy, this._handle, ModelComponentTypes.Form, id);
|
||||||
|
}
|
||||||
|
|
||||||
card(): sqlops.ComponentBuilder<sqlops.CardComponent> {
|
card(): sqlops.ComponentBuilder<sqlops.CardComponent> {
|
||||||
let id = this.getNextComponentId();
|
let id = this.getNextComponentId();
|
||||||
return this.withEventHandler(new CardWrapper(this._proxy, this._handle, id), id);
|
return this.withEventHandler(new CardWrapper(this._proxy, this._handle, id), id);
|
||||||
@@ -43,6 +48,16 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
|||||||
return this.withEventHandler(new InputBoxWrapper(this._proxy, this._handle, id), id);
|
return this.withEventHandler(new InputBoxWrapper(this._proxy, this._handle, id), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button(): sqlops.ComponentBuilder<sqlops.ButtonComponent> {
|
||||||
|
let id = this.getNextComponentId();
|
||||||
|
return this.withEventHandler(new ButtonWrapper(this._proxy, this._handle, id), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
dropDown(): sqlops.ComponentBuilder<sqlops.DropDownComponent> {
|
||||||
|
let id = this.getNextComponentId();
|
||||||
|
return this.withEventHandler(new DropDownWrapper(this._proxy, this._handle, id), id);
|
||||||
|
}
|
||||||
|
|
||||||
dashboardWidget(widgetId: string): sqlops.ComponentBuilder<sqlops.WidgetComponent> {
|
dashboardWidget(widgetId: string): sqlops.ComponentBuilder<sqlops.WidgetComponent> {
|
||||||
let id = this.getNextComponentId();
|
let id = this.getNextComponentId();
|
||||||
return this.withEventHandler<sqlops.WidgetComponent>(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id), id);
|
return this.withEventHandler<sqlops.WidgetComponent>(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id), id);
|
||||||
@@ -122,9 +137,40 @@ class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> ext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> {
|
||||||
|
|
||||||
|
withFormItems(components: sqlops.FormComponent[], itemLayout?: sqlops.FormItemLayout): sqlops.ContainerBuilder<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> {
|
||||||
|
|
||||||
|
this._component.itemConfigs = components.map(item => {
|
||||||
|
let componentWrapper = item.component as ComponentWrapper;
|
||||||
|
let actions: string[] = undefined;
|
||||||
|
if (item.actions) {
|
||||||
|
actions = item.actions.map(action => {
|
||||||
|
let actionComponentWrapper = action as ComponentWrapper;
|
||||||
|
return actionComponentWrapper.id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new InternalItemConfig(componentWrapper, Object.assign({}, itemLayout, {
|
||||||
|
title: item.title,
|
||||||
|
actions: actions,
|
||||||
|
isFormComponent: true
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
components.forEach(formItem => {
|
||||||
|
if (formItem.actions) {
|
||||||
|
formItem.actions.forEach(component => {
|
||||||
|
let componentWrapper = component as ComponentWrapper;
|
||||||
|
this._component.itemConfigs.push(new InternalItemConfig(componentWrapper, itemLayout));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class InternalItemConfig {
|
class InternalItemConfig {
|
||||||
constructor(private _component: ComponentWrapper, public config: any) {}
|
constructor(private _component: ComponentWrapper, public config: any) { }
|
||||||
|
|
||||||
public toIItemConfig(): IItemConfig {
|
public toIItemConfig(): IItemConfig {
|
||||||
return {
|
return {
|
||||||
@@ -146,6 +192,7 @@ class ComponentWrapper implements sqlops.Component {
|
|||||||
|
|
||||||
private _onErrorEmitter = new Emitter<Error>();
|
private _onErrorEmitter = new Emitter<Error>();
|
||||||
public readonly onError: vscode.Event<Error> = this._onErrorEmitter.event;
|
public readonly onError: vscode.Event<Error> = this._onErrorEmitter.event;
|
||||||
|
protected _emitterMap = new Map<ComponentEventType, Emitter<any>>();
|
||||||
|
|
||||||
constructor(protected readonly _proxy: MainThreadModelViewShape,
|
constructor(protected readonly _proxy: MainThreadModelViewShape,
|
||||||
protected readonly _handle: number,
|
protected readonly _handle: number,
|
||||||
@@ -169,7 +216,7 @@ class ComponentWrapper implements sqlops.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public toComponentShape(): IComponentShape {
|
public toComponentShape(): IComponentShape {
|
||||||
return <IComponentShape> {
|
return <IComponentShape>{
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
layout: this.layout,
|
layout: this.layout,
|
||||||
@@ -183,13 +230,13 @@ class ComponentWrapper implements sqlops.Component {
|
|||||||
return this._proxy.$clearContainer(this._handle, this.id);
|
return this._proxy.$clearContainer(this._handle, this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addItems(items: Array<sqlops.Component>, itemLayout ?: any): void {
|
public addItems(items: Array<sqlops.Component>, itemLayout?: any): void {
|
||||||
for(let item of items) {
|
for (let item of items) {
|
||||||
this.addItem(item, itemLayout);
|
this.addItem(item, itemLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public addItem(item: sqlops.Component, itemLayout ?: any): void {
|
public addItem(item: sqlops.Component, itemLayout?: any): void {
|
||||||
let itemImpl = item as ComponentWrapper;
|
let itemImpl = item as ComponentWrapper;
|
||||||
if (!itemImpl) {
|
if (!itemImpl) {
|
||||||
throw new Error(nls.localize('unknownComponentType', 'Unkown component type. Must use ModelBuilder to create objects'));
|
throw new Error(nls.localize('unknownComponentType', 'Unkown component type. Must use ModelBuilder to create objects'));
|
||||||
@@ -218,7 +265,12 @@ class ComponentWrapper implements sqlops.Component {
|
|||||||
public onEvent(eventArgs: IComponentEventArgs) {
|
public onEvent(eventArgs: IComponentEventArgs) {
|
||||||
if (eventArgs && eventArgs.eventType === ComponentEventType.PropertiesChanged) {
|
if (eventArgs && eventArgs.eventType === ComponentEventType.PropertiesChanged) {
|
||||||
this.properties = eventArgs.args;
|
this.properties = eventArgs.args;
|
||||||
}
|
} else if (eventArgs) {
|
||||||
|
let emitter = this._emitterMap.get(eventArgs.eventType);
|
||||||
|
if (emitter) {
|
||||||
|
emitter.fire();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected setProperty(key: string, value: any): Thenable<boolean> {
|
protected setProperty(key: string, value: any): Thenable<boolean> {
|
||||||
@@ -278,9 +330,6 @@ class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxCompone
|
|||||||
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
|
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onTextChangedEmitter = new Emitter<any>();
|
|
||||||
private _emitterMap = new Map<ComponentEventType, Emitter<any>>();
|
|
||||||
|
|
||||||
public get value(): string {
|
public get value(): string {
|
||||||
return this.properties['value'];
|
return this.properties['value'];
|
||||||
}
|
}
|
||||||
@@ -292,15 +341,54 @@ class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxCompone
|
|||||||
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
|
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
|
||||||
return emitter && emitter.event;
|
return emitter && emitter.event;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public onEvent(eventArgs: IComponentEventArgs) {
|
class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownComponent {
|
||||||
super.onEvent(eventArgs);
|
|
||||||
if (eventArgs) {
|
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
|
||||||
let emitter = this._emitterMap.get(eventArgs.eventType);
|
super(proxy, handle, ModelComponentTypes.DropDown, id);
|
||||||
if (emitter) {
|
this.properties = {};
|
||||||
emitter.fire();
|
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public get value(): string {
|
||||||
|
return this.properties['value'];
|
||||||
|
}
|
||||||
|
public set value(v: string) {
|
||||||
|
this.setProperty('value', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get values(): string[] {
|
||||||
|
return this.properties['values'];
|
||||||
|
}
|
||||||
|
public set values(v: string[]) {
|
||||||
|
this.setProperty('values', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get onValueChanged(): vscode.Event<any> {
|
||||||
|
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
|
||||||
|
return emitter && emitter.event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ButtonWrapper extends ComponentWrapper implements sqlops.ButtonComponent {
|
||||||
|
|
||||||
|
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
|
||||||
|
super(proxy, handle, ModelComponentTypes.Button, id);
|
||||||
|
this.properties = {};
|
||||||
|
this._emitterMap.set(ComponentEventType.onDidClick, new Emitter<any>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public get label(): string {
|
||||||
|
return this.properties['label'];
|
||||||
|
}
|
||||||
|
public set label(v: string) {
|
||||||
|
this.setProperty('label', v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get onDidClick(): vscode.Event<any> {
|
||||||
|
let emitter = this._emitterMap.get(ComponentEventType.onDidClick);
|
||||||
|
return emitter && emitter.event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
211
src/sql/workbench/api/node/extHostModelViewDialog.ts
Normal file
211
src/sql/workbench/api/node/extHostModelViewDialog.ts
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||||
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
import { deepClone } from 'vs/base/common/objects';
|
||||||
|
import * as nls from 'vs/nls';
|
||||||
|
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
|
||||||
|
import { SqlMainContext, ExtHostModelViewDialogShape, MainThreadModelViewDialogShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
|
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
|
class DialogImpl implements sqlops.window.modelviewdialog.Dialog {
|
||||||
|
public title: string;
|
||||||
|
public content: string | sqlops.window.modelviewdialog.DialogTab[];
|
||||||
|
public okButton: sqlops.window.modelviewdialog.Button;
|
||||||
|
public cancelButton: sqlops.window.modelviewdialog.Button;
|
||||||
|
public customButtons: sqlops.window.modelviewdialog.Button[];
|
||||||
|
|
||||||
|
constructor(private _extHostModelViewDialog: ExtHostModelViewDialog) {
|
||||||
|
this.okButton = this._extHostModelViewDialog.createButton(nls.localize('dialogOkLabel', 'Done'));
|
||||||
|
this.cancelButton = this._extHostModelViewDialog.createButton(nls.localize('dialogCancelLabel', 'Cancel'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TabImpl implements sqlops.window.modelviewdialog.DialogTab {
|
||||||
|
public title: string;
|
||||||
|
public content: string;
|
||||||
|
|
||||||
|
constructor(private _extHostModelViewDialog: ExtHostModelViewDialog) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ButtonImpl implements sqlops.window.modelviewdialog.Button {
|
||||||
|
private _label: string;
|
||||||
|
private _enabled: boolean;
|
||||||
|
private _hidden: boolean;
|
||||||
|
|
||||||
|
private _onClick = new Emitter<void>();
|
||||||
|
public onClick = this._onClick.event;
|
||||||
|
|
||||||
|
constructor(private _extHostModelViewDialog: ExtHostModelViewDialog) {
|
||||||
|
this._enabled = true;
|
||||||
|
this._hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get label(): string {
|
||||||
|
return this._label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set label(label: string) {
|
||||||
|
this._label = label;
|
||||||
|
this._extHostModelViewDialog.updateButton(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get enabled(): boolean {
|
||||||
|
return this._enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set enabled(enabled: boolean) {
|
||||||
|
this._enabled = enabled;
|
||||||
|
this._extHostModelViewDialog.updateButton(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get hidden(): boolean {
|
||||||
|
return this._hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set hidden(hidden: boolean) {
|
||||||
|
this._hidden = hidden;
|
||||||
|
this._extHostModelViewDialog.updateButton(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getOnClickCallback(): () => void {
|
||||||
|
return () => this._onClick.fire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||||
|
private static _currentHandle = 0;
|
||||||
|
|
||||||
|
private readonly _proxy: MainThreadModelViewDialogShape;
|
||||||
|
|
||||||
|
private readonly _dialogHandles = new Map<sqlops.window.modelviewdialog.Dialog, number>();
|
||||||
|
private readonly _tabHandles = new Map<sqlops.window.modelviewdialog.DialogTab, number>();
|
||||||
|
private readonly _buttonHandles = new Map<sqlops.window.modelviewdialog.Button, number>();
|
||||||
|
|
||||||
|
private readonly _onClickCallbacks = new Map<number, () => void>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
mainContext: IMainContext
|
||||||
|
) {
|
||||||
|
this._proxy = mainContext.getProxy(SqlMainContext.MainThreadModelViewDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getNewHandle() {
|
||||||
|
let handle = ExtHostModelViewDialog._currentHandle;
|
||||||
|
ExtHostModelViewDialog._currentHandle += 1;
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDialogHandle(dialog: sqlops.window.modelviewdialog.Dialog) {
|
||||||
|
let handle = this._dialogHandles.get(dialog);
|
||||||
|
if (handle === undefined) {
|
||||||
|
handle = ExtHostModelViewDialog.getNewHandle();
|
||||||
|
this._dialogHandles.set(dialog, handle);
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTabHandle(tab: sqlops.window.modelviewdialog.DialogTab) {
|
||||||
|
let handle = this._tabHandles.get(tab);
|
||||||
|
if (handle === undefined) {
|
||||||
|
handle = ExtHostModelViewDialog.getNewHandle();
|
||||||
|
this._tabHandles.set(tab, handle);
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getButtonHandle(button: sqlops.window.modelviewdialog.Button) {
|
||||||
|
let handle = this._buttonHandles.get(button);
|
||||||
|
if (handle === undefined) {
|
||||||
|
handle = ExtHostModelViewDialog.getNewHandle();
|
||||||
|
this._buttonHandles.set(button, handle);
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public $onButtonClick(handle: number): void {
|
||||||
|
this._onClickCallbacks.get(handle)();
|
||||||
|
}
|
||||||
|
|
||||||
|
public open(dialog: sqlops.window.modelviewdialog.Dialog): void {
|
||||||
|
let handle = this.getDialogHandle(dialog);
|
||||||
|
this.updateDialogContent(dialog);
|
||||||
|
this._proxy.$open(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public close(dialog: sqlops.window.modelviewdialog.Dialog): void {
|
||||||
|
let handle = this.getDialogHandle(dialog);
|
||||||
|
this._proxy.$close(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateDialogContent(dialog: sqlops.window.modelviewdialog.Dialog): void {
|
||||||
|
let handle = this.getDialogHandle(dialog);
|
||||||
|
let tabs = dialog.content;
|
||||||
|
if (tabs && typeof tabs !== 'string') {
|
||||||
|
tabs.forEach(tab => this.updateTabContent(tab));
|
||||||
|
}
|
||||||
|
if (dialog.customButtons) {
|
||||||
|
dialog.customButtons.forEach(button => this.updateButton(button));
|
||||||
|
}
|
||||||
|
this.updateButton(dialog.okButton);
|
||||||
|
this.updateButton(dialog.cancelButton);
|
||||||
|
this._proxy.$setDialogDetails(handle, {
|
||||||
|
title: dialog.title,
|
||||||
|
okButton: this.getButtonHandle(dialog.okButton),
|
||||||
|
cancelButton: this.getButtonHandle(dialog.cancelButton),
|
||||||
|
content: dialog.content && typeof dialog.content !== 'string' ? dialog.content.map(tab => this.getTabHandle(tab)) : dialog.content as string,
|
||||||
|
customButtons: dialog.customButtons ? dialog.customButtons.map(button => this.getButtonHandle(button)) : undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateTabContent(tab: sqlops.window.modelviewdialog.DialogTab): void {
|
||||||
|
let handle = this.getTabHandle(tab);
|
||||||
|
this._proxy.$setTabDetails(handle, {
|
||||||
|
title: tab.title,
|
||||||
|
content: tab.content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateButton(button: sqlops.window.modelviewdialog.Button): void {
|
||||||
|
let handle = this.getButtonHandle(button);
|
||||||
|
this._proxy.$setButtonDetails(handle, {
|
||||||
|
label: button.label,
|
||||||
|
enabled: button.enabled,
|
||||||
|
hidden: button.hidden
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public registerOnClickCallback(button: sqlops.window.modelviewdialog.Button, callback: () => void) {
|
||||||
|
let handle = this.getButtonHandle(button);
|
||||||
|
this._onClickCallbacks.set(handle, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createDialog(title: string): sqlops.window.modelviewdialog.Dialog {
|
||||||
|
let dialog = new DialogImpl(this);
|
||||||
|
dialog.title = title;
|
||||||
|
this.getDialogHandle(dialog);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createTab(title: string): sqlops.window.modelviewdialog.DialogTab {
|
||||||
|
let tab = new TabImpl(this);
|
||||||
|
tab.title = title;
|
||||||
|
this.getTabHandle(tab);
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createButton(label: string): sqlops.window.modelviewdialog.Button {
|
||||||
|
let button = new ButtonImpl(this);
|
||||||
|
this.getButtonHandle(button);
|
||||||
|
this.registerOnClickCallback(button, button.getOnClickCallback());
|
||||||
|
button.label = label;
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
|
|||||||
}
|
}
|
||||||
|
|
||||||
$initializeModel(handle: number, rootComponent: IComponentShape): Thenable<void> {
|
$initializeModel(handle: number, rootComponent: IComponentShape): Thenable<void> {
|
||||||
return this.execModelViewAction(handle, (modelView) => {
|
return this.execModelViewAction(handle, (modelView) => {
|
||||||
modelView.initializeModel(rootComponent);
|
modelView.initializeModel(rootComponent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -67,11 +67,13 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
|
|||||||
this._proxy.$handleEvent(handle, componentId, eventArgs);
|
this._proxy.$handleEvent(handle, componentId, eventArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
$registerEvent(handle: number, componentId: string): Thenable<void> {
|
$registerEvent(handle: number, componentId: string): Thenable<void> {
|
||||||
let properties: { [key: string]: any; } = { eventName: this.onEvent };
|
let properties: { [key: string]: any; } = { eventName: this.onEvent };
|
||||||
return this.execModelViewAction(handle, (modelView) => {
|
return this.execModelViewAction(handle, (modelView) => {
|
||||||
this._register(modelView.onEvent (e => {
|
this._register(modelView.onEvent(e => {
|
||||||
this.onEvent(handle, componentId, e);
|
if (e.componentId && e.componentId === componentId) {
|
||||||
|
this.onEvent(handle, componentId, e);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
130
src/sql/workbench/api/node/mainThreadModelViewDialog.ts
Normal file
130
src/sql/workbench/api/node/mainThreadModelViewDialog.ts
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { MainThreadModelViewDialogShape, SqlMainContext, ExtHostModelViewDialogShape, SqlExtHostContext } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
|
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||||
|
import { Dialog, DialogTab, DialogButton } from 'sql/platform/dialog/dialogTypes';
|
||||||
|
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||||
|
import { CustomDialogService } from 'sql/platform/dialog/customDialogService';
|
||||||
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { IModelViewDialogDetails, IModelViewTabDetails, IModelViewButtonDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
|
@extHostNamedCustomer(SqlMainContext.MainThreadModelViewDialog)
|
||||||
|
export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape {
|
||||||
|
private readonly _proxy: ExtHostModelViewDialogShape;
|
||||||
|
private readonly _dialogs = new Map<number, Dialog>();
|
||||||
|
private readonly _tabs = new Map<number, DialogTab>();
|
||||||
|
private readonly _buttons = new Map<number, DialogButton>();
|
||||||
|
private _dialogService: CustomDialogService;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
context: IExtHostContext,
|
||||||
|
@IInstantiationService instatiationService: IInstantiationService,
|
||||||
|
) {
|
||||||
|
this._proxy = context.getProxy(SqlExtHostContext.ExtHostModelViewDialog);
|
||||||
|
this._dialogService = new CustomDialogService(instatiationService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose(): void {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public $open(handle: number): Thenable<void> {
|
||||||
|
let dialog = this.getDialog(handle);
|
||||||
|
this._dialogService.showDialog(dialog);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
public $close(handle: number): Thenable<void> {
|
||||||
|
let dialog = this.getDialog(handle);
|
||||||
|
this._dialogService.closeDialog(dialog);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
public $setDialogDetails(handle: number, details: IModelViewDialogDetails): Thenable<void> {
|
||||||
|
let dialog = this._dialogs.get(handle);
|
||||||
|
if (!dialog) {
|
||||||
|
dialog = new Dialog(details.title);
|
||||||
|
let okButton = this.getButton(details.okButton);
|
||||||
|
let cancelButton = this.getButton(details.cancelButton);
|
||||||
|
dialog.okButton = okButton;
|
||||||
|
dialog.cancelButton = cancelButton;
|
||||||
|
this._dialogs.set(handle, dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.title = details.title;
|
||||||
|
if (details.content && typeof details.content !== 'string') {
|
||||||
|
dialog.content = details.content.map(tabHandle => this.getTab(tabHandle));
|
||||||
|
} else {
|
||||||
|
dialog.content = details.content as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (details.customButtons) {
|
||||||
|
dialog.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
public $setTabDetails(handle: number, details: IModelViewTabDetails): Thenable<void> {
|
||||||
|
let tab = this._tabs.get(handle);
|
||||||
|
if (!tab) {
|
||||||
|
tab = new DialogTab(details.title);
|
||||||
|
this._tabs.set(handle, tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
tab.title = details.title;
|
||||||
|
tab.content = details.content;
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
public $setButtonDetails(handle: number, details: IModelViewButtonDetails): Thenable<void> {
|
||||||
|
let button = this._buttons.get(handle);
|
||||||
|
if (!button) {
|
||||||
|
button = new DialogButton(details.label, details.enabled);
|
||||||
|
button.hidden = details.hidden;
|
||||||
|
button.onClick(() => this.onButtonClick(handle));
|
||||||
|
this._buttons.set(handle, button);
|
||||||
|
} else {
|
||||||
|
button.label = details.label;
|
||||||
|
button.enabled = details.enabled;
|
||||||
|
button.hidden = details.hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDialog(handle: number): Dialog {
|
||||||
|
let dialog = this._dialogs.get(handle);
|
||||||
|
if (!dialog) {
|
||||||
|
throw new Error('No dialog matching the given handle');
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTab(handle: number): DialogTab {
|
||||||
|
let tab = this._tabs.get(handle);
|
||||||
|
if (!tab) {
|
||||||
|
throw new Error('No tab matching the given handle');
|
||||||
|
}
|
||||||
|
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getButton(handle: number): DialogButton {
|
||||||
|
let button = this._buttons.get(handle);
|
||||||
|
if (!button) {
|
||||||
|
throw new Error('No button matching the given handle');
|
||||||
|
}
|
||||||
|
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onButtonClick(handle: number): void {
|
||||||
|
this._proxy.$onButtonClick(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import { ExtHostConnectionManagement } from 'sql/workbench/api/node/extHostConne
|
|||||||
import { ExtHostDashboard } from 'sql/workbench/api/node/extHostDashboard';
|
import { ExtHostDashboard } from 'sql/workbench/api/node/extHostDashboard';
|
||||||
import { ExtHostObjectExplorer } from 'sql/workbench/api/node/extHostObjectExplorer';
|
import { ExtHostObjectExplorer } from 'sql/workbench/api/node/extHostObjectExplorer';
|
||||||
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
||||||
|
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
|
||||||
import { ExtHostQueryEditor } from 'sql/workbench/api/node/extHostQueryEditor';
|
import { ExtHostQueryEditor } from 'sql/workbench/api/node/extHostQueryEditor';
|
||||||
|
|
||||||
export interface ISqlExtensionApiFactory {
|
export interface ISqlExtensionApiFactory {
|
||||||
@@ -65,6 +66,7 @@ export function createApiFactory(
|
|||||||
const extHostWebviewWidgets = rpcProtocol.set(SqlExtHostContext.ExtHostDashboardWebviews, new ExtHostDashboardWebviews(rpcProtocol));
|
const extHostWebviewWidgets = rpcProtocol.set(SqlExtHostContext.ExtHostDashboardWebviews, new ExtHostDashboardWebviews(rpcProtocol));
|
||||||
const extHostModelView = rpcProtocol.set(SqlExtHostContext.ExtHostModelView, new ExtHostModelView(rpcProtocol));
|
const extHostModelView = rpcProtocol.set(SqlExtHostContext.ExtHostModelView, new ExtHostModelView(rpcProtocol));
|
||||||
const extHostDashboard = rpcProtocol.set(SqlExtHostContext.ExtHostDashboard, new ExtHostDashboard(rpcProtocol));
|
const extHostDashboard = rpcProtocol.set(SqlExtHostContext.ExtHostDashboard, new ExtHostDashboard(rpcProtocol));
|
||||||
|
const extHostModelViewDialog = rpcProtocol.set(SqlExtHostContext.ExtHostModelViewDialog, new ExtHostModelViewDialog(rpcProtocol));
|
||||||
const extHostQueryEditor = rpcProtocol.set(SqlExtHostContext.ExtHostQueryEditor, new ExtHostQueryEditor(rpcProtocol));
|
const extHostQueryEditor = rpcProtocol.set(SqlExtHostContext.ExtHostQueryEditor, new ExtHostQueryEditor(rpcProtocol));
|
||||||
|
|
||||||
|
|
||||||
@@ -283,10 +285,21 @@ export function createApiFactory(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const modelViewDialog: typeof sqlops.window.modelviewdialog = {
|
const modelViewDialog: typeof sqlops.window.modelviewdialog = {
|
||||||
// TODO mairvine 4/18/18: Implement the extension layer for custom dialogs
|
createDialog(title: string): sqlops.window.modelviewdialog.Dialog {
|
||||||
createDialog(title: string): sqlops.window.modelviewdialog.Dialog { return undefined; },
|
return extHostModelViewDialog.createDialog(title);
|
||||||
createTab(title: string): sqlops.window.modelviewdialog.DialogTab { return undefined; },
|
},
|
||||||
createButton(label: string): sqlops.window.modelviewdialog.Button { return undefined; }
|
createTab(title: string): sqlops.window.modelviewdialog.DialogTab {
|
||||||
|
return extHostModelViewDialog.createTab(title);
|
||||||
|
},
|
||||||
|
createButton(label: string): sqlops.window.modelviewdialog.Button {
|
||||||
|
return extHostModelViewDialog.createButton(label);
|
||||||
|
},
|
||||||
|
openDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
|
||||||
|
return extHostModelViewDialog.open(dialog);
|
||||||
|
},
|
||||||
|
closeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
|
||||||
|
return extHostModelViewDialog.close(dialog);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const window: typeof sqlops.window = {
|
const window: typeof sqlops.window = {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import 'sql/workbench/api/electron-browser/mainThreadDashboard';
|
|||||||
import 'sql/workbench/api/node/mainThreadDashboardWebview';
|
import 'sql/workbench/api/node/mainThreadDashboardWebview';
|
||||||
import 'sql/workbench/api/node/mainThreadQueryEditor';
|
import 'sql/workbench/api/node/mainThreadQueryEditor';
|
||||||
import 'sql/workbench/api/node/mainThreadModelView';
|
import 'sql/workbench/api/node/mainThreadModelView';
|
||||||
|
import 'sql/workbench/api/node/mainThreadModelViewDialog';
|
||||||
import './mainThreadAccountManagement';
|
import './mainThreadAccountManagement';
|
||||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import * as sqlops from 'sqlops';
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks';
|
import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks';
|
||||||
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { IItemConfig, ModelComponentTypes, IComponentShape, IModelViewDialogDetails, IModelViewTabDetails, IModelViewButtonDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
import Event, { Emitter } from 'vs/base/common/event';
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
export abstract class ExtHostAccountManagementShape {
|
export abstract class ExtHostAccountManagementShape {
|
||||||
@@ -448,6 +448,7 @@ export const SqlMainContext = {
|
|||||||
MainThreadDashboardWebview: createMainId<MainThreadDashboardWebviewShape>('MainThreadDashboardWebview'),
|
MainThreadDashboardWebview: createMainId<MainThreadDashboardWebviewShape>('MainThreadDashboardWebview'),
|
||||||
MainThreadModelView: createMainId<MainThreadModelViewShape>('MainThreadModelView'),
|
MainThreadModelView: createMainId<MainThreadModelViewShape>('MainThreadModelView'),
|
||||||
MainThreadDashboard: createMainId<MainThreadDashboardShape>('MainThreadDashboard'),
|
MainThreadDashboard: createMainId<MainThreadDashboardShape>('MainThreadDashboard'),
|
||||||
|
MainThreadModelViewDialog: createMainId<MainThreadModelViewDialogShape>('MainThreadModelViewDialog'),
|
||||||
MainThreadQueryEditor: createMainId<MainThreadQueryEditorShape>('MainThreadQueryEditor'),
|
MainThreadQueryEditor: createMainId<MainThreadQueryEditorShape>('MainThreadQueryEditor'),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -464,6 +465,7 @@ export const SqlExtHostContext = {
|
|||||||
ExtHostDashboardWebviews: createExtId<ExtHostDashboardWebviewsShape>('ExtHostDashboardWebviews'),
|
ExtHostDashboardWebviews: createExtId<ExtHostDashboardWebviewsShape>('ExtHostDashboardWebviews'),
|
||||||
ExtHostModelView: createExtId<ExtHostModelViewShape>('ExtHostModelView'),
|
ExtHostModelView: createExtId<ExtHostModelViewShape>('ExtHostModelView'),
|
||||||
ExtHostDashboard: createExtId<ExtHostDashboardShape>('ExtHostDashboard'),
|
ExtHostDashboard: createExtId<ExtHostDashboardShape>('ExtHostDashboard'),
|
||||||
|
ExtHostModelViewDialog: createExtId<ExtHostModelViewDialogShape>('ExtHostModelViewDialog'),
|
||||||
ExtHostQueryEditor: createExtId<ExtHostQueryEditorShape>('ExtHostQueryEditor')
|
ExtHostQueryEditor: createExtId<ExtHostQueryEditorShape>('ExtHostQueryEditor')
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -543,10 +545,21 @@ export interface MainThreadObjectExplorerShape extends IDisposable {
|
|||||||
$findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]>;
|
$findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExtHostModelViewDialogShape {
|
||||||
|
$onButtonClick(handle: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MainThreadModelViewDialogShape extends IDisposable {
|
||||||
|
$open(handle: number): Thenable<void>;
|
||||||
|
$close(handle: number): Thenable<void>;
|
||||||
|
$setDialogDetails(handle: number, details: IModelViewDialogDetails): Thenable<void>;
|
||||||
|
$setTabDetails(handle: number, details: IModelViewTabDetails): Thenable<void>;
|
||||||
|
$setButtonDetails(handle: number, details: IModelViewButtonDetails): Thenable<void>;
|
||||||
|
}
|
||||||
export interface ExtHostQueryEditorShape {
|
export interface ExtHostQueryEditorShape {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MainThreadQueryEditorShape extends IDisposable {
|
export interface MainThreadQueryEditorShape extends IDisposable {
|
||||||
$connect(fileUri: string, connectionId: string): Thenable<void>;
|
$connect(fileUri: string, connectionId: string): Thenable<void>;
|
||||||
$runQuery(fileUri: string): void;
|
$runQuery(fileUri: string): void;
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/sqltest/parts/grid/services/sharedServices.test.ts
Normal file
43
src/sqltest/parts/grid/services/sharedServices.test.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 sqlops from 'sqlops';
|
||||||
|
import * as assert from 'assert';
|
||||||
|
import * as SharedServices from 'sql/parts/grid/services/sharedServices';
|
||||||
|
|
||||||
|
const testText = '<div>test text</div>';
|
||||||
|
|
||||||
|
suite('Grid shared services tests', () => {
|
||||||
|
test('textFormatter should encode HTML when formatting a DBCellValue object', () => {
|
||||||
|
// If I format a DBCellValue object that contains HTML
|
||||||
|
let cellValue = new SharedServices.DBCellValue();
|
||||||
|
cellValue.displayValue = testText;
|
||||||
|
cellValue.isNull = false;
|
||||||
|
let formattedHtml = SharedServices.textFormatter(undefined, undefined, cellValue, undefined, undefined);
|
||||||
|
|
||||||
|
// Then the result is HTML for a span element containing the cell value's display value as plain text
|
||||||
|
verifyFormattedHtml(formattedHtml, testText);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('textFormatter should encode HTML when formatting a string', () => {
|
||||||
|
// If I format a string that contains HTML
|
||||||
|
let formattedHtml = SharedServices.textFormatter(undefined, undefined, testText, undefined, undefined);
|
||||||
|
|
||||||
|
// Then the result is HTML for a span element containing the given text as plain text
|
||||||
|
verifyFormattedHtml(formattedHtml, testText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function verifyFormattedHtml(formattedHtml: string, expectedText: string): void {
|
||||||
|
// Create an element containing the span returned by the format call
|
||||||
|
let element = document.createElement('div');
|
||||||
|
element.innerHTML = formattedHtml;
|
||||||
|
let spanElement = element.children[0];
|
||||||
|
|
||||||
|
// Verify that the span element's text, not its innerHTML, matches the expected text
|
||||||
|
assert.equal(spanElement.textContent, testText);
|
||||||
|
assert.notEqual(spanElement.innerHTML, testText);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Dialog, DialogTab } from 'sql/platform/dialog/dialogTypes';
|
import { Dialog, DialogTab } from 'sql/platform/dialog/dialogTypes';
|
||||||
import { Mock, It, Times } from 'typemoq';
|
import { Mock, It, Times } from 'typemoq';
|
||||||
import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService';
|
import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService';
|
||||||
import { DialogPane } from 'sql/platform/dialog/dialogPane';
|
import { DialogPane } from 'sql/platform/dialog/dialogPane';
|
||||||
|
|||||||
121
src/sqltest/workbench/api/extHostModelViewDialog.test.ts
Normal file
121
src/sqltest/workbench/api/extHostModelViewDialog.test.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as assert from 'assert';
|
||||||
|
import { Mock, It, Times } from 'typemoq';
|
||||||
|
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
|
||||||
|
import { MainThreadModelViewDialogShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
|
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
suite('ExtHostModelViewDialog Tests', () => {
|
||||||
|
let extHostModelViewDialog: ExtHostModelViewDialog;
|
||||||
|
let mockProxy: Mock<MainThreadModelViewDialogShape>;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
mockProxy = Mock.ofInstance(<MainThreadModelViewDialogShape>{
|
||||||
|
$open: handle => undefined,
|
||||||
|
$close: handle => undefined,
|
||||||
|
$setDialogDetails: (handle, details) => undefined,
|
||||||
|
$setTabDetails: (handle, details) => undefined,
|
||||||
|
$setButtonDetails: (handle, details) => undefined
|
||||||
|
});
|
||||||
|
let mainContext = <IMainContext>{
|
||||||
|
getProxy: proxyType => mockProxy.object
|
||||||
|
};
|
||||||
|
extHostModelViewDialog = new ExtHostModelViewDialog(mainContext);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Creating a dialog returns a dialog with initialized ok and cancel buttons and the given title', () => {
|
||||||
|
let title = 'dialog_title';
|
||||||
|
let dialog = extHostModelViewDialog.createDialog(title);
|
||||||
|
|
||||||
|
assert.equal(dialog.title, title);
|
||||||
|
assert.equal(dialog.okButton.enabled, true);
|
||||||
|
assert.equal(dialog.cancelButton.enabled, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Creating a tab returns a tab with the given title', () => {
|
||||||
|
let title = 'tab_title';
|
||||||
|
let tab = extHostModelViewDialog.createTab(title);
|
||||||
|
|
||||||
|
assert.equal(tab.title, title);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Creating a button returns an enabled button with the given label', () => {
|
||||||
|
let label = 'button_label';
|
||||||
|
let button = extHostModelViewDialog.createButton(label);
|
||||||
|
|
||||||
|
assert.equal(button.label, label);
|
||||||
|
assert.equal(button.enabled, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Opening a dialog updates its tabs and buttons on the main thread', () => {
|
||||||
|
mockProxy.setup(x => x.$open(It.isAny()));
|
||||||
|
mockProxy.setup(x => x.$setDialogDetails(It.isAny(), It.isAny()));
|
||||||
|
mockProxy.setup(x => x.$setTabDetails(It.isAny(), It.isAny()));
|
||||||
|
mockProxy.setup(x => x.$setButtonDetails(It.isAny(), It.isAny()));
|
||||||
|
|
||||||
|
// Create a dialog with 2 tabs and 2 custom buttons
|
||||||
|
let dialogTitle = 'dialog_title';
|
||||||
|
let dialog = extHostModelViewDialog.createDialog(dialogTitle);
|
||||||
|
let tab1Title = 'tab_1';
|
||||||
|
let tab1 = extHostModelViewDialog.createTab(tab1Title);
|
||||||
|
let tab2Title = 'tab_2';
|
||||||
|
let tab2 = extHostModelViewDialog.createTab(tab2Title);
|
||||||
|
dialog.content = [tab1, tab2];
|
||||||
|
let button1Label = 'button_1';
|
||||||
|
let button1 = extHostModelViewDialog.createButton(button1Label);
|
||||||
|
button1.enabled = false;
|
||||||
|
let button2Label = 'button_2';
|
||||||
|
let button2 = extHostModelViewDialog.createButton(button2Label);
|
||||||
|
|
||||||
|
// Open the dialog and verify that the correct main thread methods were called
|
||||||
|
extHostModelViewDialog.open(dialog);
|
||||||
|
mockProxy.verify(x => x.$setButtonDetails(It.isAny(), It.is(details => {
|
||||||
|
return details.enabled === false && details.label === button1Label;
|
||||||
|
})), Times.once());
|
||||||
|
mockProxy.verify(x => x.$setButtonDetails(It.isAny(), It.is(details => {
|
||||||
|
return details.enabled === true && details.label === button2Label;
|
||||||
|
})), Times.once());
|
||||||
|
mockProxy.verify(x => x.$setTabDetails(It.isAny(), It.is(details => {
|
||||||
|
return details.title === tab1Title;
|
||||||
|
})), Times.once());
|
||||||
|
mockProxy.verify(x => x.$setTabDetails(It.isAny(), It.is(details => {
|
||||||
|
return details.title === tab2Title;
|
||||||
|
})), Times.once());
|
||||||
|
mockProxy.verify(x => x.$setDialogDetails(It.isAny(), It.is(details => {
|
||||||
|
return details.title === dialogTitle;
|
||||||
|
})), Times.once());
|
||||||
|
mockProxy.verify(x => x.$open(It.isAny()), Times.once());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Button clicks are forwarded to the correct button', () => {
|
||||||
|
// Set up the proxy to record button handles
|
||||||
|
let handles = [];
|
||||||
|
mockProxy.setup(x => x.$setButtonDetails(It.isAny(), It.isAny())).callback((handle, details) => handles.push(handle));
|
||||||
|
|
||||||
|
// Set up the buttons to record click events
|
||||||
|
let label1 = 'button_1';
|
||||||
|
let label2 = 'button_2';
|
||||||
|
let button1 = extHostModelViewDialog.createButton(label1);
|
||||||
|
let button2 = extHostModelViewDialog.createButton(label2);
|
||||||
|
let clickEvents = [];
|
||||||
|
button1.onClick(() => clickEvents.push(1));
|
||||||
|
button2.onClick(() => clickEvents.push(2));
|
||||||
|
extHostModelViewDialog.updateButton(button1);
|
||||||
|
extHostModelViewDialog.updateButton(button2);
|
||||||
|
|
||||||
|
// If the main thread sends some notifications that the buttons have been clicked
|
||||||
|
extHostModelViewDialog.$onButtonClick(handles[0]);
|
||||||
|
extHostModelViewDialog.$onButtonClick(handles[1]);
|
||||||
|
extHostModelViewDialog.$onButtonClick(handles[1]);
|
||||||
|
extHostModelViewDialog.$onButtonClick(handles[0]);
|
||||||
|
|
||||||
|
// Then the clicks should have been handled by the expected handlers
|
||||||
|
assert.deepEqual(clickEvents, [1, 2, 2, 1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
157
src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts
Normal file
157
src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as assert from 'assert';
|
||||||
|
import { Mock, It, Times } from 'typemoq';
|
||||||
|
import { MainThreadModelViewDialog } from 'sql/workbench/api/node/mainThreadModelViewDialog';
|
||||||
|
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||||
|
import { IModelViewButtonDetails, IModelViewTabDetails, IModelViewDialogDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
import { CustomDialogService } from 'sql/platform/dialog/customDialogService';
|
||||||
|
import { Dialog, DialogTab } from 'sql/platform/dialog/dialogTypes';
|
||||||
|
import { ExtHostModelViewDialogShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
|
import { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
suite('MainThreadModelViewDialog Tests', () => {
|
||||||
|
let mainThreadModelViewDialog: MainThreadModelViewDialog;
|
||||||
|
let mockExtHostModelViewDialog: Mock<ExtHostModelViewDialogShape>;
|
||||||
|
let mockDialogService: Mock<CustomDialogService>;
|
||||||
|
let openedDialog: Dialog;
|
||||||
|
|
||||||
|
// Dialog details
|
||||||
|
let button1Details: IModelViewButtonDetails;
|
||||||
|
let button2Details: IModelViewButtonDetails;
|
||||||
|
let okButtonDetails: IModelViewButtonDetails;
|
||||||
|
let cancelButtonDetails: IModelViewButtonDetails;
|
||||||
|
let tab1Details: IModelViewTabDetails;
|
||||||
|
let tab2Details: IModelViewTabDetails;
|
||||||
|
let dialogDetails: IModelViewDialogDetails;
|
||||||
|
let button1Handle = 1;
|
||||||
|
let button2Handle = 2;
|
||||||
|
let okButtonHandle = 3;
|
||||||
|
let cancelButtonHandle = 4;
|
||||||
|
let tab1Handle = 5;
|
||||||
|
let tab2Handle = 6;
|
||||||
|
let dialogHandle = 7;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
mockExtHostModelViewDialog = Mock.ofInstance(<ExtHostModelViewDialogShape>{
|
||||||
|
$onButtonClick: handle => undefined
|
||||||
|
});
|
||||||
|
let extHostContext = <IExtHostContext>{
|
||||||
|
getProxy: proxyType => mockExtHostModelViewDialog.object
|
||||||
|
};
|
||||||
|
mainThreadModelViewDialog = new MainThreadModelViewDialog(extHostContext, undefined);
|
||||||
|
|
||||||
|
// Set up the mock dialog service
|
||||||
|
mockDialogService = Mock.ofType(CustomDialogService, undefined, undefined);
|
||||||
|
openedDialog = undefined;
|
||||||
|
mockDialogService.setup(x => x.showDialog(It.isAny())).callback(dialog => openedDialog = dialog);
|
||||||
|
(mainThreadModelViewDialog as any)._dialogService = mockDialogService.object;
|
||||||
|
|
||||||
|
// Set up the dialog details
|
||||||
|
button1Details = {
|
||||||
|
label: 'button1',
|
||||||
|
enabled: false,
|
||||||
|
hidden: false
|
||||||
|
};
|
||||||
|
button2Details = {
|
||||||
|
label: 'button2',
|
||||||
|
enabled: true,
|
||||||
|
hidden: false
|
||||||
|
};
|
||||||
|
okButtonDetails = {
|
||||||
|
label: 'ok_label',
|
||||||
|
enabled: true,
|
||||||
|
hidden: false
|
||||||
|
};
|
||||||
|
cancelButtonDetails = {
|
||||||
|
label: 'cancel_label',
|
||||||
|
enabled: true,
|
||||||
|
hidden: false
|
||||||
|
};
|
||||||
|
tab1Details = {
|
||||||
|
title: 'tab1',
|
||||||
|
content: 'content1'
|
||||||
|
};
|
||||||
|
tab2Details = {
|
||||||
|
title: 'tab2',
|
||||||
|
content: 'content2'
|
||||||
|
};
|
||||||
|
dialogDetails = {
|
||||||
|
title: 'dialog1',
|
||||||
|
content: [tab1Handle, tab2Handle],
|
||||||
|
okButton: okButtonHandle,
|
||||||
|
cancelButton: cancelButtonHandle,
|
||||||
|
customButtons: [button1Handle, button2Handle]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register the buttons, tabs, and dialog
|
||||||
|
mainThreadModelViewDialog.$setButtonDetails(button1Handle, button1Details);
|
||||||
|
mainThreadModelViewDialog.$setButtonDetails(button2Handle, button2Details);
|
||||||
|
mainThreadModelViewDialog.$setButtonDetails(okButtonHandle, okButtonDetails);
|
||||||
|
mainThreadModelViewDialog.$setButtonDetails(cancelButtonHandle, cancelButtonDetails);
|
||||||
|
mainThreadModelViewDialog.$setTabDetails(tab1Handle, tab1Details);
|
||||||
|
mainThreadModelViewDialog.$setTabDetails(tab2Handle, tab2Details);
|
||||||
|
mainThreadModelViewDialog.$setDialogDetails(dialogHandle, dialogDetails);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Creating a dialog and calling open on it causes a dialog with correct content and buttons to open', () => {
|
||||||
|
// If I open the dialog
|
||||||
|
mainThreadModelViewDialog.$open(dialogHandle);
|
||||||
|
|
||||||
|
// Then the opened dialog's content and buttons match what was set
|
||||||
|
mockDialogService.verify(x => x.showDialog(It.isAny()), Times.once());
|
||||||
|
assert.notEqual(openedDialog, undefined);
|
||||||
|
assert.equal(openedDialog.title, dialogDetails.title);
|
||||||
|
assert.equal(openedDialog.okButton.label, okButtonDetails.label);
|
||||||
|
assert.equal(openedDialog.okButton.enabled, okButtonDetails.enabled);
|
||||||
|
assert.equal(openedDialog.cancelButton.label, cancelButtonDetails.label);
|
||||||
|
assert.equal(openedDialog.cancelButton.enabled, cancelButtonDetails.enabled);
|
||||||
|
assert.equal(openedDialog.customButtons.length, 2);
|
||||||
|
assert.equal(openedDialog.customButtons[0].label, button1Details.label);
|
||||||
|
assert.equal(openedDialog.customButtons[0].enabled, button1Details.enabled);
|
||||||
|
assert.equal(openedDialog.customButtons[1].label, button2Details.label);
|
||||||
|
assert.equal(openedDialog.customButtons[1].enabled, button2Details.enabled);
|
||||||
|
assert.equal(openedDialog.content.length, 2);
|
||||||
|
assert.equal((openedDialog.content[0] as DialogTab).content, tab1Details.content);
|
||||||
|
assert.equal((openedDialog.content[0] as DialogTab).title, tab1Details.title);
|
||||||
|
assert.equal((openedDialog.content[1] as DialogTab).content, tab2Details.content);
|
||||||
|
assert.equal((openedDialog.content[1] as DialogTab).title, tab2Details.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Button presses are forwarded to the extension host', () => {
|
||||||
|
// Set up the mock proxy to capture button presses
|
||||||
|
let pressedHandles = [];
|
||||||
|
mockExtHostModelViewDialog.setup(x => x.$onButtonClick(It.isAny())).callback(handle => pressedHandles.push(handle));
|
||||||
|
|
||||||
|
// Open the dialog so that its buttons can be accessed
|
||||||
|
mainThreadModelViewDialog.$open(dialogHandle);
|
||||||
|
|
||||||
|
// Set up click emitters for each button
|
||||||
|
let okEmitter = new Emitter<void>();
|
||||||
|
let cancelEmitter = new Emitter<void>();
|
||||||
|
let button1Emitter = new Emitter<void>();
|
||||||
|
let button2Emitter = new Emitter<void>();
|
||||||
|
openedDialog.okButton.registerClickEvent(okEmitter.event);
|
||||||
|
openedDialog.cancelButton.registerClickEvent(cancelEmitter.event);
|
||||||
|
openedDialog.customButtons[0].registerClickEvent(button1Emitter.event);
|
||||||
|
openedDialog.customButtons[1].registerClickEvent(button2Emitter.event);
|
||||||
|
|
||||||
|
// Click the buttons
|
||||||
|
button1Emitter.fire();
|
||||||
|
button2Emitter.fire();
|
||||||
|
okEmitter.fire();
|
||||||
|
cancelEmitter.fire();
|
||||||
|
button2Emitter.fire();
|
||||||
|
cancelEmitter.fire();
|
||||||
|
button1Emitter.fire();
|
||||||
|
okEmitter.fire();
|
||||||
|
|
||||||
|
// Verify that the correct button click notifications were sent to the proxy
|
||||||
|
assert.deepEqual(pressedHandles, [button1Handle, button2Handle, okButtonHandle, cancelButtonHandle, button2Handle, cancelButtonHandle, button1Handle, okButtonHandle]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -462,11 +462,12 @@ export class IssueReporter extends Disposable {
|
|||||||
response.json().then(result => {
|
response.json().then(result => {
|
||||||
this.clearSearchResults();
|
this.clearSearchResults();
|
||||||
|
|
||||||
if (result && result.candidates) {
|
// {{SQL CARBON EDIT}}
|
||||||
this.displaySearchResults(result.candidates);
|
// if (result && result.candidates) {
|
||||||
} else {
|
// this.displaySearchResults(result.candidates);
|
||||||
throw new Error('Unexpected response, no candidates property');
|
// } else {
|
||||||
}
|
// throw new Error('Unexpected response, no candidates property');
|
||||||
|
// }
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.logSearchError(error);
|
this.logSearchError(error);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -59,13 +59,14 @@ export class IssueReporterModel {
|
|||||||
assign(this._data, newData);
|
assign(this._data, newData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
serialize(): string {
|
serialize(): string {
|
||||||
return `
|
return `
|
||||||
Issue Type: <b>${this.getIssueTypeTitle()}</b>
|
Issue Type: <b>${this.getIssueTypeTitle()}</b>
|
||||||
|
|
||||||
${this._data.issueDescription}
|
${this._data.issueDescription}
|
||||||
|
|
||||||
VS Code version: ${this._data.versionInfo && this._data.versionInfo.vscodeVersion}
|
SQL Operations Studio version: ${this._data.versionInfo && this._data.versionInfo.vscodeVersion}
|
||||||
OS version: ${this._data.versionInfo && this._data.versionInfo.os}
|
OS version: ${this._data.versionInfo && this._data.versionInfo.os}
|
||||||
|
|
||||||
${this.getInfos()}
|
${this.getInfos()}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ suite('IssueReporter', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
test('serializes model skeleton when no data is provided', () => {
|
test('serializes model skeleton when no data is provided', () => {
|
||||||
const issueReporterModel = new IssueReporterModel();
|
const issueReporterModel = new IssueReporterModel();
|
||||||
assert.equal(issueReporterModel.serialize(),
|
assert.equal(issueReporterModel.serialize(),
|
||||||
@@ -31,7 +32,7 @@ Issue Type: <b>Feature Request</b>
|
|||||||
|
|
||||||
undefined
|
undefined
|
||||||
|
|
||||||
VS Code version: undefined
|
SQL Operations Studio version: undefined
|
||||||
OS version: undefined
|
OS version: undefined
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -435,7 +435,9 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||||||
|
|
||||||
return this.queryGallery(query).then(({ galleryExtensions, total }) => {
|
return this.queryGallery(query).then(({ galleryExtensions, total }) => {
|
||||||
const extensions = galleryExtensions.map((e, index) => toExtension(e, this.extensionsGalleryUrl, index, query, options.source));
|
const extensions = galleryExtensions.map((e, index) => toExtension(e, this.extensionsGalleryUrl, index, query, options.source));
|
||||||
const pageSize = query.pageSize;
|
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
|
const pageSize = extensions.length;
|
||||||
const getPage = (pageIndex: number) => {
|
const getPage = (pageIndex: number) => {
|
||||||
const nextPageQuery = query.withPage(pageIndex + 1);
|
const nextPageQuery = query.withPage(pageIndex + 1);
|
||||||
return this.queryGallery(nextPageQuery)
|
return this.queryGallery(nextPageQuery)
|
||||||
|
|||||||
@@ -31,7 +31,18 @@ function getClient(aiKey: string): typeof appInsights.client {
|
|||||||
|
|
||||||
const client = appInsights.getClient(aiKey);
|
const client = appInsights.getClient(aiKey);
|
||||||
client.channel.setOfflineMode(true);
|
client.channel.setOfflineMode(true);
|
||||||
client.context.tags[client.context.keys.deviceMachineName] = ''; //prevent App Insights from reporting machine name
|
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
|
// clear all ID fields from telemetry
|
||||||
|
client.context.tags[client.context.keys.deviceMachineName] = '';
|
||||||
|
client.context.tags[client.context.keys.cloudRoleInstance] = '';
|
||||||
|
|
||||||
|
// set envelope flags to suppress Vortex ingest header
|
||||||
|
client.addTelemetryProcessor((envelope, contextObjects) => {
|
||||||
|
envelope.flags = 0x200000;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
if (aiKey.indexOf('AIF-') === 0) {
|
if (aiKey.indexOf('AIF-') === 0) {
|
||||||
client.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1';
|
client.config.endpointUrl = 'https://vortex.data.microsoft.com/collect/v1';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,15 @@ import product from 'vs/platform/node/product';
|
|||||||
|
|
||||||
export function resolveCommonProperties(commit: string, version: string, machineId: string, installSourcePath: string): TPromise<{ [name: string]: string; }> {
|
export function resolveCommonProperties(commit: string, version: string, machineId: string, installSourcePath: string): TPromise<{ [name: string]: string; }> {
|
||||||
const result: { [name: string]: string; } = Object.create(null);
|
const result: { [name: string]: string; } = Object.create(null);
|
||||||
|
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
// __GDPR__COMMON__ "common.machineId" : { "endPoint": "MacAddressHash", "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" }
|
// __GDPR__COMMON__ "common.machineId" : { "endPoint": "MacAddressHash", "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" }
|
||||||
result['common.machineId'] = machineId;
|
// result['common.machineId'] = machineId;
|
||||||
// __GDPR__COMMON__ "sessionID" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
result['common.machineId'] = '';
|
||||||
result['sessionID'] = uuid.generateUuid() + Date.now();
|
// // __GDPR__COMMON__ "sessionID" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||||
|
// result['sessionID'] = uuid.generateUuid() + Date.now();
|
||||||
|
result['sessionID'] = '';
|
||||||
|
|
||||||
// __GDPR__COMMON__ "commitHash" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
// __GDPR__COMMON__ "commitHash" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||||
result['commitHash'] = commit;
|
result['commitHash'] = commit;
|
||||||
// __GDPR__COMMON__ "version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
// __GDPR__COMMON__ "version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||||
@@ -32,7 +37,7 @@ export function resolveCommonProperties(commit: string, version: string, machine
|
|||||||
result['common.nodePlatform'] = process.platform;
|
result['common.nodePlatform'] = process.platform;
|
||||||
// __GDPR__COMMON__ "common.nodeArch" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
// __GDPR__COMMON__ "common.nodeArch" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||||
result['common.nodeArch'] = process.arch;
|
result['common.nodeArch'] = process.arch;
|
||||||
|
|
||||||
// {{SQL CARBON EDIT}}
|
// {{SQL CARBON EDIT}}
|
||||||
result['common.application.name'] = product.nameLong;
|
result['common.application.name'] = product.nameLong;
|
||||||
|
|
||||||
|
|||||||
@@ -20,48 +20,34 @@ export function resolveWorkbenchCommonProperties(storageService: IStorageService
|
|||||||
result['common.version.renderer'] = process.versions && (<any>process).versions['chrome'];
|
result['common.version.renderer'] = process.versions && (<any>process).versions['chrome'];
|
||||||
// {{SQL CARBON EDIT}}
|
// {{SQL CARBON EDIT}}
|
||||||
result['common.application.name'] = product.nameLong;
|
result['common.application.name'] = product.nameLong;
|
||||||
getUserId(storageService).then(value => result['common.userId'] = value);
|
// {{SQL CARBON EDIT}}
|
||||||
|
result['common.userId'] = '';
|
||||||
|
|
||||||
const lastSessionDate = storageService.get('telemetry.lastSessionDate');
|
// {{SQL CARBON EDIT}}
|
||||||
const firstSessionDate = storageService.get('telemetry.firstSessionDate') || new Date().toUTCString();
|
// const lastSessionDate = storageService.get('telemetry.lastSessionDate');
|
||||||
storageService.store('telemetry.firstSessionDate', firstSessionDate);
|
// const firstSessionDate = storageService.get('telemetry.firstSessionDate') || new Date().toUTCString();
|
||||||
storageService.store('telemetry.lastSessionDate', new Date().toUTCString());
|
// storageService.store('telemetry.firstSessionDate', firstSessionDate);
|
||||||
|
// storageService.store('telemetry.lastSessionDate', new Date().toUTCString());
|
||||||
|
|
||||||
// __GDPR__COMMON__ "common.firstSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
// // __GDPR__COMMON__ "common.firstSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||||
result['common.firstSessionDate'] = firstSessionDate;
|
// result['common.firstSessionDate'] = firstSessionDate;
|
||||||
// __GDPR__COMMON__ "common.lastSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
// // __GDPR__COMMON__ "common.lastSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||||
result['common.lastSessionDate'] = lastSessionDate;
|
// result['common.lastSessionDate'] = lastSessionDate;
|
||||||
// __GDPR__COMMON__ "common.isNewSession" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
// // __GDPR__COMMON__ "common.isNewSession" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||||
result['common.isNewSession'] = !lastSessionDate ? '1' : '0';
|
// result['common.isNewSession'] = !lastSessionDate ? '1' : '0';
|
||||||
|
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
// __GDPR__COMMON__ "common.instanceId" : { "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" }
|
// __GDPR__COMMON__ "common.instanceId" : { "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" }
|
||||||
result['common.instanceId'] = getOrCreateInstanceId(storageService);
|
// result['common.instanceId'] = getOrCreateInstanceId(storageService);
|
||||||
|
result['common.instanceId'] = '';
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOrCreateInstanceId(storageService: IStorageService): string {
|
|
||||||
const result = storageService.get('telemetry.instanceId') || uuid.generateUuid();
|
|
||||||
storageService.store('telemetry.instanceId', result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
// {{SQL CARBON EDIT}}
|
// {{SQL CARBON EDIT}}
|
||||||
// Get the unique ID for the current user
|
// function getOrCreateInstanceId(storageService: IStorageService): string {
|
||||||
function getUserId(storageService: IStorageService): Promise<string> {
|
// const result = storageService.get('telemetry.instanceId') || uuid.generateUuid();
|
||||||
var userId = storageService.get('common.userId');
|
// storageService.store('telemetry.instanceId', result);
|
||||||
return new Promise<string>(resolve => {
|
// return result;
|
||||||
// Generate the user id if it has not been created already
|
// }
|
||||||
if (typeof userId === 'undefined') {
|
|
||||||
let id = Utils.generateUserId();
|
|
||||||
id.then( newId => {
|
|
||||||
userId = newId;
|
|
||||||
resolve(userId);
|
|
||||||
//store the user Id in the storage service
|
|
||||||
storageService.store('common.userId', userId);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
resolve(userId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -32,58 +32,59 @@ suite('Telemetry - common properties', function () {
|
|||||||
del(parentDir, os.tmpdir(), done);
|
del(parentDir, os.tmpdir(), done);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('default', function () {
|
// {{SQL CARBON EDIT}}
|
||||||
return mkdirp(parentDir).then(() => {
|
// test('default', function () {
|
||||||
fs.writeFileSync(installSource, 'my.install.source');
|
// return mkdirp(parentDir).then(() => {
|
||||||
|
// fs.writeFileSync(installSource, 'my.install.source');
|
||||||
|
|
||||||
return resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource).then(props => {
|
// return resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource).then(props => {
|
||||||
assert.ok('commitHash' in props);
|
// assert.ok('commitHash' in props);
|
||||||
assert.ok('sessionID' in props);
|
// assert.ok('sessionID' in props);
|
||||||
assert.ok('timestamp' in props);
|
// assert.ok('timestamp' in props);
|
||||||
assert.ok('common.platform' in props);
|
// assert.ok('common.platform' in props);
|
||||||
assert.ok('common.nodePlatform' in props);
|
// assert.ok('common.nodePlatform' in props);
|
||||||
assert.ok('common.nodeArch' in props);
|
// assert.ok('common.nodeArch' in props);
|
||||||
assert.ok('common.timesincesessionstart' in props);
|
// assert.ok('common.timesincesessionstart' in props);
|
||||||
assert.ok('common.sequence' in props);
|
// assert.ok('common.sequence' in props);
|
||||||
|
|
||||||
// assert.ok('common.version.shell' in first.data); // only when running on electron
|
// // assert.ok('common.version.shell' in first.data); // only when running on electron
|
||||||
// assert.ok('common.version.renderer' in first.data);
|
// // assert.ok('common.version.renderer' in first.data);
|
||||||
assert.ok('common.osVersion' in props, 'osVersion');
|
// assert.ok('common.osVersion' in props, 'osVersion');
|
||||||
assert.ok('common.platformVersion' in props, 'platformVersion');
|
// assert.ok('common.platformVersion' in props, 'platformVersion');
|
||||||
assert.ok('version' in props);
|
// assert.ok('version' in props);
|
||||||
assert.equal(props['common.source'], 'my.install.source');
|
// assert.equal(props['common.source'], 'my.install.source');
|
||||||
|
|
||||||
// {{SQL CARBON EDIT}}
|
// // {{SQL CARBON EDIT}}
|
||||||
assert.ok('common.application.name' in props);
|
// assert.ok('common.application.name' in props);
|
||||||
|
|
||||||
assert.ok('common.firstSessionDate' in props, 'firstSessionDate');
|
// assert.ok('common.firstSessionDate' in props, 'firstSessionDate');
|
||||||
assert.ok('common.lastSessionDate' in props, 'lastSessionDate'); // conditional, see below, 'lastSessionDate'ow
|
// assert.ok('common.lastSessionDate' in props, 'lastSessionDate'); // conditional, see below, 'lastSessionDate'ow
|
||||||
assert.ok('common.isNewSession' in props, 'isNewSession');
|
// assert.ok('common.isNewSession' in props, 'isNewSession');
|
||||||
|
|
||||||
// machine id et al
|
// // machine id et al
|
||||||
assert.ok('common.instanceId' in props, 'instanceId');
|
// assert.ok('common.instanceId' in props, 'instanceId');
|
||||||
assert.ok('common.machineId' in props, 'machineId');
|
// assert.ok('common.machineId' in props, 'machineId');
|
||||||
|
|
||||||
fs.unlinkSync(installSource);
|
// fs.unlinkSync(installSource);
|
||||||
|
|
||||||
return resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource).then(props => {
|
// return resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource).then(props => {
|
||||||
assert.ok(!('common.source' in props));
|
// assert.ok(!('common.source' in props));
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
test('lastSessionDate when aviablale', function () {
|
// test('lastSessionDate when aviablale', function () {
|
||||||
|
|
||||||
storageService.store('telemetry.lastSessionDate', new Date().toUTCString());
|
// storageService.store('telemetry.lastSessionDate', new Date().toUTCString());
|
||||||
|
|
||||||
return resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource).then(props => {
|
// return resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource).then(props => {
|
||||||
|
|
||||||
assert.ok('common.lastSessionDate' in props); // conditional, see below
|
// assert.ok('common.lastSessionDate' in props); // conditional, see below
|
||||||
assert.ok('common.isNewSession' in props);
|
// assert.ok('common.isNewSession' in props);
|
||||||
assert.equal(props['common.isNewSession'], 0);
|
// assert.equal(props['common.isNewSession'], 0);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
test('values chance on ask', function () {
|
test('values chance on ask', function () {
|
||||||
return resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource).then(props => {
|
return resolveWorkbenchCommonProperties(storageService, commit, version, 'someMachineId', installSource).then(props => {
|
||||||
|
|||||||
@@ -177,7 +177,8 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
|||||||
private createDefaultRecommendedExtensionsListViewDescriptor(): IViewDescriptor {
|
private createDefaultRecommendedExtensionsListViewDescriptor(): IViewDescriptor {
|
||||||
return {
|
return {
|
||||||
id: 'extensions.recommendedList',
|
id: 'extensions.recommendedList',
|
||||||
name: localize('recommendedExtensions', "Recommended"),
|
// {{ SQL CARBON EDIT}}
|
||||||
|
name: localize('recommendedExtensions', "Marketplace"),
|
||||||
location: ViewLocation.Extensions,
|
location: ViewLocation.Extensions,
|
||||||
ctor: RecommendedExtensionsView,
|
ctor: RecommendedExtensionsView,
|
||||||
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('defaultRecommendedExtensions')),
|
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('defaultRecommendedExtensions')),
|
||||||
|
|||||||
@@ -234,6 +234,9 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
|||||||
return this.getAllRecommendationsModel(query, options);
|
return this.getAllRecommendationsModel(query, options);
|
||||||
} else if (ExtensionsListView.isRecommendedExtensionsQuery(query.value)) {
|
} else if (ExtensionsListView.isRecommendedExtensionsQuery(query.value)) {
|
||||||
return this.getRecommendationsModel(query, options);
|
return this.getRecommendationsModel(query, options);
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
|
} else if (ExtensionsListView.isAllMarketplaceExtensionsQuery(query.value)) {
|
||||||
|
return this.getAllMarketplaceModel(query, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
let text = query.value;
|
let text = query.value;
|
||||||
@@ -363,6 +366,42 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
|
private getAllMarketplaceModel(query: Query, options: IQueryOptions): TPromise<IPagedModel<IExtension>> {
|
||||||
|
const value = query.value.trim().toLowerCase();
|
||||||
|
return this.extensionsWorkbenchService.queryLocal()
|
||||||
|
.then(result => result.filter(e => e.type === LocalExtensionType.User))
|
||||||
|
.then(local => {
|
||||||
|
return this.tipsService.getOtherRecommendations().then((recommmended) => {
|
||||||
|
const installedExtensions = local.map(x => `${x.publisher}.${x.name}`);
|
||||||
|
options = assign(options, { text: value, source: 'searchText' });
|
||||||
|
return TPromise.as(this.extensionsWorkbenchService.queryGallery(options).then((pager) => {
|
||||||
|
// filter out installed extensions
|
||||||
|
pager.firstPage = pager.firstPage.filter((p) => {
|
||||||
|
return installedExtensions.indexOf(`${p.publisher}.${p.name}`) === -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// sort the marketplace extensions
|
||||||
|
pager.firstPage.sort((a, b) => {
|
||||||
|
let isRecommendedA: boolean = recommmended.indexOf(`${a.publisher}.${a.name}`) > -1;
|
||||||
|
let isRecommendedB: boolean = recommmended.indexOf(`${b.publisher}.${b.name}`) > -1;
|
||||||
|
|
||||||
|
// sort recommeded extensions before other extensions
|
||||||
|
if (isRecommendedA !== isRecommendedB) {
|
||||||
|
return (isRecommendedA && !isRecommendedB) ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise sort by name
|
||||||
|
return a.displayName.toLowerCase() < b.displayName.toLowerCase() ? -1 : 1;
|
||||||
|
});
|
||||||
|
pager.total = pager.firstPage.length;
|
||||||
|
pager.pageSize = pager.firstPage.length;
|
||||||
|
return new PagedModel(pager || []);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Given all recommendations, trims and returns recommendations in the relevant order after filtering out installed extensions
|
// Given all recommendations, trims and returns recommendations in the relevant order after filtering out installed extensions
|
||||||
private getTrimmedRecommendations(installedExtensions: string[], value: string, fileBasedRecommendations: string[], otherRecommendations: string[], workpsaceRecommendations: string[], ) {
|
private getTrimmedRecommendations(installedExtensions: string[], value: string, fileBasedRecommendations: string[], otherRecommendations: string[], workpsaceRecommendations: string[], ) {
|
||||||
const totalCount = 8;
|
const totalCount = 8;
|
||||||
@@ -524,6 +563,11 @@ export class ExtensionsListView extends ViewsViewletPanel {
|
|||||||
static isKeymapsRecommendedExtensionsQuery(query: string): boolean {
|
static isKeymapsRecommendedExtensionsQuery(query: string): boolean {
|
||||||
return /@recommended:keymaps/i.test(query);
|
return /@recommended:keymaps/i.test(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
|
static isAllMarketplaceExtensionsQuery(query: string): boolean {
|
||||||
|
return /@allmarketplace/i.test(query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InstalledExtensionsView extends ExtensionsListView {
|
export class InstalledExtensionsView extends ExtensionsListView {
|
||||||
@@ -560,7 +604,8 @@ export class BuiltInExtensionsView extends ExtensionsListView {
|
|||||||
export class RecommendedExtensionsView extends ExtensionsListView {
|
export class RecommendedExtensionsView extends ExtensionsListView {
|
||||||
|
|
||||||
async show(query: string): TPromise<IPagedModel<IExtension>> {
|
async show(query: string): TPromise<IPagedModel<IExtension>> {
|
||||||
return super.show(!query.trim() ? '@recommended:all' : '@recommended');
|
// {{SQL CARBON EDIT}}
|
||||||
|
return super.show('@allmarketplace');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user