Compare commits

..

107 Commits

Author SHA1 Message Date
Anthony Dresser
1f3e59c9f9 added default config for timeSeries since it is not the same (#1852) 2018-07-06 13:21:12 -07:00
Karl Burtram
1956078c8c Update config.json 2018-07-06 12:50:06 -07:00
Leila Lali
11230f59fc vbump sqlops version (#1862) 2018-07-06 09:43:06 -07:00
Karl Burtram
21bad7a01f Refresh agent dashboard panel after create\update\delete operations (#1861)
* Edit alert WIP

* A couple alert edit bugs

* Hook up dashboard refresh notification

* Hook onchange event to other agent service calls

* Switch update handler to scalar value

* Add null check on handler callback
2018-07-06 08:57:30 -07:00
Aditya Bist
6f9a27ecc7 Agent - UI changes (#1859)
* fixed regressions, added separator

* finished operator dialog UI
2018-07-05 20:31:54 -07:00
Matt Irvine
c504113d13 Update card layout to give more icon space (#1858) 2018-07-05 14:58:46 -07:00
Karl Burtram
c92ff60592 Update Agent extension package-lock.json (#1857)
* Update Agent extension package-lock.json

* Try removing the pacakge-lock file
2018-07-05 14:30:55 -07:00
Karl Burtram
e9013d1a2a Bump SQL Tools Service to 1.5.0-alpha.3 2018-07-05 13:34:27 -07:00
Matt Irvine
9c4580fe40 Add grouping feature for model view forms (#1853) 2018-07-05 11:36:29 -07:00
Anthony Dresser
cb060cb5db add row status on status bar for queries (#1841) 2018-07-05 10:43:24 -07:00
Karl Burtram
6c3d85cc45 New Operator, Alert and Proxy request handlers (#1846)
* Add agent dialog class

* Rename agent dialog data classes

* Alert dialog data updates

* Create operator and proxy handlers
2018-07-05 08:26:03 -07:00
Aditya Bist
14ae89e87c added common action bar with context based actions for all pages (#1842) 2018-07-03 20:32:04 -07:00
Karl Burtram
24c48f025d Add Delete Alert action implementation (#1840)
* Delete alert WIP 1

* Add agent delete methods

* Add Delete implementation for Jobs, Operators, and Proxies
2018-07-03 19:58:02 -07:00
Anthony Dresser
f0a556f004 add quote to string escape (#1838) 2018-07-03 16:37:44 -07:00
Karl Burtram
fd4d6abb4d Update CHANGELOG.md 2018-07-03 14:01:52 -07:00
Anthony Dresser
41cc839380 add themeing to profiler (#1826) 2018-07-03 13:34:20 -07:00
Karl Burtram
c2a4380b96 Rename Agent dialog classes to remove "Create" (#1837)
* Remove Create from agent dialog names

* Move actions class into common directory
2018-07-03 13:30:16 -07:00
Aditya Bist
6f402ac79f Agent : New Step dialog (#1834)
* added file browser tree to API and dialog

* added callbacks for selected files

* added file browser to step dialog

* remove commented code

* fixed file name bug
2018-07-03 13:07:02 -07:00
Karl Burtram
bf7c1306b1 Agent Tab panel visibility check base class (#1829)
* Agent Tab panel visibility check base class

* Add context menu to tab panel tables for Edit and Delete
2018-07-03 12:42:37 -07:00
Matt Irvine
c1509cf09d Update button icon when icon path changes (#1833) 2018-07-03 11:58:43 -07:00
Anthony Dresser
014bca031c add logic to clean up providers when appropriate (#1824) 2018-07-03 11:49:57 -07:00
Anthony Dresser
4f864fd5bd add escape formatting (#1825) 2018-07-03 11:49:17 -07:00
JASM2007
2da67567e4 Update mssql.JSON (#1803)
I modified lots of snippets to include the database name so that the user can additionally tab through the database name. I made some quality of life change for the user.  I normalized and standardized snippets to look more like one user created the file rather than each script taking on various flavors from the contributors. I tried to write the comments in the snippets in the same style at the majority of the snippets.  I added highlighting in key areas that were missing it or were not replacing the comments. Additionally there was no snippets for sqlCreateIndex and sqlCreateTempTable which I find standard for SQL users.  Let me know your thoughts! No trying to offend anyone if I changed your code.
2018-07-03 08:42:56 -07:00
Karl Burtram
5b19d2b1fc Fill out controls for Alert, Operator and Proxy dialogs (#1827)
* Clean up alert dialog

* Fill in controls for operator and proxy dialogs
2018-07-03 07:34:39 -07:00
Karl Burtram
a6837dcd40 Remove "client" folder in Agent extension (#1820)
* Remove "client" folder in Agent extension

* Alert dialog cleanups
2018-07-02 14:47:00 -07:00
Matt Irvine
af80751a1f Fix linked account error message typo (#1823) 2018-07-02 14:14:10 -07:00
Matt Irvine
dd02597c3b Fix display problems with model view icon components (#1822) 2018-07-02 14:03:22 -07:00
Matt Irvine
2926a3cbd8 Update query editor connection display when connecting via API (#1819) 2018-07-02 13:10:19 -07:00
Matt
b02bb3bfd4 Fixes implicit any type - issue #1814 (#1815) 2018-07-02 09:09:40 -07:00
Karl Burtram
67a4683bb1 Bump Agent and Profiler extension versions (#1812) 2018-06-30 23:02:00 -07:00
Peter Schneider
9baee1c22c Changed the stored procedure call to work on case sensitive instances (#1809) 2018-06-30 15:48:15 -07:00
Karl Burtram
8cb67b4f9d Add Alert, Operator and Proxy panel tabs (#1811)
* Add Create Alert dialog

* Add Job Alerts view

* Stage WIP

* Add Proxy View component

* Hook up proxy and operator view callbacks

* Style cleanup

* Add additonal columns to views
2018-06-30 15:31:40 -07:00
Matt Irvine
0cd47bc328 Fix webview editor height issue (#1808) 2018-06-29 19:13:22 -07:00
Kevin Cunnane
07fb58d5e1 Fixes #1804 Dashboard Home tab should be overrideable for other connection providers (#1805)
- Adds new isHomeTab property. If set, this indicates a tab should override the default home tab.
2018-06-29 18:49:56 -07:00
Anthony Dresser
2d80d5e611 Add connection info to title (#1645)
* add connection info to the file label

* formatting

* first attempt at title shortening

* add user to title shortening

* add settings to control connection info in title

* formatting

* move setting
2018-06-29 17:04:09 -07:00
Anthony Dresser
4bd63b615b fix table highlighting issues (#1802) 2018-06-29 17:03:01 -07:00
Kevin Cunnane
335f667507 Fix null ref error when no database or server node are in object tree (#1790)
- Also fixed minor issue where `var` was used instead of `let` in a related file, since this is discouraged in our codebase.
2018-06-29 16:49:46 -07:00
Madeline MacDonald
1819036d7d Profiler Keybindings (#1801)
* Added default new profiler keybinding. Exposed profiler.start and profiler.stop so they can be used with keybindings

* Changing where session state gets set

* Cleaning up unnecessary code
2018-06-29 16:34:40 -07:00
Kevin Cunnane
4f76f116ac Ensure dashboard tabs only show for supported providers (#1798)
- Fixes #1764, fixes #1765
- Fixes bug where configModifiers were passed in the dashboard service in some places, and a different object with incompatible / missing fields in a different one. Now it always passes in the owner object, and this should have the required fields
 - Note: this doesn't fix the problem where the code does not fail to build, which I would have expected to be the case.
- Adds "provider" as an option for TabConfig including in the schema
- Filter by "provider" type when loading tabs. If provider is not set for a tab, will assume "MSSQL" since it's the default provider. This is a design decision - without this, we would either have to disable tabs that don't show this or have them incorrectly show for non-MSSQL provider types

Notes:

Does not override behavior for individual widgets, which still get the current provider set as their provider if not specified. This seems to be required so should be fine.
We will fix tasks/widgets that aren't supported by a provider showing up in a separate PR, since it's a different issue
2018-06-29 16:04:57 -07:00
Matt Irvine
1eba7c7d2a Fix model view input box bugs (#1797) 2018-06-29 14:20:06 -07:00
Karl Burtram
83234dd52c Update SQL Tools Service to 1.5.0-alpha.1 2018-06-29 11:09:24 -07:00
Aditya Bist
bae23b7fce Agent: UI/UX finishes and clean up (#1768)
* changed columnw widths

* fixed scrolling for prev run job history list

* fixed all resizing and scrolling issues in agent

* removed commented code

* code review comments
2018-06-28 10:12:02 -07:00
Matt Irvine
3db61eaa82 Add sqlservices wizard sample (#1769) 2018-06-28 10:06:48 -07:00
Matt Irvine
5cf85a0361 Display page number, title, and description in wizard page headers (#1766) 2018-06-27 16:26:26 -07:00
Anthony Dresser
ffe27f5bde Tab outline in dashboard (#1742)
* add border to dashboard tabs

* formatting
2018-06-27 14:33:58 -07:00
Anthony Dresser
78bcd9d54c bump slickgrid (#1758) 2018-06-27 14:29:18 -07:00
Anthony Dresser
1a9797f0ff Change default settings to remove error (#1741)
* change default settings

* formatting
2018-06-27 13:59:36 -07:00
Karl Burtram
0de94ff8a4 Add "Schedule Picker" dialog (#1759)
* Merge master

* Add pick schedule dialog 1

* Add Pick Schedule button to create job dialog

* Cleanup Pick Schedule dialog

* Hook-up onClose event for Pick Schedule

* Code review feedback
2018-06-27 13:42:08 -07:00
Kevin Cunnane
472233d9a7 Providers without metadata service or serverInfo shouldn't break dashboard (#1761)
- Fix a number of issues that arose while testing a provider without a metadata service or serverInfo object returned via DMP calls. These should be optional services/features and we should be resilient to them not existing. In most places we already have these checks
- This does not cover a number of "improvement" scenarios, such as filtering extension tabs by provider, and defaulting any tabs that don't specify a provider to be MSSQL. This and some other features to ensure things make sense will be implemented in separate PRs but this unblocked the scenario
2018-06-27 13:40:37 -07:00
Aditya Bist
f69e31b0d5 Jobs/new step (#1734)
* added jobs view toolbar

* create job command and dialog stub

* add tab content and wire up the provider

* fix the steps tab error

* create job dialog 6/15 changes

* general tab done

* success action and retries completed

* added failure action dropdown

* add notification tab checkbox events

* added AgentJobStepInfo objects in sqlops

* create job dialog - 0618 update 1

* added model save function

* width for controls and initial state for notification tab controls

* refresh master and changes to work with latest code

* fixed next and prev button positions

* new step dialog ui finished

* implemented parse button

* fix package file

* add validation and sub-items collections

* hook up the step creation dialog - step 1

* merged master

* fixed step issue, step can me made now
2018-06-26 19:11:59 -07:00
Karl Burtram
60b696cc31 Fix casing for resourceprovider module require (#1731)
* Fix casing for resourceprovider module require

* Rename files to avoid build output recasing on macos
2018-06-26 18:37:31 -07:00
Leila Lali
549037f744 fixing model view issues (#1737)
* fixing model view issues
2018-06-26 16:40:41 -07:00
Karl Burtram
ca5e1e6133 Fix a couple references to VS Code in UI (#1730) 2018-06-26 15:30:11 -07:00
Aditya Bist
3e3ff163db Jobs - New step (WIP) (#1711)
* added jobs view toolbar

* create job command and dialog stub

* add tab content and wire up the provider

* fix the steps tab error

* create job dialog 6/15 changes

* general tab done

* success action and retries completed

* added failure action dropdown

* add notification tab checkbox events

* added AgentJobStepInfo objects in sqlops

* create job dialog - 0618 update 1

* added model save function

* width for controls and initial state for notification tab controls

* refresh master and changes to work with latest code

* fixed next and prev button positions

* new step dialog ui finished

* implemented parse button

* fix package file

* add validation and sub-items collections

* hook up the step creation dialog - step 1

* merged master
2018-06-26 11:09:41 -07:00
Matt Irvine
ca755365ce Fix dialog/wizard undefined connectionInfo bug (#1725) 2018-06-25 18:26:25 -07:00
Matt Irvine
ea979de19f Disable wizard/dialog next/done buttons when page not valid (#1708) 2018-06-25 12:25:48 -07:00
Anthony Dresser
473ddfcdf1 Modifying angular bootstrap to add injection at the module level (#1691)
* work on fixing injection

* change bootstrapping method

* add a catch for testing

* remove unneeded code
2018-06-22 16:09:13 -07:00
Leila Lali
a627285a4c Feature/selectable card component (#1703)
* added selectable card

* creating new card type
2018-06-22 14:25:21 -07:00
Alan Ren
322847469d fix dropdown component issue (#1709)
* fix dropdown component issue

* handle enable property default
2018-06-21 18:00:07 -07:00
Matt Irvine
6c5fac997f Add dialog close validation (#1704) 2018-06-21 16:55:47 -07:00
Leila Lali
1871fd383e Feature/form improvements (#1707)
* adding more options for form

* added form requied and info for item
2018-06-21 15:26:52 -07:00
Matt Irvine
f5b147ca4b Add info/warning/error messages for wizards and dialogs (#1696) 2018-06-21 11:55:23 -07:00
Anthony Dresser
9d2b206156 Accessibility enhancements (#1678)
* adds aria-label to inputs for connection

* formatting

* add ariaLabels to all checkboxes/inputboxes/dropdowns

* fix labels on database dropdown action

* fix compile errors

* remove unnecessary code
2018-06-20 16:56:19 -07:00
Karl Burtram
a372c76e07 Update SQL Ops to 0.31.1 for July iteration 2018-06-20 12:52:37 -07:00
Karl Burtram
b1ce07d3ae Add SQL CARBON EDIT tag in product class (#1690) 2018-06-20 12:42:32 -07:00
Karl Burtram
bc09fb30d8 Updaet readme for June release (#1684) 2018-06-19 22:27:32 -07:00
Leila Lali
c13f219318 form should not default to100% by default (#1679) 2018-06-19 14:12:04 -07:00
Madeline MacDonald
6b018c5d06 Profiler toolbar fixes + creating connections (#1677)
* Fixing button enabling, connection selection

* Fixing spacing
2018-06-19 13:54:47 -07:00
Anthony Dresser
e69158d9b2 Swallow error on context view when disposing children (#1663)
* swallow error on context view  when disposing children

* fix edit comment
2018-06-19 12:22:25 -07:00
Madeline MacDonald
520cfb780a Profiler notifications (#1648)
* adding lost events flag to events available notification

* Initial changes to support notifications for stopped session and lost events

* Updated localized strings & send stop notification box

* reordering imports

* vbump sqltools & dataprotocolclient, fix notification wording
2018-06-19 11:23:01 -07:00
Leila Lali
e686fed209 fixing the table, form dialog tab layout issues (#1671) 2018-06-19 10:55:23 -07:00
Karl Burtram
80ab19ac23 Add screenshot to Profiler extension readme (#1670)
* Add screenshot to Profiler extension readme

* Fix up the readme text a little
2018-06-19 10:09:34 -07:00
Matt Irvine
25228fa58e Fix edit data limit dropdown position (#1667) 2018-06-18 20:27:25 -04:00
Karl Burtram
676d35090f Fix Release Notes link to refer to correct location (#1665)
* Fix Release Notes link to refer to correct location

* Fix typo
2018-06-18 20:18:24 -04:00
Karl Burtram
e1a36a356c Add Profiler extension to recommended list (#1662) 2018-06-18 19:30:16 -04:00
Matt Irvine
3274c0b734 Make height and width optional for model view components (#1657) 2018-06-18 16:27:57 -07:00
Karl Burtram
9c95e1289f Bump SQL Ops to 0.30.6 2018-06-18 15:37:17 -07:00
Karl Burtram
ef29871b62 Pickup SQL Tools Service 1.4.0-alpha.45 (#1649) 2018-06-18 13:06:00 -07:00
Leila Lali
a2a87f8d2b fixed two layout issues in model view components (#1647) 2018-06-18 12:23:54 -07:00
Matt Irvine
83c01c6bcb Fix edit data revert row bugs (#1634)
* Clean up edit data revert behavior

* Unify escape and revert row edit data handling

* Revert unedited new row when clicking off of it

* Make delete work when adding new row
2018-06-18 12:17:16 -07:00
Aditya Bist
c1d850804c Agent: Added highlighting for jobs (#1588)
* added highlighting for jobs

* cleaner code with theme compatibility
2018-06-18 10:55:40 -07:00
Aditya Bist
6590d5f58a fixed refresh and removed unused imports (#1633) 2018-06-18 10:54:15 -07:00
Leila Lali
a225925bc4 fixed several model view issues (#1640)
* fixed several model view issues
2018-06-15 16:59:12 -07:00
Matt Irvine
ab39f1f44f Use theme color for model view button text (#1639) 2018-06-15 15:29:15 -07:00
Madeline MacDonald
8d89364d72 Displaying all event data (#1635)
* Displaying all event data

* Fixing viewing blank text panels and highlighting

* add aria-label for insights dialog (#1629)

* Displaying all event data

* Fixing viewing blank text panels and highlighting

* descriptive comments, removing redundant code
2018-06-15 14:16:42 -07:00
Madeline MacDonald
af2bc859d1 Profiler toolbar changes (#1615)
* Clear event view pane when starting new session

* Removing debug logging

* Handling pause button

* Cleaning up comments from previous commit

* Fixing comments from PR

* Fixing a comment I forgot to delete

* Bumping dataprotocolclient version

* Bumping sqltoolsservice & sqlops-dataprotocolclient version
2018-06-15 13:39:46 -07:00
Anthony Dresser
8e72fdaa52 add aria-label for insights dialog (#1629) 2018-06-14 15:40:57 -07:00
Matt Irvine
e9661f90d0 Prevent components from being defined multiple times (#1627) 2018-06-13 16:51:06 -07:00
Leila Lali
30b111034d added option to mode view input box to create the input as text area (#1630)
* added option to create input as multi line

* added min and max for input box
2018-06-13 16:32:34 -07:00
Leila Lali
df18359309 fixed the issue with disabling the select box (#1625) 2018-06-13 15:23:59 -07:00
Matt Irvine
03857e0afd Update SQL select box styler (#1619) 2018-06-12 17:38:02 -07:00
Matt Irvine
eaf1e08752 Fix bug where select box selection could not be changed by mouse (#1616) 2018-06-12 14:48:58 -07:00
Leila Lali
d39ceffa94 fixed the issue with displaying licenses (#1610) 2018-06-12 13:41:59 -07:00
Anthony Dresser
406b171c66 add a check for when the task is not valid (#1607) 2018-06-12 10:48:49 -07:00
Aditya Bist
6d89b9e203 Agent: Previous Runs chart functionality (#1564)
* added basic prev run chart functionality

* removed dead code

* resizing columns generates charts again
2018-06-12 09:28:40 -07:00
Karl Burtram
733bb69d25 Add Agent Service configuration request methods (#1608)
* WIP 1

* WIP 2

* Add Agent Service configuration request methods

* Fix typo

* Tabify sqlops.d.ts.
2018-06-11 16:32:18 -07:00
Leila Lali
4609694141 added Declarative table to model view controls (#1593)
* added Declarative table to model view controls
2018-06-11 12:40:17 -07:00
Anthony Dresser
3be0c5130a Fixes accessibility for grid (#1592)
* adds neccessary code to read cell value for table cells

* formatting
2018-06-11 12:06:06 -07:00
Abbie Petchtes
e50b512580 fix keyboard issues in editable dropdown (#1600) 2018-06-11 09:28:27 -07:00
Karl Burtram
eb62d054de Update package.json 2018-06-08 18:05:16 -07:00
Aditya Bist
e870a309c0 Agent: Filtering & Sorting (#1441)
* have a working filter

* fixed error details when filtering

* filtering with styling done

* fix transition styling

* fixed more styling issues

* optimized errors when switching pages

* added sorting functionality

* removed dead code

* fixed styling issues when sorting

* added sorting with styling for every column

* code review comments

* fixed styling issue when a bigger filter was applied, followed by a smaller one and then cleared

* use absolute paths in imports

* fixed issues with styling when sorting is performed on a filtered dataset
2018-06-08 17:08:01 -07:00
Leila Lali
3afd3b0ff3 fixed the compile error (#1601) 2018-06-08 15:47:29 -07:00
Matt Irvine
e3a2ed95d4 Add wizard navigation validator (#1587) 2018-06-08 15:32:37 -07:00
Matt Irvine
20c4f085c8 Fix custom UI validation bugs (#1583) 2018-06-08 15:32:21 -07:00
Leila Lali
02af7e9299 Feature/model view list (#1551)
* fixed the issue with drop down styling

* added list box to model view library
2018-06-08 13:51:02 -07:00
Abbie Petchtes
0ae9b36d93 fix colors issues in dashboard (#1591) 2018-06-08 11:55:31 -07:00
Matt Irvine
2bbb2842e5 Fix model view component updateProperties implementation (#1586) 2018-06-08 09:41:00 -07:00
225 changed files with 10897 additions and 4110 deletions

View File

@@ -38,5 +38,6 @@
}
}
],
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"git.ignoreLimitWarning": true
}

View File

@@ -1,5 +1,18 @@
# Change Log
## Version 0.30.6
* Release date: June 20, 2018
* Release status: Public Preview
## What's new in this version
* **SQL Server Profiler for SQL Operations Studio *Preview*** extension initial release
* The new **SQL Data Warehouse** extension includes rich customizable dashboard widgets surfacing insights to your data warehouse. This unlocks key scenarios around managing and tuning your data warehouse to ensure it is optimized for consistent performance.
* **Edit Data "Filtering and Sorting"** support
* **SQL Server Agent for SQL Operations Studio *Preview*** extension enhancements for Jobs and Job History views
* Improved **Wizard & Dialog UI Builder Framework** extensibility APIs
* Update VS Code Platform source code integrating [March 2018 (1.22)](https://code.visualstudio.com/updates/v1_22) and [April 2018 (1.23)](https://code.visualstudio.com/updates/v1_23) releases
* Fix GitHub Issues
## Version 0.29.3
* Release date: May 7, 2018
* Release status: Public Preview
@@ -9,7 +22,7 @@ The May release is focused on stabilization and bug fixes leading up to the Buil
* Announcing **Redgate SQL Search** extension available in Extension Manager
* Community Localization available for 10 languages: **German, Spanish, French, Italian, Japanese, Korean, Portuguese, Russian, Simplified Chinese and Traditional Chinese!**
* **GDPR-compliant** build has reduced telemetry collection, improved [opt-out](https://github.com/Microsoft/sqlopsstudio/wiki/How-to-Disable-Telemetry-Reporting) experience and in-product links to [Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement)
* Reduced telemetry collection, improved [opt-out](https://github.com/Microsoft/sqlopsstudio/wiki/How-to-Disable-Telemetry-Reporting) experience and in-product links to [Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement)
* Extension Manager has improved Marketplace experience to easily discover community extensions
* SQL Agent extension Jobs and Job History view improvement
* Updates for **whoisactive** and **Server Reports** extensions

View File

@@ -4,16 +4,16 @@
SQL Operations Studio is a data management tool that enables you to work with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
**Download SQL Operations Studio May Public Preview**
**Download SQL Operations Studio June Public Preview**
Platform | Link
-- | --
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=873386
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=873387
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=873388
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=873389
Linux RPM | https://go.microsoft.com/fwlink/?linkid=873390
Linux DEB | https://go.microsoft.com/fwlink/?linkid=873391
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=875602
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=875603
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=875604
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=875605
Linux RPM | https://go.microsoft.com/fwlink/?linkid=875606
Linux DEB | https://go.microsoft.com/fwlink/?linkid=875607
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
@@ -61,6 +61,7 @@ The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.micro
## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* lanceklinger `Fix for double clicking column handle in results table #1504`
* westerncj for `Removed duplicate contribution from README.md (#753)`
* ntovas for `Fix for duplicate extensions shown in "Save File" dialog. (#779)`
* SebastianPfliegel for `Add cursor snippet (#475)`

View File

@@ -1,38 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import * as data from 'sqlops';
import { ApiWrapper } from './apiWrapper';
/**
* The main controller class that initializes the extension
*/
export class MainController {
protected _apiWrapper: ApiWrapper;
protected _context: vscode.ExtensionContext;
// PUBLIC METHODS //////////////////////////////////////////////////////
public constructor(context: vscode.ExtensionContext, apiWrapper?: ApiWrapper) {
this._apiWrapper = apiWrapper || new ApiWrapper();
this._context = context;
console.log('Got: ' + apiWrapper);
}
/**
* Deactivates the extension
*/
public deactivate(): void {
}
public activate(): void {
this._apiWrapper.registerWebviewProvider('data-management-agent', webview => {
webview.html = '<div><h1>SQL Agent</h1></div>';
});
}
}

View File

@@ -1,23 +1,20 @@
{
"name": "agent",
"displayName": "SQL Server Agent",
"description": "Manage and troubleshoot SQL Server Agent jobs (early preview)",
"version": "0.29.0",
"description": "Manage and troubleshoot SQL Server Agent jobs",
"version": "0.31.1",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
"icon": "images/sqlserver.png",
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
"engines": {
"vscode": "0.10.x"
"vscode": "0.10.x"
},
"activationEvents": [
"*"
],
"main": "./client/out/main",
"scripts": {
"compile": "gulp compile-extension:agent-client"
},
"main": "./out/main",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/sqlopsstudio.git"
@@ -29,22 +26,32 @@
"outputChannels": [
"sqlagent"
],
"commands": [
{
"command": "agent.openNewStepDialog",
"title": "agent.openNewStepDialog"
}
],
"dashboard.tabs": [
{
"id": "data-management-agent",
"description": "Manage and troubleshoot SQL Agent jobs",
"provider": "MSSQL",
"title": "SQL Agent",
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
"container": {
"controlhost-container": {
"type": "agent"
}
}
{
"id": "data-management-agent",
"description": "Manage and troubleshoot SQL Agent jobs",
"provider": "MSSQL",
"title": "SQL Agent",
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
"container": {
"controlhost-container": {
"type": "agent"
}
}
}
]
},
"dependencies": {
"vscode-nls": "^3.2.1"
},
"devDependencies": {
"vscode": "1.0.1"
}
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7"
}
}

View File

@@ -0,0 +1,38 @@
'use strict';
import * as sqlops from 'sqlops';
export class AgentUtils {
private static _agentService: sqlops.AgentServicesProvider;
private static _connectionService: sqlops.ConnectionProvider;
private static _queryProvider: sqlops.QueryProvider;
public static async getAgentService(): Promise<sqlops.AgentServicesProvider> {
if (!AgentUtils._agentService) {
let currentConnection = await sqlops.connection.getCurrentConnection();
this._agentService = sqlops.dataprotocol.getProvider<sqlops.AgentServicesProvider>(currentConnection.providerName, sqlops.DataProviderType.AgentServicesProvider);
}
return AgentUtils._agentService;
}
public static async getDatabases(ownerUri: string): Promise<string[]> {
if (!AgentUtils._connectionService) {
let currentConnection = await sqlops.connection.getCurrentConnection();
this._connectionService = sqlops.dataprotocol.getProvider<sqlops.ConnectionProvider>(currentConnection.providerName, sqlops.DataProviderType.ConnectionProvider);
}
return AgentUtils._connectionService.listDatabases(ownerUri).then(result => {
if (result && result.databaseNames && result.databaseNames.length > 0) {
return result.databaseNames;
}
});
}
public static async getQueryProvider(): Promise<sqlops.QueryProvider> {
if (!AgentUtils._queryProvider) {
let currentConnection = await sqlops.connection.getCurrentConnection();
this._queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(currentConnection.providerName, sqlops.DataProviderType.QueryProvider);
}
return this._queryProvider;
}
}

View 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.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as vscode from 'vscode';
import * as sqlops from 'sqlops';
import { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
const localize = nls.loadMessageBundle();
export class AlertData implements IAgentDialogData {
ownerUri: string;
dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
id: number;
name: string;
originalName: string;
delayBetweenResponses: number;
eventDescriptionKeyword: string;
eventSource: string;
hasNotification: number;
includeEventDescription: string;
isEnabled: boolean;
jobId: string;
jobName: string;
lastOccurrenceDate: string;
lastResponseDate: string;
messageId: number;
notificationMessage: string;
occurrenceCount: number;
performanceCondition: string;
severity: number;
databaseName: string;
countResetDate: string;
categoryName: string;
alertType: string;
wmiEventNamespace: string;
wmiEventQuery: string;
constructor(ownerUri:string, alertInfo: sqlops.AgentAlertInfo) {
this.ownerUri = ownerUri;
if (alertInfo) {
this.dialogMode = AgentDialogMode.EDIT;
this.id = alertInfo.id;
this.name = alertInfo.name;
this.originalName = alertInfo.name;
this.delayBetweenResponses = alertInfo.delayBetweenResponses;
this.eventDescriptionKeyword = alertInfo.eventDescriptionKeyword;
this.eventSource = alertInfo.eventSource;
this.hasNotification = alertInfo.hasNotification;
this.includeEventDescription = alertInfo.includeEventDescription.toString();
this.isEnabled = alertInfo.isEnabled;
this.jobId = alertInfo.jobId;
this.jobName = alertInfo.jobName;
this.lastOccurrenceDate = alertInfo.lastOccurrenceDate;
this.lastResponseDate = alertInfo.lastResponseDate;
this.messageId = alertInfo.messageId;
this.notificationMessage = alertInfo.notificationMessage;
this.occurrenceCount = alertInfo.occurrenceCount;
this.performanceCondition = alertInfo.performanceCondition;
this.severity = alertInfo.severity;
this.databaseName = alertInfo.databaseName;
this.countResetDate = alertInfo.countResetDate;
this.categoryName = alertInfo.categoryName;
this.alertType = alertInfo.alertType.toString();
this.wmiEventNamespace = alertInfo.wmiEventNamespace;
this.wmiEventQuery = alertInfo.wmiEventQuery;
}
}
public async initialize() {
}
public async save() {
let agentService = await AgentUtils.getAgentService();
let result = this.dialogMode === AgentDialogMode.CREATE
? await agentService.createAlert(this.ownerUri, this.toAgentAlertInfo())
: await agentService.updateAlert(this.ownerUri, this.originalName, this.toAgentAlertInfo());
if (!result || !result.success) {
vscode.window.showErrorMessage(
localize('alertData.saveErrorMessage', "Alert update failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
}
}
public toAgentAlertInfo(): sqlops.AgentAlertInfo {
return {
id: this.id,
name: this.name,
delayBetweenResponses: this.delayBetweenResponses,
eventDescriptionKeyword: this.eventDescriptionKeyword,
eventSource: this.eventSource,
hasNotification: this.hasNotification,
includeEventDescription: sqlops.NotifyMethods.none, // this.includeEventDescription,
isEnabled: this.isEnabled,
jobId: this.jobId,
jobName: this.jobName,
lastOccurrenceDate: this.lastOccurrenceDate,
lastResponseDate: this.lastResponseDate,
messageId: this.messageId,
notificationMessage: this.notificationMessage,
occurrenceCount: this.occurrenceCount,
performanceCondition: this.performanceCondition,
severity: this.severity,
databaseName: this.databaseName,
countResetDate: this.countResetDate,
categoryName: this.categoryName,
alertType: sqlops.AlertType.sqlServerEvent, //this.alertType,
wmiEventNamespace: this.wmiEventNamespace,
wmiEventQuery: this.wmiEventQuery
};
}
}

View File

@@ -0,0 +1,151 @@
/*---------------------------------------------------------------------------------------------
* 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 { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class JobData implements IAgentDialogData {
private readonly JobCompletionActionCondition_Always: string = 'When the job completes';
private readonly JobCompletionActionCondition_OnFailure: string = 'When the job fails';
private readonly JobCompletionActionCondition_OnSuccess: string = 'When the job succeeds';
// Error Messages
private readonly CreateJobErrorMessage_NameIsEmpty = 'Job name must be provided';
private _ownerUri: string;
private _jobCategories: string[];
private _operators: string[];
private _defaultOwner: string;
private _jobCompletionActionConditions: sqlops.CategoryValue[];
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
public name: string;
public enabled: boolean = true;
public description: string;
public category: string;
public categoryId: number;
public owner: string;
public emailLevel: sqlops.JobCompletionActionCondition = sqlops.JobCompletionActionCondition.OnFailure;
public pageLevel: sqlops.JobCompletionActionCondition = sqlops.JobCompletionActionCondition.OnFailure;
public eventLogLevel: sqlops.JobCompletionActionCondition = sqlops.JobCompletionActionCondition.OnFailure;
public deleteLevel: sqlops.JobCompletionActionCondition = sqlops.JobCompletionActionCondition.OnSuccess;
public operatorToEmail: string;
public operatorToPage: string;
public jobSteps: sqlops.AgentJobStepInfo[];
public jobSchedules: sqlops.AgentJobScheduleInfo[];
public alerts: sqlops.AgentAlertInfo[];
constructor(ownerUri: string, private _agentService: sqlops.AgentServicesProvider = null) {
this._ownerUri = ownerUri;
}
public get jobCategories(): string[] {
return this._jobCategories;
}
public get operators(): string[] {
return this._operators;
}
public get ownerUri(): string {
return this._ownerUri;
}
public get defaultOwner(): string {
return this._defaultOwner;
}
public get JobCompletionActionConditions(): sqlops.CategoryValue[] {
return this._jobCompletionActionConditions;
}
public async initialize() {
this._agentService = await AgentUtils.getAgentService();
let jobDefaults = await this._agentService.getJobDefaults(this.ownerUri);
if (jobDefaults && jobDefaults.success) {
this._jobCategories = jobDefaults.categories.map((cat) => {
return cat.name;
});
this._defaultOwner = jobDefaults.owner;
this._operators = ['', this._defaultOwner];
}
this._jobCompletionActionConditions = [{
displayName: this.JobCompletionActionCondition_OnSuccess,
name: sqlops.JobCompletionActionCondition.OnSuccess.toString()
}, {
displayName: this.JobCompletionActionCondition_OnFailure,
name: sqlops.JobCompletionActionCondition.OnFailure.toString()
}, {
displayName: this.JobCompletionActionCondition_Always,
name: sqlops.JobCompletionActionCondition.Always.toString()
}];
this.jobSchedules = [];
}
public async save() {
await this._agentService.createJob(this.ownerUri, {
name: this.name,
owner: this.owner,
description: this.description,
EmailLevel: this.emailLevel,
PageLevel: this.pageLevel,
EventLogLevel: this.eventLogLevel,
DeleteLevel: this.deleteLevel,
OperatorToEmail: this.operatorToEmail,
OperatorToPage: this.operatorToPage,
enabled: this.enabled,
category: this.category,
Alerts: this.alerts,
JobSchedules: this.jobSchedules,
JobSteps: this.jobSteps,
// The properties below are not collected from UI
// We could consider using a seperate class for create job request
//
currentExecutionStatus: 0,
lastRunOutcome: 0,
currentExecutionStep: '',
hasTarget: true,
hasSchedule: false,
hasStep: false,
runnable: true,
categoryId: 0,
categoryType: 1, // LocalJob, hard-coding the value, corresponds to the target tab in SSMS
lastRun: '',
nextRun: '',
jobId: ''
}).then(result => {
if (!result.success) {
console.info(result.errorMessage);
}
});
}
public validate(): { valid: boolean, errorMessages: string[] } {
let validationErrors: string[] = [];
if (!(this.name && this.name.trim())) {
validationErrors.push(this.CreateJobErrorMessage_NameIsEmpty);
}
return {
valid: validationErrors.length === 0,
errorMessages: validationErrors
};
}
public addJobSchedule(schedule: sqlops.AgentJobScheduleInfo) {
let existingSchedule = this.jobSchedules.find(item => item.name === schedule.name);
if (!existingSchedule) {
this.jobSchedules.push(schedule);
}
}
}

View File

@@ -0,0 +1,76 @@
/*---------------------------------------------------------------------------------------------
* 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 { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class JobStepData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
public ownerUri: string;
public jobId: string; //
public jobName: string;
public script: string; //
public scriptName: string;
public stepName: string; //
public subSystem: string; //
public id: number;
public failureAction: string; //
public successAction: string; //
public failStepId: number;
public successStepId: number;
public command: string;
public commandExecutionSuccessCode: number;
public databaseName: string; //
public databaseUserName: string;
public server: string;
public outputFileName: string; //
public appendToLogFile: boolean;
public appendToStepHist: boolean;
public writeLogToTable: boolean;
public appendLogToTable: boolean;
public retryAttempts: number; //
public retryInterval: number; //
public proxyName: string;
constructor(ownerUri:string) {
this.ownerUri = ownerUri;
}
public async initialize() {
}
public async save() {
let agentService = await AgentUtils.getAgentService();
agentService.createJobStep(this.ownerUri, {
jobId: this.jobId,
jobName: this.jobName,
script: this.script,
scriptName: this.scriptName,
stepName: this.stepName,
subSystem: this.subSystem,
id: 1,
failureAction: this.failureAction,
successAction: this.successAction,
failStepId: this.failStepId,
successStepId: this.successStepId,
command: this.command,
commandExecutionSuccessCode: this.commandExecutionSuccessCode,
databaseName: this.databaseName,
databaseUserName: this.databaseUserName,
server: this.server,
outputFileName: this.outputFileName,
appendToLogFile: this.appendToLogFile,
appendToStepHist: this.appendToStepHist,
writeLogToTable: this.writeLogToTable,
appendLogToTable: this.appendLogToTable,
retryAttempts: this.retryAttempts,
retryInterval: this.retryInterval,
proxyName: this.proxyName
}).then(result => {
console.info(result);
});
}
}

View File

@@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* 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 { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class OperatorData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
ownerUri: string;
name: string;
id: number;
emailAddress: string;
enabled: boolean;
lastEmailDate: string;
lastNetSendDate: string;
lastPagerDate: string;
pagerAddress: string;
categoryName: string;
pagerDays: string;
saturdayPagerEndTime: string;
saturdayPagerStartTime: string;
sundayPagerEndTime: string;
sundayPagerStartTime: string;
netSendAddress: string;
weekdayPagerStartTime: string;
weekdayPagerEndTime: string;
constructor(ownerUri:string) {
this.ownerUri = ownerUri;
}
public async initialize() {
}
public async save() {
let agentService = await AgentUtils.getAgentService();
let result = await agentService.createOperator(this.ownerUri, this.toAgentOperatorInfo());
if (!result || !result.success) {
// TODO handle error here
}
}
public toAgentOperatorInfo(): sqlops.AgentOperatorInfo {
return {
name: this.name,
id: this.id,
emailAddress: this.emailAddress,
enabled: this.enabled,
lastEmailDate: this.lastEmailDate,
lastNetSendDate: this.lastNetSendDate,
lastPagerDate: this.lastPagerDate,
pagerAddress: this.pagerAddress,
categoryName: this.categoryName,
pagerDays: sqlops.WeekDays.weekDays, //this.pagerDays,
saturdayPagerEndTime: this.saturdayPagerEndTime,
saturdayPagerStartTime: this.saturdayPagerStartTime,
sundayPagerEndTime: this.sundayPagerEndTime,
sundayPagerStartTime: this.sundayPagerStartTime,
netSendAddress: this.netSendAddress,
weekdayPagerStartTime: this.weekdayPagerStartTime,
weekdayPagerEndTime: this.weekdayPagerEndTime
};
}
}

View File

@@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* 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 { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class PickScheduleData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.VIEW;
public ownerUri: string;
public schedules: sqlops.AgentJobScheduleInfo[];
public selectedSchedule: sqlops.AgentJobScheduleInfo;
constructor(ownerUri:string) {
this.ownerUri = ownerUri;
}
public async initialize() {
let agentService = await AgentUtils.getAgentService();
let result = await agentService.getJobSchedules(this.ownerUri);
if (result && result.success) {
this.schedules = result.schedules;
}
}
public async save() {
}
}

View File

@@ -0,0 +1,48 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class ProxyData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
ownerUri: string;
id: number;
accountName: string;
description: string;
credentialName: string;
credentialIdentity: string;
credentialId: number;
isEnabled: boolean;
constructor(ownerUri:string) {
this.ownerUri = ownerUri;
}
public async initialize() {
}
public async save() {
let agentService = await AgentUtils.getAgentService();
let result = await agentService.createProxy(this.ownerUri, this.toAgentProxyInfo());
if (!result || !result.success) {
// TODO handle error here
}
}
public toAgentProxyInfo(): sqlops.AgentProxyInfo {
return {
id: this.id,
accountName: this.accountName,
description: this.description,
credentialName: this.credentialName,
credentialIdentity: this.credentialIdentity,
credentialId: this.credentialId,
isEnabled: this.isEnabled
};
}
}

View File

@@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* 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 { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class ScheduleData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
public ownerUri: string;
public schedules: sqlops.AgentJobScheduleInfo[];
public selectedSchedule: sqlops.AgentJobScheduleInfo;
constructor(ownerUri:string) {
this.ownerUri = ownerUri;
}
public async initialize() {
let agentService = await AgentUtils.getAgentService();
let result = await agentService.getJobSchedules(this.ownerUri);
if (result && result.success) {
this.schedules = result.schedules;
}
}
public async save() {
}
}

View File

@@ -0,0 +1,78 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
const localize = nls.loadMessageBundle();
export abstract class AgentDialog<T extends IAgentDialogData> {
private static readonly OkButtonText: string = localize('agentDialog.OK', 'OK');
private static readonly CancelButtonText: string = localize('agentDialog.Cancel', 'Cancel');
protected _onSuccess: vscode.EventEmitter<T> = new vscode.EventEmitter<T>();
public readonly onSuccess: vscode.Event<T> = this._onSuccess.event;
public dialog: sqlops.window.modelviewdialog.Dialog;
constructor(public ownerUri: string, public model: T, public title: string) {
}
public get dialogMode(): AgentDialogMode {
return this.model.dialogMode;
}
protected abstract async updateModel();
protected abstract async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog);
public async openDialog() {
this.dialog = sqlops.window.modelviewdialog.createDialog(this.title);
await this.model.initialize();
await this.initializeDialog(this.dialog);
this.dialog.okButton.label = AgentDialog.OkButtonText;
this.dialog.okButton.onClick(async () => await this.execute());
this.dialog.cancelButton.label = AgentDialog.CancelButtonText;
this.dialog.cancelButton.onClick(async () => await this.cancel());
sqlops.window.modelviewdialog.openDialog(this.dialog);
}
protected async execute() {
this.updateModel();
let success = await this.model.save();
if (success) {
this._onSuccess.fire(this.model);
}
}
protected async cancel() {
}
protected getActualConditionValue(checkbox: sqlops.CheckBoxComponent, dropdown: sqlops.DropDownComponent): sqlops.JobCompletionActionCondition {
return checkbox.checked ? Number(this.getDropdownValue(dropdown)) : sqlops.JobCompletionActionCondition.Never;
}
protected getDropdownValue(dropdown: sqlops.DropDownComponent): string {
return (typeof dropdown.value === 'string') ? dropdown.value : dropdown.value.name;
}
protected setConditionDropdownSelectedValue(dropdown: sqlops.DropDownComponent, selectedValue: number) {
let idx: number = 0;
for (idx = 0; idx < dropdown.values.length; idx++) {
if (Number((<sqlops.CategoryValue>dropdown.values[idx]).name) === selectedValue) {
dropdown.value = dropdown.values[idx];
break;
}
}
}
}

View File

@@ -0,0 +1,355 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import { AgentDialog } from './agentDialog';
import { AgentUtils } from '../agentUtils';
import { AlertData } from '../data/alertData';
const localize = nls.loadMessageBundle();
export class AlertDialog extends AgentDialog<AlertData> {
// Top level
private static readonly CreateDialogTitle: string = localize('alertDialog.createAlert', 'Create Alert');
private static readonly EditDialogTitle: string = localize('alertDialog.editAlert', 'Edit Alert');
private static readonly GeneralTabText: string = localize('alertDialog.General', 'General');
private static readonly ResponseTabText: string = localize('alertDialog.Response', 'Response');
private static readonly OptionsTabText: string = localize('alertDialog.Options', 'Options');
// General tab strings
private static readonly NameLabel: string = localize('alertDialog.Name', 'Name');
private static readonly TypeLabel: string = localize('alertDialog.Type', 'Type');
private static readonly EnabledCheckboxLabel: string = localize('alertDialog.Enabled', 'Enabled');
private static readonly DatabaseLabel: string = localize('alertDialog.DatabaseName', 'Database name');
private static readonly ErrorNumberLabel: string = localize('alertDialog.ErrorNumber', 'Error number');
private static readonly SeverityLabel: string = localize('alertDialog.Severity', 'Severity');
private static readonly RaiseIfMessageContainsLabel: string = localize('alertDialog.RaiseAlertContains', 'Raise alert when message contains');
private static readonly MessageTextLabel: string = localize('alertDialog.MessageText', 'Message text');
private static readonly AlertTypeSqlServerEventString: string = localize('alertDialog.SqlServerEventAlert', 'SQL Server event alert');
private static readonly AlertTypePerformanceConditionString: string = localize('alertDialog.PerformanceCondition', 'SQL Server performance condition alert');
private static readonly AlertTypeWmiEventString: string = localize('alertDialog.WmiEvent', 'WMI event alert');
private static readonly AlertSeverity001Label: string = localize('alertDialog.Severity001', '001 - Miscellaneous System Information');
private static readonly AlertSeverity002Label: string = localize('alertDialog.Severity002', '002 - Reserved');
private static readonly AlertSeverity003Label: string = localize('alertDialog.Severity003', '003 - Reserved');
private static readonly AlertSeverity004Label: string = localize('alertDialog.Severity004', '004 - Reserved');
private static readonly AlertSeverity005Label: string = localize('alertDialog.Severity005', '005 - Reserved');
private static readonly AlertSeverity006Label: string = localize('alertDialog.Severity006', '006 - Reserved');
private static readonly AlertSeverity007Label: string = localize('alertDialog.Severity007', '007 - Notification: Status Information');
private static readonly AlertSeverity008Label: string = localize('alertDialog.Severity008', '008 - Notification: User Intervention Required');
private static readonly AlertSeverity009Label: string = localize('alertDialog.Severity009', '009 - User Defined');
private static readonly AlertSeverity010Label: string = localize('alertDialog.Severity010', '010 - Information');
private static readonly AlertSeverity011Label: string = localize('alertDialog.Severity011', '011 - Specified Database Object Not Found');
private static readonly AlertSeverity012Label: string = localize('alertDialog.Severity012', '012 - Unused');
private static readonly AlertSeverity013Label: string = localize('alertDialog.Severity013', '013 - User Transaction Syntax Error');
private static readonly AlertSeverity014Label: string = localize('alertDialog.Severity014', '014 - Insufficient Permission');
private static readonly AlertSeverity015Label: string = localize('alertDialog.Severity015', '015 - Syntax Error in SQL Statements');
private static readonly AlertSeverity016Label: string = localize('alertDialog.Severity016', '016 - Miscellaneous User Error');
private static readonly AlertSeverity017Label: string = localize('alertDialog.Severity017', '017 - Insufficient Resources');
private static readonly AlertSeverity018Label: string = localize('alertDialog.Severity018', '018 - Nonfatal Internal Error');
private static readonly AlertSeverity019Label: string = localize('alertDialog.Severity019', '019 - Fatal Error in Resource');
private static readonly AlertSeverity020Label: string = localize('alertDialog.Severity020', '020 - Fatal Error in Current Process');
private static readonly AlertSeverity021Label: string = localize('alertDialog.Severity021', '021 - Fatal Error in Database Processes');
private static readonly AlertSeverity022Label: string = localize('alertDialog.Severity022', '022 - Fatal Error: Table Integrity Suspect');
private static readonly AlertSeverity023Label: string = localize('alertDialog.Severity023', '023 - Fatal Error: Database Integrity Suspect');
private static readonly AlertSeverity024Label: string = localize('alertDialog.Severity024', '024 - Fatal Error: Hardware Error');
private static readonly AlertSeverity025Label: string = localize('alertDialog.Severity025', '025 - Fatal Error');
private static readonly AlertTypes: string[] = [
AlertDialog.AlertTypeSqlServerEventString,
AlertDialog.AlertTypePerformanceConditionString,
AlertDialog.AlertTypeWmiEventString
];
private static readonly AlertSeverities: string[] = [
AlertDialog.AlertSeverity001Label,
AlertDialog.AlertSeverity002Label,
AlertDialog.AlertSeverity003Label,
AlertDialog.AlertSeverity004Label,
AlertDialog.AlertSeverity005Label,
AlertDialog.AlertSeverity006Label,
AlertDialog.AlertSeverity007Label,
AlertDialog.AlertSeverity008Label,
AlertDialog.AlertSeverity009Label,
AlertDialog.AlertSeverity010Label,
AlertDialog.AlertSeverity011Label,
AlertDialog.AlertSeverity012Label,
AlertDialog.AlertSeverity013Label,
AlertDialog.AlertSeverity014Label,
AlertDialog.AlertSeverity015Label,
AlertDialog.AlertSeverity016Label,
AlertDialog.AlertSeverity017Label,
AlertDialog.AlertSeverity018Label,
AlertDialog.AlertSeverity019Label,
AlertDialog.AlertSeverity020Label,
AlertDialog.AlertSeverity021Label,
AlertDialog.AlertSeverity022Label,
AlertDialog.AlertSeverity023Label,
AlertDialog.AlertSeverity024Label,
AlertDialog.AlertSeverity025Label
];
// Response tab strings
private static readonly ExecuteJobCheckBoxLabel: string = localize('alertDialog.ExecuteJob', 'Execute Job');
private static readonly ExecuteJobTextBoxLabel: string = localize('alertDialog.ExecuteJobName', 'Job Name');
private static readonly NotifyOperatorsTextBoxLabel: string = localize('alertDialog.NotifyOperators', 'Notify Operators');
private static readonly NewJobButtonLabel: string = localize('alertDialog.NewJob', 'New Job');
private static readonly OperatorListLabel: string = localize('alertDialog.OperatorList', 'Operator List');
private static readonly OperatorNameColumnLabel: string = localize('alertDialog.OperatorName', 'Operator');
private static readonly OperatorEmailColumnLabel: string = localize('alertDialog.OperatorEmail', 'E-mail');
private static readonly OperatorPagerColumnLabel: string = localize('alertDialog.OperatorPager', 'Pager');
private static readonly NewOperatorButtonLabel: string = localize('alertDialog.NewOperator', 'New Operator');
// Options tab strings
private static readonly IncludeErrorInEmailCheckBoxLabel: string = localize('alertDialog.IncludeErrorInEmail', 'Include alert error text in e-mail');
private static readonly IncludeErrorInPagerCheckBoxLabel: string = localize('alertDialog.IncludeErrorInPager', 'Include alert error text in pager');
private static readonly AdditionalMessageTextBoxLabel: string = localize('alertDialog.AdditionalNotification', 'Additional notification message to send');
private static readonly DelayBetweenResponsesTextBoxLabel: string = localize('alertDialog.DelayBetweenResponse', 'Delay between responses');
private static readonly DelayMinutesTextBoxLabel: string = localize('alertDialog.DelayMinutes', 'Delay Minutes');
private static readonly DelaySecondsTextBoxLabel: string = localize('alertDialog.DelaySeconds', 'Delay Seconds');
// UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab;
private responseTab: sqlops.window.modelviewdialog.DialogTab;
private optionsTab: sqlops.window.modelviewdialog.DialogTab;
// General tab controls
private nameTextBox: sqlops.InputBoxComponent;
private typeDropDown: sqlops.DropDownComponent;
private severityDropDown: sqlops.DropDownComponent;
private databaseDropDown: sqlops.DropDownComponent;
private enabledCheckBox: sqlops.CheckBoxComponent;
private raiseAlertMessageCheckBox: sqlops.CheckBoxComponent;
private raiseAlertMessageTextBox: sqlops.InputBoxComponent;
// Response tab controls
private executeJobTextBox: sqlops.InputBoxComponent;
private executeJobCheckBox: sqlops.CheckBoxComponent;
private newJobButton: sqlops.ButtonComponent;
private notifyOperatorsCheckBox: sqlops.CheckBoxComponent;
private operatorsTable: sqlops.TableComponent;
private newOperatorButton: sqlops.ButtonComponent;
// Options tab controls
private additionalMessageTextBox: sqlops.InputBoxComponent;
private includeErrorInEmailTextBox: sqlops.CheckBoxComponent;
private includeErrorInPagerTextBox: sqlops.CheckBoxComponent;
private delayMinutesTextBox: sqlops.InputBoxComponent;
private delaySecondsTextBox: sqlops.InputBoxComponent;
constructor(ownerUri: string, alertInfo: sqlops.AgentAlertInfo = null) {
super(ownerUri,
new AlertData(ownerUri, alertInfo),
alertInfo ? AlertDialog.EditDialogTitle : AlertDialog.CreateDialogTitle);
}
protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
let databases = await AgentUtils.getDatabases(this.ownerUri);
this.generalTab = sqlops.window.modelviewdialog.createTab(AlertDialog.GeneralTabText);
this.responseTab = sqlops.window.modelviewdialog.createTab(AlertDialog.ResponseTabText);
this.optionsTab = sqlops.window.modelviewdialog.createTab(AlertDialog.OptionsTabText);
this.initializeGeneralTab(databases);
this.initializeResponseTab();
this.initializeOptionsTab();
dialog.content = [this.generalTab, this.responseTab, this.optionsTab];
}
private initializeGeneralTab(databases: string[]) {
this.generalTab.registerContent(async view => {
this.nameTextBox = view.modelBuilder.inputBox().component();
this.enabledCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.EnabledCheckboxLabel
}).component();
this.databaseDropDown = view.modelBuilder.dropDown()
.withProperties({
value: databases[0],
values: databases
}).component();
this.typeDropDown = view.modelBuilder.dropDown()
.withProperties({
value: AlertDialog.AlertTypes[0],
values: AlertDialog.AlertTypes
}).component();
this.severityDropDown = view.modelBuilder.dropDown()
.withProperties({
value: AlertDialog.AlertSeverities[0],
values: AlertDialog.AlertSeverities
}).component();
this.raiseAlertMessageCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.RaiseIfMessageContainsLabel
}).component();
this.raiseAlertMessageTextBox = view.modelBuilder.inputBox().component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.nameTextBox,
title: AlertDialog.NameLabel
}, {
component: this.enabledCheckBox,
title: ''
}, {
component: this.typeDropDown,
title: AlertDialog.TypeLabel
}, {
component: this.databaseDropDown,
title: AlertDialog.DatabaseLabel
}, {
component: this.severityDropDown,
title: AlertDialog.SeverityLabel
}, {
component: this.raiseAlertMessageCheckBox,
title: ''
}, {
component: this.raiseAlertMessageTextBox,
title: AlertDialog.MessageTextLabel
}
]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
this.nameTextBox.value = this.model.name;
this.enabledCheckBox.checked = this.model.isEnabled;
});
}
private initializeResponseTab() {
this.responseTab.registerContent(async view => {
this.executeJobCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.ExecuteJobCheckBoxLabel
}).component();
this.executeJobTextBox = view.modelBuilder.inputBox().component();
this.newJobButton = view.modelBuilder.button().withProperties({
label: AlertDialog.NewJobButtonLabel,
width: 80
}).component();
this.notifyOperatorsCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.NotifyOperatorsTextBoxLabel
}).component();
this.operatorsTable = view.modelBuilder.table()
.withProperties({
columns: [
AlertDialog.OperatorNameColumnLabel,
AlertDialog.OperatorEmailColumnLabel,
AlertDialog.OperatorPagerColumnLabel
],
data: [],
height: 500
}).component();
this.newOperatorButton = view.modelBuilder.button().withProperties({
label: this.newOperatorButton,
width: 80
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.executeJobCheckBox,
title: ''
}, {
component: this.executeJobTextBox,
title: AlertDialog.ExecuteJobTextBoxLabel
}, {
component: this.newJobButton,
title: AlertDialog.NewJobButtonLabel
}, {
component: this.notifyOperatorsCheckBox,
title: ''
}, {
component: this.operatorsTable,
title: AlertDialog.OperatorListLabel,
actions: [this.newOperatorButton]
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private initializeOptionsTab() {
this.optionsTab.registerContent(async view => {
this.includeErrorInEmailTextBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.IncludeErrorInEmailCheckBoxLabel
}).component();
this.includeErrorInPagerTextBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.IncludeErrorInPagerCheckBoxLabel
}).component();
this.additionalMessageTextBox = view.modelBuilder.inputBox().component();
this.delayMinutesTextBox = view.modelBuilder.inputBox().component();
this.delaySecondsTextBox = view.modelBuilder.inputBox().component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.includeErrorInEmailTextBox,
title: ''
}, {
component: this.includeErrorInPagerTextBox,
title: ''
}, {
component: this.additionalMessageTextBox,
title: AlertDialog.AdditionalMessageTextBoxLabel
}, {
component: this.delayMinutesTextBox,
title: AlertDialog.DelayMinutesTextBoxLabel
}, {
component: this.delaySecondsTextBox,
title: AlertDialog.DelaySecondsTextBoxLabel
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private getSeverityNumber(): number {
let selected = this.getDropdownValue(this.severityDropDown);
let severityNumber: number = 0;
if (selected) {
let index = AlertDialog.AlertSeverities.indexOf(selected);
if (index >= 0) {
severityNumber = index + 1;
}
}
return severityNumber;
}
protected updateModel() {
this.model.name = this.nameTextBox.value;
this.model.isEnabled = this.enabledCheckBox.checked;
this.model.alertType = this.getDropdownValue(this.typeDropDown);
this.model.databaseName = this.getDropdownValue(this.databaseDropDown);
this.model.severity = this.getSeverityNumber();
this.model.messageId = undefined;
let raiseIfError = this.raiseAlertMessageCheckBox.checked;
if (raiseIfError) {
let messageText = this.raiseAlertMessageTextBox.value;
}
}
}

View File

@@ -0,0 +1,425 @@
/*---------------------------------------------------------------------------------------------
* 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 { JobData } from '../data/jobData';
import { JobStepDialog } from './jobStepDialog';
import { PickScheduleDialog } from './pickScheduleDialog';
import { AlertDialog } from './alertDialog';
import { AgentDialog } from './agentDialog';
export class JobDialog extends AgentDialog<JobData> {
// TODO: localize
// Top level
private static readonly DialogTitle: string = 'New Job';
private readonly GeneralTabText: string = 'General';
private readonly StepsTabText: string = 'Steps';
private readonly SchedulesTabText: string = 'Schedules';
private readonly AlertsTabText: string = 'Alerts';
private readonly NotificationsTabText: string = 'Notifications';
// General tab strings
private readonly NameTextBoxLabel: string = 'Name';
private readonly OwnerTextBoxLabel: string = 'Owner';
private readonly CategoryDropdownLabel: string = 'Category';
private readonly DescriptionTextBoxLabel: string = 'Description';
private readonly EnabledCheckboxLabel: string = 'Enabled';
// Steps tab strings
private readonly JobStepsTopLabelString: string = 'Job step list';
private readonly StepsTable_StepColumnString: string = 'Step';
private readonly StepsTable_NameColumnString: string = 'Name';
private readonly StepsTable_TypeColumnString: string = 'Type';
private readonly StepsTable_SuccessColumnString: string = 'On Success';
private readonly StepsTable_FailureColumnString: string = 'On Failure';
private readonly NewStepButtonString: string = 'New...';
private readonly InsertStepButtonString: string = 'Insert...';
private readonly EditStepButtonString: string = 'Edit';
private readonly DeleteStepButtonString: string = 'Delete';
// Notifications tab strings
private readonly NotificationsTabTopLabelString: string = 'Actions to perform when the job completes';
private readonly EmailCheckBoxString: string = 'Email';
private readonly PagerCheckBoxString: string = 'Page';
private readonly EventLogCheckBoxString: string = 'Write to the Windows Application event log';
private readonly DeleteJobCheckBoxString: string = 'Automatically delete job';
// Schedules tab strings
private readonly SchedulesTopLabelString: string = 'Schedules list';
private readonly PickScheduleButtonString: string = 'Pick Schedule';
// Alerts tab strings
private readonly AlertsTopLabelString: string = 'Alerts list';
private readonly NewAlertButtonString: string = 'New Alert';
// UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab;
private stepsTab: sqlops.window.modelviewdialog.DialogTab;
private alertsTab: sqlops.window.modelviewdialog.DialogTab;
private schedulesTab: sqlops.window.modelviewdialog.DialogTab;
private notificationsTab: sqlops.window.modelviewdialog.DialogTab;
// General tab controls
private nameTextBox: sqlops.InputBoxComponent;
private ownerTextBox: sqlops.InputBoxComponent;
private categoryDropdown: sqlops.DropDownComponent;
private descriptionTextBox: sqlops.InputBoxComponent;
private enabledCheckBox: sqlops.CheckBoxComponent;
// Steps tab controls
private stepsTable: sqlops.TableComponent;
private newStepButton: sqlops.ButtonComponent;
private insertStepButton: sqlops.ButtonComponent;
private editStepButton: sqlops.ButtonComponent;
private deleteStepButton: sqlops.ButtonComponent;
// Notifications tab controls
private notificationsTabTopLabel: sqlops.TextComponent;
private emailCheckBox: sqlops.CheckBoxComponent;
private emailOperatorDropdown: sqlops.DropDownComponent;
private emailConditionDropdown: sqlops.DropDownComponent;
private pagerCheckBox: sqlops.CheckBoxComponent;
private pagerOperatorDropdown: sqlops.DropDownComponent;
private pagerConditionDropdown: sqlops.DropDownComponent;
private eventLogCheckBox: sqlops.CheckBoxComponent;
private eventLogConditionDropdown: sqlops.DropDownComponent;
private deleteJobCheckBox: sqlops.CheckBoxComponent;
private deleteJobConditionDropdown: sqlops.DropDownComponent;
// Schedule tab controls
private schedulesTable: sqlops.TableComponent;
private pickScheduleButton: sqlops.ButtonComponent;
// Alert tab controls
private alertsTable: sqlops.TableComponent;
private newAlertButton: sqlops.ButtonComponent;
constructor(ownerUri: string) {
super(ownerUri, new JobData(ownerUri), JobDialog.DialogTitle);
}
protected async initializeDialog() {
this.generalTab = sqlops.window.modelviewdialog.createTab(this.GeneralTabText);
this.stepsTab = sqlops.window.modelviewdialog.createTab(this.StepsTabText);
this.alertsTab = sqlops.window.modelviewdialog.createTab(this.AlertsTabText);
this.schedulesTab = sqlops.window.modelviewdialog.createTab(this.SchedulesTabText);
this.notificationsTab = sqlops.window.modelviewdialog.createTab(this.NotificationsTabText);
this.initializeGeneralTab();
this.initializeStepsTab();
this.initializeAlertsTab();
this.initializeSchedulesTab();
this.initializeNotificationsTab();
this.dialog.content = [this.generalTab, this.stepsTab, this.schedulesTab, this.alertsTab, this.notificationsTab];
this.dialog.registerCloseValidator(() => {
this.updateModel();
let validationResult = this.model.validate();
if (!validationResult.valid) {
// TODO: Show Error Messages
console.error(validationResult.errorMessages.join(','));
}
return validationResult.valid;
});
}
private initializeGeneralTab() {
this.generalTab.registerContent(async view => {
this.nameTextBox = view.modelBuilder.inputBox().component();
this.ownerTextBox = view.modelBuilder.inputBox().component();
this.categoryDropdown = view.modelBuilder.dropDown().component();
this.descriptionTextBox = view.modelBuilder.inputBox().withProperties({
multiline: true,
height: 200
}).component();
this.enabledCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: this.EnabledCheckboxLabel
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.nameTextBox,
title: this.NameTextBoxLabel
}, {
component: this.ownerTextBox,
title: this.OwnerTextBoxLabel
}, {
component: this.categoryDropdown,
title: this.CategoryDropdownLabel
}, {
component: this.descriptionTextBox,
title: this.DescriptionTextBoxLabel
}, {
component: this.enabledCheckBox,
title: ''
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
this.ownerTextBox.value = this.model.defaultOwner;
this.categoryDropdown.values = this.model.jobCategories;
this.categoryDropdown.value = this.model.jobCategories[0];
this.enabledCheckBox.checked = this.model.enabled;
this.descriptionTextBox.value = '';
});
}
private initializeStepsTab() {
this.stepsTab.registerContent(async view => {
this.stepsTable = view.modelBuilder.table()
.withProperties({
columns: [
this.StepsTable_StepColumnString,
this.StepsTable_NameColumnString,
this.StepsTable_TypeColumnString,
this.StepsTable_SuccessColumnString,
this.StepsTable_FailureColumnString
],
data: [],
height: 800
}).component();
this.newStepButton = view.modelBuilder.button().withProperties({
label: this.NewStepButtonString,
width: 80
}).component();
this.newStepButton.onDidClick((e)=>{
let stepDialog = new JobStepDialog(this.model.ownerUri, '', '', 1, this.model);
stepDialog.openNewStepDialog();
});
this.insertStepButton = view.modelBuilder.button().withProperties({
label: this.InsertStepButtonString,
width: 80
}).component();
this.editStepButton = view.modelBuilder.button().withProperties({
label: this.EditStepButtonString,
width: 80
}).component();
this.deleteStepButton = view.modelBuilder.button().withProperties({
label: this.DeleteStepButtonString,
width: 80
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.stepsTable,
title: this.JobStepsTopLabelString,
actions: [this.newStepButton, this.insertStepButton, this.editStepButton, this.deleteStepButton]
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private initializeAlertsTab() {
this.alertsTab.registerContent(async view => {
this.alertsTable = view.modelBuilder.table()
.withProperties({
columns: [
'Alert Name'
],
data: [],
height: 600,
width: 400
}).component();
this.newAlertButton = view.modelBuilder.button().withProperties({
label: this.NewAlertButtonString,
width: 80
}).component();
this.newAlertButton.onDidClick((e)=>{
let alertDialog = new AlertDialog(this.model.ownerUri);
alertDialog.onSuccess((dialogModel) => {
});
alertDialog.openDialog();
});
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.alertsTable,
title: this.AlertsTopLabelString,
actions: [this.newAlertButton]
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private initializeSchedulesTab() {
this.schedulesTab.registerContent(async view => {
this.schedulesTable = view.modelBuilder.table()
.withProperties({
columns: [
'Schedule Name'
],
data: [],
height: 600,
width: 400
}).component();
this.pickScheduleButton = view.modelBuilder.button().withProperties({
label: this.PickScheduleButtonString,
width: 80
}).component();
this.pickScheduleButton.onDidClick((e)=>{
let pickScheduleDialog = new PickScheduleDialog(this.model.ownerUri);
pickScheduleDialog.onSuccess((dialogModel) => {
let selectedSchedule = dialogModel.selectedSchedule;
if (selectedSchedule) {
this.model.addJobSchedule(selectedSchedule);
this.populateScheduleTable();
}
});
pickScheduleDialog.showDialog();
});
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.schedulesTable,
title: this.SchedulesTopLabelString,
actions: [this.pickScheduleButton]
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
this.populateScheduleTable();
});
}
private populateScheduleTable() {
if (this.model.jobSchedules) {
let data: any[][] = [];
for (let i = 0; i < this.model.jobSchedules.length; ++i) {
let schedule = this.model.jobSchedules[i];
data[i] = [ schedule.name ];
}
this.schedulesTable.data = data;
}
}
private initializeNotificationsTab() {
this.notificationsTab.registerContent(async view => {
this.notificationsTabTopLabel = view.modelBuilder.text().withProperties({ value: this.NotificationsTabTopLabelString }).component();
this.emailCheckBox = view.modelBuilder.checkBox().withProperties({
label: this.EmailCheckBoxString,
width: 80
}).component();
this.pagerCheckBox = view.modelBuilder.checkBox().withProperties({
label: this.PagerCheckBoxString,
width: 80
}).component();
this.eventLogCheckBox = view.modelBuilder.checkBox().withProperties({
label: this.EventLogCheckBoxString,
width: 250
}).component();
this.deleteJobCheckBox = view.modelBuilder.checkBox().withProperties({
label: this.DeleteJobCheckBoxString,
width: 250
}).component();
this.emailCheckBox.onChanged(() => {
this.emailConditionDropdown.enabled = this.emailCheckBox.checked;
this.emailOperatorDropdown.enabled = this.emailCheckBox.checked;
});
this.pagerCheckBox.onChanged(() => {
this.pagerConditionDropdown.enabled = this.pagerCheckBox.checked;
this.pagerOperatorDropdown.enabled = this.pagerCheckBox.checked;
});
this.eventLogCheckBox.onChanged(() => {
this.eventLogConditionDropdown.enabled = this.eventLogCheckBox.checked;
});
this.deleteJobCheckBox.onChanged(() => {
this.deleteJobConditionDropdown.enabled = this.deleteJobCheckBox.checked;
});
this.emailOperatorDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
this.pagerOperatorDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
this.emailConditionDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
this.pagerConditionDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
this.eventLogConditionDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
this.deleteJobConditionDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
let emailContainer = this.createRowContainer(view).withItems([this.emailCheckBox, this.emailOperatorDropdown, this.emailConditionDropdown]).component();
let pagerContainer = this.createRowContainer(view).withItems([this.pagerCheckBox, this.pagerOperatorDropdown, this.pagerConditionDropdown]).component();
let eventLogContainer = this.createRowContainer(view).withItems([this.eventLogCheckBox, this.eventLogConditionDropdown]).component();
let deleteJobContainer = this.createRowContainer(view).withItems([this.deleteJobCheckBox, this.deleteJobConditionDropdown]).component();
let formModel = view.modelBuilder.formContainer().withFormItems([
{
component: this.notificationsTabTopLabel,
title: ''
}, {
component: emailContainer,
title: ''
}, {
component: pagerContainer,
title: ''
}, {
component: eventLogContainer,
title: ''
}, {
component: deleteJobContainer,
title: ''
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
this.emailConditionDropdown.values = this.model.JobCompletionActionConditions;
this.pagerConditionDropdown.values = this.model.JobCompletionActionConditions;
this.eventLogConditionDropdown.values = this.model.JobCompletionActionConditions;
this.deleteJobConditionDropdown.values = this.model.JobCompletionActionConditions;
this.setConditionDropdownSelectedValue(this.emailConditionDropdown, this.model.emailLevel);
this.setConditionDropdownSelectedValue(this.pagerConditionDropdown, this.model.pageLevel);
this.setConditionDropdownSelectedValue(this.eventLogConditionDropdown, this.model.eventLogLevel);
this.setConditionDropdownSelectedValue(this.deleteJobConditionDropdown, this.model.deleteLevel);
this.emailOperatorDropdown.values = this.model.operators;
this.pagerOperatorDropdown.values = this.model.operators;
this.emailCheckBox.checked = false;
this.pagerCheckBox.checked = false;
this.eventLogCheckBox.checked = false;
this.deleteJobCheckBox.checked = false;
this.emailOperatorDropdown.enabled = false;
this.pagerOperatorDropdown.enabled = false;
this.emailConditionDropdown.enabled = false;
this.pagerConditionDropdown.enabled = false;
this.eventLogConditionDropdown.enabled = false;
this.deleteJobConditionDropdown.enabled = false;
});
}
private createRowContainer(view: sqlops.ModelView): sqlops.FlexBuilder {
return view.modelBuilder.flexContainer().withLayout({
flexFlow: 'row',
alignItems: 'left',
justifyContent: 'space-between'
});
}
protected updateModel() {
this.model.name = this.nameTextBox.value;
this.model.owner = this.ownerTextBox.value;
this.model.enabled = this.enabledCheckBox.checked;
this.model.description = this.descriptionTextBox.value;
this.model.category = this.getDropdownValue(this.categoryDropdown);
this.model.emailLevel = this.getActualConditionValue(this.emailCheckBox, this.emailConditionDropdown);
this.model.operatorToEmail = this.getDropdownValue(this.emailOperatorDropdown);
this.model.operatorToPage = this.getDropdownValue(this.pagerOperatorDropdown);
this.model.pageLevel = this.getActualConditionValue(this.pagerCheckBox, this.pagerConditionDropdown);
this.model.eventLogLevel = this.getActualConditionValue(this.eventLogCheckBox, this.eventLogConditionDropdown);
this.model.deleteLevel = this.getActualConditionValue(this.deleteJobCheckBox, this.deleteJobConditionDropdown);
}
}

View File

@@ -0,0 +1,490 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import { JobStepData } from '../data/jobStepData';
import { AgentUtils } from '../agentUtils';
import { JobData } from '../data/jobData';
const path = require('path');
export class JobStepDialog {
// TODO: localize
// Top level
//
private static readonly DialogTitle: string = 'New Job Step';
private static readonly FileBrowserDialogTitle: string = 'Locate Database Files - ';
private static readonly OkButtonText: string = 'OK';
private static readonly CancelButtonText: string = 'Cancel';
private static readonly GeneralTabText: string = 'General';
private static readonly AdvancedTabText: string = 'Advanced';
private static readonly OpenCommandText: string = 'Open...';
private static readonly ParseCommandText: string = 'Parse';
private static readonly NextButtonText: string = 'Next';
private static readonly PreviousButtonText: string = 'Previous';
private static readonly SuccessAction: string = 'On success action';
private static readonly FailureAction: string = 'On failure action';
// Dropdown options
private static readonly TSQLScript: string = 'Transact-SQL script (T-SQL)';
private static readonly AgentServiceAccount: string = 'SQL Server Agent Service Account';
private static readonly NextStep: string = 'Go to the next step';
private static readonly QuitJobReportingSuccess: string = 'Quit the job reporting success';
private static readonly QuitJobReportingFailure: string = 'Quit the job reporting failure';
// UI Components
// Dialogs
private dialog: sqlops.window.modelviewdialog.Dialog;
private fileBrowserDialog: sqlops.window.modelviewdialog.Dialog;
// Dialog tabs
private generalTab: sqlops.window.modelviewdialog.DialogTab;
private advancedTab: sqlops.window.modelviewdialog.DialogTab;
//Input boxes
private nameTextBox: sqlops.InputBoxComponent;
private commandTextBox: sqlops.InputBoxComponent;
private selectedPathTextBox: sqlops.InputBoxComponent;
private retryAttemptsBox: sqlops.InputBoxComponent;
private retryIntervalBox: sqlops.InputBoxComponent;
private outputFileNameBox: sqlops.InputBoxComponent;
private fileBrowserNameBox: sqlops.InputBoxComponent;
// Dropdowns
private typeDropdown: sqlops.DropDownComponent;
private runAsDropdown: sqlops.DropDownComponent;
private databaseDropdown: sqlops.DropDownComponent;
private successActionDropdown: sqlops.DropDownComponent;
private failureActionDropdown: sqlops.DropDownComponent;
private fileTypeDropdown: sqlops.DropDownComponent;
// Buttons
private openButton: sqlops.ButtonComponent;
private parseButton: sqlops.ButtonComponent;
private nextButton: sqlops.ButtonComponent;
private previousButton: sqlops.ButtonComponent;
private outputFileBrowserButton: sqlops.ButtonComponent;
// Checkbox
private appendToExistingFileCheckbox: sqlops.CheckBoxComponent;
private logToTableCheckbox: sqlops.CheckBoxComponent;
private fileBrowserTree: sqlops.FileBrowserTreeComponent;
private jobModel: JobData;
private model: JobStepData;
private ownerUri: string;
private jobName: string;
private server: string;
private stepId: number;
constructor(
ownerUri: string,
jobName: string,
server: string,
stepId: number,
jobModel?: JobData
) {
this.model = new JobStepData(ownerUri);
this.stepId = stepId;
this.ownerUri = ownerUri;
this.jobName = jobName;
this.server = server;
this.jobModel = jobModel;
}
private initializeUIComponents() {
this.dialog = sqlops.window.modelviewdialog.createDialog(JobStepDialog.DialogTitle);
this.generalTab = sqlops.window.modelviewdialog.createTab(JobStepDialog.GeneralTabText);
this.advancedTab = sqlops.window.modelviewdialog.createTab(JobStepDialog.AdvancedTabText);
this.dialog.content = [this.generalTab, this.advancedTab];
this.dialog.okButton.onClick(async () => await this.execute());
this.dialog.okButton.label = JobStepDialog.OkButtonText;
this.dialog.cancelButton.label = JobStepDialog.CancelButtonText;
}
private createCommands(view, queryProvider: sqlops.QueryProvider) {
this.openButton = view.modelBuilder.button()
.withProperties({
label: JobStepDialog.OpenCommandText,
width: '80px'
}).component();
this.parseButton = view.modelBuilder.button()
.withProperties({
label: JobStepDialog.ParseCommandText,
width: '80px'
}).component();
this.parseButton.onDidClick(e => {
if (this.commandTextBox.value) {
queryProvider.parseSyntax(this.ownerUri, this.commandTextBox.value).then(result => {
if (result && result.parseable) {
this.dialog.message = { text: 'The command was successfully parsed.', level: 2};
} else if (result && !result.parseable) {
this.dialog.message = { text: 'The command failed' };
}
});
}
});
this.commandTextBox = view.modelBuilder.inputBox()
.withProperties({
height: 300,
width: 400,
multiline: true,
inputType: 'text'
})
.component();
this.nextButton = view.modelBuilder.button()
.withProperties({
label: JobStepDialog.NextButtonText,
enabled: false,
width: '80px'
}).component();
this.previousButton = view.modelBuilder.button()
.withProperties({
label: JobStepDialog.PreviousButtonText,
enabled: false,
width: '80px'
}).component();
}
private createGeneralTab(databases: string[], queryProvider: sqlops.QueryProvider) {
this.generalTab.registerContent(async (view) => {
this.nameTextBox = view.modelBuilder.inputBox()
.withProperties({
}).component();
this.nameTextBox.required = true;
this.typeDropdown = view.modelBuilder.dropDown()
.withProperties({
value: JobStepDialog.TSQLScript,
values: [JobStepDialog.TSQLScript]
})
.component();
this.runAsDropdown = view.modelBuilder.dropDown()
.withProperties({
value: '',
values: ['']
})
.component();
this.runAsDropdown.enabled = false;
this.typeDropdown.onValueChanged((type) => {
if (type.selected !== JobStepDialog.TSQLScript) {
this.runAsDropdown.value = JobStepDialog.AgentServiceAccount;
this.runAsDropdown.values = [this.runAsDropdown.value];
} else {
this.runAsDropdown.value = '';
this.runAsDropdown.values = [''];
}
});
this.databaseDropdown = view.modelBuilder.dropDown()
.withProperties({
value: databases[0],
values: databases
}).component();
// create the commands section
this.createCommands(view, queryProvider);
let buttonContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
justifyContent: 'space-between',
width: 420
}).withItems([this.openButton, this.parseButton, this.previousButton, this.nextButton], {
flex: '1 1 50%'
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.nameTextBox,
title: 'Step name'
}, {
component: this.typeDropdown,
title: 'Type'
}, {
component: this.runAsDropdown,
title: 'Run as'
}, {
component: this.databaseDropdown,
title: 'Database'
}, {
component: this.commandTextBox,
title: 'Command',
actions: [buttonContainer]
}], {
horizontal: false,
componentWidth: 420
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
await view.initializeModel(formWrapper);
});
}
private createRunAsUserOptions(view) {
let userInputBox = view.modelBuilder.inputBox()
.withProperties({ inputType: 'text', width: '100px' }).component();
let viewButton = view.modelBuilder.button()
.withProperties({ label: '...', width: '20px' }).component();
let viewButtonContainer = view.modelBuilder.flexContainer()
.withLayout({ width: 100, textAlign: 'right' })
.withItems([viewButton], { flex: '1 1 50%' }).component();
let userInputBoxContainer = view.modelBuilder.flexContainer()
.withLayout({ width: 200, textAlign: 'left' })
.withItems([userInputBox], { flex: '1 1 50%' }).component();
let runAsUserContainer = view.modelBuilder.flexContainer()
.withLayout({ width: 200 })
.withItems([userInputBoxContainer, viewButtonContainer], { flex: '1 1 50%' })
.component();
let runAsUserForm = view.modelBuilder.formContainer()
.withFormItems([{
component: runAsUserContainer,
title: 'Run as user'
}], { horizontal: true, componentWidth: 200 }).component();
return runAsUserForm;
}
private createAdvancedTab() {
this.advancedTab.registerContent(async (view) => {
this.successActionDropdown = view.modelBuilder.dropDown()
.withProperties({
value: JobStepDialog.NextStep,
values: [JobStepDialog.NextStep, JobStepDialog.QuitJobReportingSuccess, JobStepDialog.QuitJobReportingFailure]
})
.component();
let retryFlexContainer = this.createRetryCounters(view);
this.failureActionDropdown = view.modelBuilder.dropDown()
.withProperties({
value: JobStepDialog.QuitJobReportingFailure,
values: [JobStepDialog.QuitJobReportingFailure, JobStepDialog.NextStep, JobStepDialog.QuitJobReportingSuccess]
})
.component();
let optionsGroup = this.createTSQLOptions(view);
let viewButton = view.modelBuilder.button()
.withProperties({ label: 'View', width: '50px' }).component();
viewButton.enabled = false;
this.logToTableCheckbox = view.modelBuilder.checkBox()
.withProperties({
label: 'Log to table'
}).component();
let appendToExistingEntryInTableCheckbox = view.modelBuilder.checkBox()
.withProperties({ label: 'Append output to existing entry in table' }).component();
appendToExistingEntryInTableCheckbox.enabled = false;
this.logToTableCheckbox.onChanged(e => {
viewButton.enabled = e;
appendToExistingEntryInTableCheckbox.enabled = e;
});
let appendCheckboxContainer = view.modelBuilder.groupContainer()
.withItems([appendToExistingEntryInTableCheckbox]).component();
let logToTableContainer = view.modelBuilder.flexContainer()
.withLayout({ flexFlow: 'row', justifyContent: 'space-between', width: 300 })
.withItems([this.logToTableCheckbox, viewButton]).component();
let logStepOutputHistoryCheckbox = view.modelBuilder.checkBox()
.withProperties({ label: 'Include step output in history' }).component();
let runAsUserOptions = this.createRunAsUserOptions(view);
let formModel = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.successActionDropdown,
title: JobStepDialog.SuccessAction
}, {
component: retryFlexContainer,
title: ''
}, {
component: this.failureActionDropdown,
title: JobStepDialog.FailureAction
}, {
component: optionsGroup,
title: 'Transact-SQL script (T-SQL)'
}, {
component: logToTableContainer,
title: ''
}, {
component: appendCheckboxContainer,
title: ' '
}, {
component: logStepOutputHistoryCheckbox,
title: ''
}, {
component: runAsUserOptions,
title: ''
}], {
componentWidth: 400
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
view.initializeModel(formWrapper);
});
}
private createRetryCounters(view) {
this.retryAttemptsBox = view.modelBuilder.inputBox()
.withValidation(component => component.value >= 0)
.withProperties({
inputType: 'number'
})
.component();
this.retryIntervalBox = view.modelBuilder.inputBox()
.withValidation(component => component.value >= 0)
.withProperties({
inputType: 'number'
}).component();
let retryAttemptsContainer = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.retryAttemptsBox,
title: 'Retry Attempts'
}], {
horizontal: false
})
.component();
let retryIntervalContainer = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.retryIntervalBox,
title: 'Retry Interval (minutes)'
}], {
horizontal: false
})
.component();
let retryFlexContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
}).withItems([retryAttemptsContainer, retryIntervalContainer]).component();
return retryFlexContainer;
}
private openFileBrowserDialog() {
let fileBrowserTitle = JobStepDialog.FileBrowserDialogTitle + `${this.server}`;
this.fileBrowserDialog = sqlops.window.modelviewdialog.createDialog(fileBrowserTitle);
let fileBrowserTab = sqlops.window.modelviewdialog.createTab('File Browser');
this.fileBrowserDialog.content = [fileBrowserTab];
fileBrowserTab.registerContent(async (view) => {
this.fileBrowserTree = view.modelBuilder.fileBrowserTree()
.withProperties({ ownerUri: this.ownerUri, width: 420, height: 700 })
.component();
this.selectedPathTextBox = view.modelBuilder.inputBox()
.withProperties({ inputType: 'text'})
.component();
this.fileBrowserTree.onDidChange((args) => {
this.selectedPathTextBox.value = args.fullPath;
this.fileBrowserNameBox.value = args.isFile ? path.win32.basename(args.fullPath) : '';
});
this.fileTypeDropdown = view.modelBuilder.dropDown()
.withProperties({
value: 'All Files (*)',
values: ['All Files (*)']
})
.component();
this.fileBrowserNameBox = view.modelBuilder.inputBox()
.withProperties({})
.component();
let fileBrowserContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.fileBrowserTree,
title: ''
}, {
component: this.selectedPathTextBox,
title: 'Selected path:'
}, {
component: this.fileTypeDropdown,
title: 'Files of type:'
}, {
component: this.fileBrowserNameBox,
title: 'File name:'
}
]).component();
view.initializeModel(fileBrowserContainer);
});
this.fileBrowserDialog.okButton.onClick(() => {
this.outputFileNameBox.value = path.join(path.dirname(this.selectedPathTextBox.value), this.fileBrowserNameBox.value);
});
this.fileBrowserDialog.okButton.label = JobStepDialog.OkButtonText;
this.fileBrowserDialog.cancelButton.label = JobStepDialog.CancelButtonText;
sqlops.window.modelviewdialog.openDialog(this.fileBrowserDialog);
}
private createTSQLOptions(view) {
this.outputFileBrowserButton = view.modelBuilder.button()
.withProperties({ width: '20px', label: '...' }).component();
this.outputFileBrowserButton.onDidClick(() => this.openFileBrowserDialog());
this.outputFileNameBox = view.modelBuilder.inputBox()
.withProperties({
width: '150px',
inputType: 'text'
}).component();
let outputViewButton = view.modelBuilder.button()
.withProperties({
width: '50px',
label: 'View'
}).component();
outputViewButton.enabled = false;
let outputButtonContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
textAlign: 'right',
width: 120
}).withItems([this.outputFileBrowserButton, outputViewButton], { flex: '1 1 50%' }).component();
let outputFlexBox = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
width: 350
}).withItems([this.outputFileNameBox, outputButtonContainer], {
flex: '1 1 50%'
}).component();
this.appendToExistingFileCheckbox = view.modelBuilder.checkBox()
.withProperties({
label: 'Append output to existing file'
}).component();
this.appendToExistingFileCheckbox.enabled = false;
this.outputFileNameBox.onTextChanged((input) => {
if (input !== '') {
this.appendToExistingFileCheckbox.enabled = true;
} else {
this.appendToExistingFileCheckbox.enabled = false;
}
});
let outputFileForm = view.modelBuilder.formContainer()
.withFormItems([{
component: outputFlexBox,
title: 'Output file'
}, {
component: this.appendToExistingFileCheckbox,
title: ''
}], { horizontal: true, componentWidth: 200 }).component();
return outputFileForm;
}
private async execute() {
this.model.jobName = this.jobName;
this.model.id = this.stepId;
this.model.server = this.server;
this.model.stepName = this.nameTextBox.value;
this.model.subSystem = this.typeDropdown.value as string;
this.model.databaseName = this.databaseDropdown.value as string;
this.model.script = this.commandTextBox.value;
this.model.successAction = this.successActionDropdown.value as string;
this.model.retryAttempts = +this.retryAttemptsBox.value;
this.model.retryInterval = +this.retryIntervalBox.value;
this.model.failureAction = this.failureActionDropdown.value as string;
this.model.outputFileName = this.outputFileNameBox.value;
this.model.appendToLogFile = this.appendToExistingFileCheckbox.checked;
await this.model.save();
}
public async openNewStepDialog() {
let databases = await AgentUtils.getDatabases(this.ownerUri);
let queryProvider = await AgentUtils.getQueryProvider();
this.initializeUIComponents();
this.createGeneralTab(databases, queryProvider);
this.createAdvancedTab();
sqlops.window.modelviewdialog.openDialog(this.dialog);
}
}

View File

@@ -0,0 +1,405 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import { OperatorData } from '../data/operatorData';
import * as nls from 'vscode-nls';
import { AgentDialog } from './agentDialog';
const localize = nls.loadMessageBundle();
export class OperatorDialog extends AgentDialog<OperatorData> {
// Top level
private static readonly DialogTitle: string = localize('createOperator.createOperator', 'Create Operator');
private static readonly GeneralTabText: string = localize('createOperator.General', 'General');
private static readonly NotificationsTabText: string = localize('createOperator.Notifications', 'Notifications');
// General tab strings
private static readonly NameLabel: string = localize('createOperator.Name', 'Name');
private static readonly EnabledCheckboxLabel: string = localize('createOperator.Enabled', 'Enabled');
private static readonly EmailNameTextLabel: string = localize('createOperator.EmailName', 'E-mail Name');
private static readonly PagerEmailNameTextLabel: string = localize('createOperator.PagerEmailName', 'Pager E-mail Name');
private static readonly PagerMondayCheckBoxLabel: string = localize('createOperator.PagerMondayCheckBox', 'Monday');
private static readonly PagerTuesdayCheckBoxLabel: string = localize('createOperator.PagerTuesdayCheckBox', 'Tuesday');
private static readonly PagerWednesdayCheckBoxLabel: string = localize('createOperator.PagerWednesdayCheckBox', 'Wednesday');
private static readonly PagerThursdayCheckBoxLabel: string = localize('createOperator.PagerThursdayCheckBox', 'Thursday');
private static readonly PagerFridayCheckBoxLabel: string = localize('createOperator.PagerFridayCheckBox', 'Friday ');
private static readonly PagerSaturdayCheckBoxLabel: string = localize('createOperator.PagerSaturdayCheckBox', 'Saturday');
private static readonly PagerSundayCheckBoxLabel: string = localize('createOperator.PagerSundayCheckBox', 'Sunday');
// Notifications tab strings
private static readonly AlertsTableLabel: string = localize('createOperator.AlertListHeading', 'Alert list');
private static readonly AlertNameColumnLabel: string = localize('createOperator.AlertNameColumnLabel', 'Alert name');
private static readonly AlertEmailColumnLabel: string = localize('createOperator.AlertEmailColumnLabel', 'E-mail');
private static readonly AlertPagerColumnLabel: string = localize('createOperator.AlertPagerColumnLabel', 'Pager');
// UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab;
private notificationsTab: sqlops.window.modelviewdialog.DialogTab;
// General tab controls
private nameTextBox: sqlops.InputBoxComponent;
private enabledCheckBox: sqlops.CheckBoxComponent;
private emailNameTextBox: sqlops.InputBoxComponent;
private pagerEmailNameTextBox: sqlops.InputBoxComponent;
private pagerMondayCheckBox: sqlops.CheckBoxComponent;
private pagerTuesdayCheckBox: sqlops.CheckBoxComponent;
private pagerWednesdayCheckBox: sqlops.CheckBoxComponent;
private pagerThursdayCheckBox: sqlops.CheckBoxComponent;
private pagerFridayCheckBox: sqlops.CheckBoxComponent;
private pagerSaturdayCheckBox: sqlops.CheckBoxComponent;
private pagerSundayCheckBox: sqlops.CheckBoxComponent;
private weekdayPagerStartTimeInput: sqlops.InputBoxComponent;
private weekdayPagerEndTimeInput: sqlops.InputBoxComponent;
private saturdayPagerStartTimeInput: sqlops.InputBoxComponent;
private saturdayPagerEndTimeInput: sqlops.InputBoxComponent;
private sundayPagerStartTimeInput: sqlops.InputBoxComponent;
private sundayPagerEndTimeInput: sqlops.InputBoxComponent;
// Notification tab controls
private alertsTable: sqlops.TableComponent;
constructor(ownerUri: string) {
super(ownerUri, new OperatorData(ownerUri), OperatorDialog.DialogTitle);
}
protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
this.generalTab = sqlops.window.modelviewdialog.createTab(OperatorDialog.GeneralTabText);
this.notificationsTab = sqlops.window.modelviewdialog.createTab(OperatorDialog.NotificationsTabText);
this.initializeGeneralTab();
this.initializeNotificationTab();
this.dialog.content = [this.generalTab, this.notificationsTab];
}
private initializeGeneralTab() {
this.generalTab.registerContent(async view => {
this.nameTextBox = view.modelBuilder.inputBox().component();
this.enabledCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.EnabledCheckboxLabel
}).component();
this.enabledCheckBox.checked = true;
this.emailNameTextBox = view.modelBuilder.inputBox().component();
this.pagerEmailNameTextBox = view.modelBuilder.inputBox().component();
this.enabledCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.EnabledCheckboxLabel
}).component();
this.pagerMondayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerMondayCheckBoxLabel
}).component();
this.pagerMondayCheckBox.onChanged(() => {
if (this.pagerMondayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = true;
this.weekdayPagerEndTimeInput.enabled = true;
} else {
if (!this.pagerTuesdayCheckBox.checked && !this.pagerWednesdayCheckBox.checked &&
!this.pagerThursdayCheckBox.checked && !this.pagerFridayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = false;
this.weekdayPagerEndTimeInput.enabled = false;
}
}
});
this.pagerTuesdayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerTuesdayCheckBoxLabel
}).component();
this.pagerTuesdayCheckBox.onChanged(() => {
if (this.pagerTuesdayCheckBox .checked) {
this.weekdayPagerStartTimeInput.enabled = true;
this.weekdayPagerEndTimeInput.enabled = true;
} else {
if (!this.pagerMondayCheckBox.checked && !this.pagerWednesdayCheckBox.checked &&
!this.pagerThursdayCheckBox.checked && !this.pagerFridayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = false;
this.weekdayPagerEndTimeInput.enabled = false;
}
}
});
this.pagerWednesdayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerWednesdayCheckBoxLabel
}).component();
this.pagerWednesdayCheckBox.onChanged(() => {
if (this.pagerWednesdayCheckBox .checked) {
this.weekdayPagerStartTimeInput.enabled = true;
this.weekdayPagerEndTimeInput.enabled = true;
} else {
if (!this.pagerMondayCheckBox.checked && !this.pagerTuesdayCheckBox.checked &&
!this.pagerThursdayCheckBox.checked && !this.pagerFridayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = false;
this.weekdayPagerEndTimeInput.enabled = false;
}
}
});
this.pagerThursdayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerThursdayCheckBoxLabel
}).component();
this.pagerThursdayCheckBox.onChanged(() => {
if (this.pagerThursdayCheckBox .checked) {
this.weekdayPagerStartTimeInput.enabled = true;
this.weekdayPagerEndTimeInput.enabled = true;
} else {
if (!this.pagerMondayCheckBox.checked && !this.pagerWednesdayCheckBox.checked &&
!this.pagerTuesdayCheckBox.checked && !this.pagerFridayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = false;
this.weekdayPagerEndTimeInput.enabled = false;
}
}
});
this.weekdayPagerStartTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '08:00:00'
}).component();
this.weekdayPagerStartTimeInput.enabled = false;
let weekdayStartInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.weekdayPagerStartTimeInput,
title: 'Workday begin'
}]).component();
this.weekdayPagerEndTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '06:00:00'
}).component();
this.weekdayPagerEndTimeInput.enabled = false;
let weekdayEndInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.weekdayPagerEndTimeInput,
title: 'Workday end'
}]).component();
this.pagerFridayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerFridayCheckBoxLabel,
width: 80
}).component();
this.pagerFridayCheckBox.onChanged(() => {
if (this.pagerFridayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = true;
this.weekdayPagerEndTimeInput.enabled = true;
} else {
if (!this.pagerMondayCheckBox.checked && !this.pagerWednesdayCheckBox.checked &&
!this.pagerThursdayCheckBox.checked && !this.pagerTuesdayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = false;
this.weekdayPagerEndTimeInput.enabled = false;
}
}
});
let pagerFridayCheckboxContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
alignItems: 'baseline'
}).withItems([this.pagerFridayCheckBox, weekdayStartInputContainer, weekdayEndInputContainer])
.component();
this.pagerSaturdayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerSaturdayCheckBoxLabel,
width: 80
}).component();
this.pagerSaturdayCheckBox.onChanged(() => {
if (this.pagerSaturdayCheckBox.checked) {
this.saturdayPagerStartTimeInput.enabled = true;
this.saturdayPagerEndTimeInput.enabled = true;
} else {
this.saturdayPagerStartTimeInput.enabled = false;
this.saturdayPagerEndTimeInput.enabled = false;
}
});
this.saturdayPagerStartTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '08:00:00'
}).component();
this.saturdayPagerStartTimeInput.enabled = false;
let saturdayStartInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.saturdayPagerStartTimeInput,
title: 'Workday begin'
}]).component();
this.saturdayPagerEndTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '06:00:00'
}).component();
this.saturdayPagerEndTimeInput.enabled = false;
let saturdayEndInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.saturdayPagerEndTimeInput,
title: 'Workday end'
}]).component();
let pagerSaturdayCheckboxContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
alignItems: 'baseline'
}).withItems([this.pagerSaturdayCheckBox, saturdayStartInputContainer, saturdayEndInputContainer])
.component();
this.pagerSundayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerSundayCheckBoxLabel,
width: 80
}).component();
this.pagerSundayCheckBox.onChanged(() => {
if (this.pagerSundayCheckBox.checked) {
this.sundayPagerStartTimeInput.enabled = true;
this.sundayPagerEndTimeInput.enabled = true;
} else {
this.sundayPagerStartTimeInput.enabled = false;
this.sundayPagerEndTimeInput.enabled = false;
}
});
this.sundayPagerStartTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '08:00:00'
}).component();
this.sundayPagerStartTimeInput.enabled = false;
let sundayStartInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.sundayPagerStartTimeInput,
title: 'Workday begin'
}]).component();
this.sundayPagerEndTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '06:00:00'
}).component();
this.sundayPagerEndTimeInput.enabled = false;
let sundayEndInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.sundayPagerEndTimeInput,
title: 'Workday end'
}]).component();
let pagerSundayCheckboxContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
alignItems: 'baseline'
}).withItems([this.pagerSundayCheckBox, sundayStartInputContainer, sundayEndInputContainer])
.component();
let checkBoxContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.pagerMondayCheckBox,
title: ''
}, {
component: this.pagerTuesdayCheckBox,
title: ''
}, {
component: this.pagerWednesdayCheckBox,
title: ''
}, {
component: this.pagerThursdayCheckBox,
title: ''
}, {
component: pagerFridayCheckboxContainer,
title: ''
}, {
component: pagerSaturdayCheckboxContainer,
title: ''
}, {
component: pagerSundayCheckboxContainer,
title: ''
}]).component();
let pagerContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row'
}).withItems([checkBoxContainer])
.component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.nameTextBox,
title: OperatorDialog.NameLabel
}, {
component: this.enabledCheckBox,
title: ''
}, {
component: this.emailNameTextBox,
title: OperatorDialog.EmailNameTextLabel
}, {
component: this.pagerEmailNameTextBox,
title: OperatorDialog.PagerEmailNameTextLabel
}, {
component: pagerContainer,
title: ''
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private initializeNotificationTab() {
this.notificationsTab.registerContent(async view => {
this.alertsTable = view.modelBuilder.table()
.withProperties({
columns: [
OperatorDialog.AlertNameColumnLabel,
OperatorDialog.AlertEmailColumnLabel,
OperatorDialog.AlertPagerColumnLabel
],
data: [],
height: 500
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.alertsTable,
title: OperatorDialog.AlertsTableLabel
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
protected updateModel() {
this.model.name = this.nameTextBox.value;
this.model.enabled = this.enabledCheckBox.checked;
this.model.emailAddress = this.emailNameTextBox.value;
this.model.pagerAddress = this.pagerEmailNameTextBox.value;
this.model.weekdayPagerStartTime = this.weekdayPagerStartTimeInput.value;
this.model.weekdayPagerEndTime = this.weekdayPagerEndTimeInput.value;
this.model.saturdayPagerStartTime = this.saturdayPagerStartTimeInput.value;
this.model.saturdayPagerEndTime = this.saturdayPagerEndTimeInput.value;
this.model.sundayPagerStartTime = this.sundayPagerStartTimeInput.value;
this.model.sundayPagerEndTime = this.sundayPagerEndTimeInput.value;
}
}

View File

@@ -0,0 +1,92 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import { PickScheduleData } from '../data/pickScheduleData';
export class PickScheduleDialog {
// TODO: localize
// Top level
private readonly DialogTitle: string = 'Job Schedules';
private readonly OkButtonText: string = 'OK';
private readonly CancelButtonText: string = 'Cancel';
private readonly SchedulesTabText: string = 'Schedules';
// UI Components
private dialog: sqlops.window.modelviewdialog.Dialog;
private schedulesTable: sqlops.TableComponent;
private model: PickScheduleData;
private _onSuccess: vscode.EventEmitter<PickScheduleData> = new vscode.EventEmitter<PickScheduleData>();
public readonly onSuccess: vscode.Event<PickScheduleData> = this._onSuccess.event;
constructor(ownerUri: string) {
this.model = new PickScheduleData(ownerUri);
}
public async showDialog() {
await this.model.initialize();
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
this.initializeContent();
this.dialog.okButton.onClick(async () => await this.execute());
this.dialog.cancelButton.onClick(async () => await this.cancel());
this.dialog.okButton.label = this.OkButtonText;
this.dialog.cancelButton.label = this.CancelButtonText;
sqlops.window.modelviewdialog.openDialog(this.dialog);
}
private initializeContent() {
this.dialog.registerContent(async view => {
this.schedulesTable = view.modelBuilder.table()
.withProperties({
columns: [
'Schedule Name'
],
data: [],
height: 600,
width: 400
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.schedulesTable,
title: 'Schedules'
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
if (this.model.schedules) {
let data: any[][] = [];
for (let i = 0; i < this.model.schedules.length; ++i) {
let schedule = this.model.schedules[i];
data[i] = [ schedule.name ];
}
this.schedulesTable.data = data;
}
});
}
private async execute() {
this.updateModel();
await this.model.save();
this._onSuccess.fire(this.model);
}
private async cancel() {
}
private updateModel() {
let selectedRows = this.schedulesTable.selectedRows;
if (selectedRows && selectedRows.length > 0) {
let selectedRow = selectedRows[0];
this.model.selectedSchedule = this.model.schedules[selectedRow];
}
}
}

View File

@@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import { AgentDialog } from './agentDialog';
import { ProxyData } from '../data/proxyData';
const localize = nls.loadMessageBundle();
export class ProxyDialog extends AgentDialog<ProxyData> {
// Top level
private static readonly DialogTitle: string = localize('createProxy.createAlert', 'Create Alert');
private static readonly GeneralTabText: string = localize('createProxy.General', 'General');
// General tab strings
private static readonly ProxyNameTextBoxLabel: string = localize('createProxy.ProxyName', 'Proxy name');
private static readonly CredentialNameTextBoxLabel: string = localize('createProxy.CredentialName', 'Credential name');
private static readonly DescriptionTextBoxLabel: string = localize('createProxy.Description', 'Description');
private static readonly SubsystemsTableLabel: string = localize('createProxy.Subsystems', 'Subsystems');
private static readonly SubsystemNameColumnLabel: string = localize('createProxy.SubsystemName', 'Subsystem');
// UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab;
// General tab controls
private proxyNameTextBox: sqlops.InputBoxComponent;
private credentialNameTextBox: sqlops.InputBoxComponent;
private descriptionTextBox: sqlops.InputBoxComponent;
private subsystemsTable: sqlops.TableComponent;
constructor(ownerUri: string) {
super(ownerUri, new ProxyData(ownerUri), ProxyDialog.DialogTitle);
}
protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
this.generalTab = sqlops.window.modelviewdialog.createTab(ProxyDialog.GeneralTabText);
this.initializeGeneralTab();
this.dialog.content = [this.generalTab];
}
private initializeGeneralTab() {
this.generalTab.registerContent(async view => {
this.proxyNameTextBox = view.modelBuilder.inputBox().component();
this.credentialNameTextBox = view.modelBuilder.inputBox().component();
this.descriptionTextBox = view.modelBuilder.inputBox().component();
this.subsystemsTable = view.modelBuilder.table()
.withProperties({
columns: [
ProxyDialog.SubsystemNameColumnLabel
],
data: [],
height: 500
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.proxyNameTextBox,
title: ProxyDialog.ProxyNameTextBoxLabel
}, {
component: this.credentialNameTextBox,
title: ProxyDialog.CredentialNameTextBoxLabel
}, {
component: this.descriptionTextBox,
title: ProxyDialog.DescriptionTextBoxLabel
}, {
component: this.subsystemsTable,
title: ProxyDialog.SubsystemsTableLabel
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
protected updateModel() {
this.model.accountName = this.proxyNameTextBox.value;
this.model.credentialName = this.credentialNameTextBox.value;
this.model.description = this.descriptionTextBox.value;
}
}

View File

@@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import { ScheduleData } from '../data/scheduleData';
export class ScheduleDialog {
// Top level
private readonly DialogTitle: string = 'New Schedule';
private readonly OkButtonText: string = 'OK';
private readonly CancelButtonText: string = 'Cancel';
// UI Components
private dialog: sqlops.window.modelviewdialog.Dialog;
private schedulesTable: sqlops.TableComponent;
private model: ScheduleData;
private _onSuccess: vscode.EventEmitter<ScheduleData> = new vscode.EventEmitter<ScheduleData>();
public readonly onSuccess: vscode.Event<ScheduleData> = this._onSuccess.event;
constructor(ownerUri: string) {
this.model = new ScheduleData(ownerUri);
}
public async showDialog() {
await this.model.initialize();
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
this.initializeContent();
this.dialog.okButton.onClick(async () => await this.execute());
this.dialog.cancelButton.onClick(async () => await this.cancel());
this.dialog.okButton.label = this.OkButtonText;
this.dialog.cancelButton.label = this.CancelButtonText;
sqlops.window.modelviewdialog.openDialog(this.dialog);
}
private initializeContent() {
this.dialog.registerContent(async view => {
this.schedulesTable = view.modelBuilder.table()
.withProperties({
columns: [
'Schedule Name'
],
data: [],
height: 600,
width: 400
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.schedulesTable,
title: 'Schedules'
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
if (this.model.schedules) {
let data: any[][] = [];
for (let i = 0; i < this.model.schedules.length; ++i) {
let schedule = this.model.schedules[i];
data[i] = [ schedule.name ];
}
this.schedulesTable.data = data;
}
});
}
private async execute() {
this.updateModel();
await this.model.save();
this._onSuccess.fire(this.model);
}
private async cancel() {
}
private updateModel() {
let selectedRows = this.schedulesTable.selectedRows;
if (selectedRows && selectedRows.length > 0) {
let selectedRow = selectedRows[0];
this.model.selectedSchedule = this.model.schedules[selectedRow];
}
}
}

View File

@@ -2,7 +2,16 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
/// <reference path='../../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../../src/sql/sqlops.d.ts'/>
/// <reference types='@types/node'/>
export enum AgentDialogMode {
CREATE = 1,
EDIT = 2,
VIEW = 3
}
export interface IAgentDialogData {
dialogMode: AgentDialogMode;
initialize(): void;
save(): void;
}

View File

@@ -6,12 +6,10 @@
import vscode = require('vscode');
import { MainController } from './mainController';
import { ApiWrapper } from './apiWrapper';
export let controller: MainController;
export function activate(context: vscode.ExtensionContext) {
let apiWrapper = new ApiWrapper();
controller = new MainController(context, apiWrapper);
controller = new MainController(context);
controller.activate();
}

View File

@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* 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 vscode from 'vscode';
import { AlertDialog } from './dialogs/alertDialog';
import { JobDialog } from './dialogs/jobDialog';
import { OperatorDialog } from './dialogs/operatorDialog';
import { ProxyDialog } from './dialogs/proxyDialog';
import { JobStepDialog } from './dialogs/jobStepDialog';
import { PickScheduleDialog } from './dialogs/pickScheduleDialog';
/**
* The main controller class that initializes the extension
*/
export class MainController {
protected _context: vscode.ExtensionContext;
// PUBLIC METHODS //////////////////////////////////////////////////////
public constructor(context: vscode.ExtensionContext) {
this._context = context;
}
/**
* Activates the extension
*/
public activate(): void {
vscode.commands.registerCommand('agent.openCreateJobDialog', (ownerUri: string) => {
let dialog = new JobDialog(ownerUri);
dialog.openDialog();
});
vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, jobId: string, server: string, stepId: number) => {
let dialog = new JobStepDialog(ownerUri, jobId, server, stepId);
dialog.openNewStepDialog();
});
vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string) => {
let dialog = new PickScheduleDialog(ownerUri);
dialog.showDialog();
});
vscode.commands.registerCommand('agent.openAlertDialog', (ownerUri: string, alertInfo: sqlops.AgentAlertInfo) => {
let dialog = new AlertDialog(ownerUri, alertInfo);
dialog.openDialog();
});
vscode.commands.registerCommand('agent.openCreateOperatorDialog', (ownerUri: string) => {
let dialog = new OperatorDialog(ownerUri);
dialog.openDialog();
});
vscode.commands.registerCommand('agent.openCreateProxyDialog', (ownerUri: string) => {
let dialog = new ProxyDialog(ownerUri);
dialog.openDialog();
});
}
/**
* Deactivates the extension
*/
public deactivate(): void {
}
}

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import * as sqlops from 'sqlops';
import { JobData } from '../data/jobData';
import { TestAgentService } from './testAgentService';
const testOwnerUri = 'agent://testuri';
suite('Agent extension', () => {
test('Create Job Data', async () => {
let testAgentService = new TestAgentService();
let data = new JobData(testOwnerUri, testAgentService);
data.save();
});
});

View File

@@ -0,0 +1,105 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as sqlops from 'sqlops';
export class TestAgentService implements sqlops.AgentServicesProvider {
handle?: number;
readonly providerId: string = 'Test Provider';
// Job management methods
getJobs(ownerUri: string): Thenable<sqlops.AgentJobsResult> {
return undefined;
}
getJobHistory(ownerUri: string, jobId: string): Thenable<sqlops.AgentJobHistoryResult> {
return undefined;
}
jobAction(ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
return undefined;
}
createJob(ownerUri: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.CreateAgentJobResult> {
return undefined;
}
updateJob(ownerUri: string, originalJobName: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.UpdateAgentJobResult> {
return undefined;
}
deleteJob(ownerUri: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
getJobDefaults(ownerUri: string): Thenable<sqlops.AgentJobDefaultsResult> {
return undefined;
}
// Job Step management methods
createJobStep(ownerUri: string, jobInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.CreateAgentJobStepResult> {
return undefined;
}
updateJobStep(ownerUri: string, originalJobStepName: string, jobInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.UpdateAgentJobStepResult> {
return undefined;
}
deleteJobStep(ownerUri: string, jobInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
// Alert management methods
getAlerts(ownerUri: string): Thenable<sqlops.AgentAlertsResult> {
return undefined;
}
createAlert(ownerUri: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.CreateAgentAlertResult> {
return undefined;
}
updateAlert(ownerUri: string, originalAlertName: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.UpdateAgentAlertResult> {
return undefined;
}
deleteAlert(ownerUri: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
// Operator management methods
getOperators(ownerUri: string): Thenable<sqlops.AgentOperatorsResult> {
return undefined;
}
createOperator(ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.CreateAgentOperatorResult> {
return undefined;
}
updateOperator(ownerUri: string, originalOperatorName: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.UpdateAgentOperatorResult> {
return undefined;
}
deleteOperator(ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
// Proxy management methods
getProxies(ownerUri: string): Thenable<sqlops.AgentProxiesResult> {
return undefined;
}
createProxy(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.CreateAgentOperatorResult> {
return undefined;
}
updateProxy(ownerUri: string, originalProxyName: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.UpdateAgentOperatorResult> {
return undefined;
}
deleteProxy(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
// Job Schedule management methods
getJobSchedules(ownerUri: string): Thenable<sqlops.AgentJobSchedulesResult> {
return undefined;
}
createJobSchedule(ownerUri: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.CreateAgentJobScheduleResult> {
return undefined;
}
updateJobSchedule(ownerUri: string, originalScheduleName: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.UpdateAgentJobScheduleResult> {
return undefined;
}
deleteJobSchedule(ownerUri: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
registerOnUpdated(handler: () => any): void {
}
}

9
extensions/agent/src/typings/ref.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../src/sql/sqlops.d.ts'/>
/// <reference path='../../../../src/sql/sqlops.proposed.d.ts'/>
/// <reference types='@types/node'/>

File diff suppressed because it is too large Load Diff

View File

@@ -2,4 +2,4 @@
See [documentation](https://code.visualstudio.com/docs/editor/versioncontrol#_merge-conflicts).
**Notice** This is a an extension that is bundled with Visual Studio Code.
**Notice** This is a an extension that is bundled with SQL Operations Studio.

View File

@@ -658,7 +658,7 @@
}
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.7",
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.9",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
"vscode-extension-telemetry": "^0.0.15"

View File

@@ -8,9 +8,9 @@
"GO",
"-- Create the new database if it does not exist already",
"IF NOT EXISTS (",
"\tSELECT name",
"\tSELECT [name]",
"\t\tFROM sys.databases",
"\t\tWHERE name = N'${1:DatabaseName}'",
"\t\tWHERE [name] = N'${1:DatabaseName}'",
")",
"CREATE DATABASE ${1:DatabaseName}",
"GO"
@@ -29,9 +29,9 @@
"-- ALTER DATABASE ${1:DatabaseName} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;",
"-- Drop the database if it exists",
"IF EXISTS (",
" SELECT name",
" SELECT [name]",
" FROM sys.databases",
" WHERE name = N'${1:DatabaseName}'",
" WHERE [name] = N'${1:DatabaseName}'",
")",
"DROP DATABASE ${1:DatabaseName}",
"GO"
@@ -42,38 +42,33 @@
"Create a new Table": {
"prefix": "sqlCreateTable",
"body": [
"-- Create a new table called '${1:TableName}' in schema '${2:SchemaName}'",
"-- Create a new table called '[${1:TableName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"-- Drop the table if it already exists",
"IF OBJECT_ID('${2:SchemaName}.${1:TableName}', 'U') IS NOT NULL",
"DROP TABLE ${2:SchemaName}.${1:TableName}",
"IF OBJECT_ID('[${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]', 'U') IS NOT NULL",
"DROP TABLE [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
"GO",
"-- Create the table in the specified schema",
"CREATE TABLE ${2:SchemaName}.${1:TableName}",
"-- Create the table in the specified database and schema",
"CREATE TABLE [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
"(",
"\t${1:TableName}Id INT NOT NULL PRIMARY KEY, -- primary key column",
"\t$3Column1 [NVARCHAR](50) NOT NULL,",
"\t$4Column2 [NVARCHAR](50) NOT NULL",
"\t-- specify more columns here",
"\t[${4:ColumnName}]Id INT NOT NULL PRIMARY KEY, -- Primary Key column",
"\t[${5:ColumnName1}] [NVARCHAR](50) NOT NULL,",
"\t[${6:ColumnName2}] [NVARCHAR](50) NOT NULL",
"\t-- Specify more columns here",
");",
"GO"
],
"description": "Create a new Table"
},
"Drop a Table": {
"prefix": "sqlDropTable",
"body": [
"-- Drop the table '${1:TableName}' in schema '${2:SchemaName}'",
"IF EXISTS (",
"\tSELECT *",
"\t\tFROM sys.tables",
"\t\tJOIN sys.schemas",
"\t\t\tON sys.tables.schema_id = sys.schemas.schema_id",
"\tWHERE sys.schemas.name = N'${2:SchemaName}'",
"\t\tAND sys.tables.name = N'${1:TableName}'",
")",
"\tDROP TABLE ${2:SchemaName}.${1:TableName}",
"GO"
"-- Drop a table called '${3:TableName}' in schema '${2:SchemaName}' in Database '${1:DatabaseName}'",
"-- Drop the table if it already exists",
"IF OBJECT_ID('[${1:DatabaseName}].[${2:SchemaName}].[${3:TableName}]', 'U') IS NOT NULL",
"DROP TABLE [${1:DatabaseName}].[${2:SchemaName}].[${3:TableName}]",
"GO"
],
"description": "Drop a Table"
},
@@ -81,9 +76,9 @@
"Add a new column to a Table": {
"prefix": "sqlAddColumn",
"body": [
"-- Add a new column '${1:NewColumnName}' to table '${2:TableName}' in schema '${3:SchemaName}'",
"ALTER TABLE ${3:SchemaName}.${2:TableName}",
"\tADD ${1:NewColumnName} /*new_column_name*/ int /*new_column_datatype*/ NULL /*new_column_nullability*/",
"-- Add a new column '[${1:NewColumnName}]' to table '[${2:TableName}]' in schema '[${3:SchemaName}]' in database '[${4:DatabaseName}]'",
"ALTER TABLE [${4:DatabaseName}].[${3:SchemaName}].[${2:TableName}]",
"\tADD [${1:NewColumnName}] /*new_column_name*/ ${5:int} /*new_column_datatype*/ ${6:NULL} /*new_column_nullability*/",
"GO"
],
"description": "Add a new column to a Table"
@@ -92,9 +87,9 @@
"Drop a column from a Table": {
"prefix": "sqlDropColumn",
"body": [
"-- Drop '${1:ColumnName}' from table '${2:TableName}' in schema '${3:SchemaName}'",
"ALTER TABLE ${3:SchemaName}.${2:TableName}",
"\tDROP COLUMN ${1:ColumnName}",
"-- Drop '[${1:ColumnName}]' from table '[${2:TableName}]' in schema '[${3:SchemaName}]' in database '[${4:DatabaseName}]'",
"ALTER TABLE [${4:DatabaseName}].[${3:SchemaName}].[${2:TableName}]",
"\tDROP COLUMN [${1:ColumnName}]",
"GO"
],
"description": "Add a new column to a Table"
@@ -103,9 +98,9 @@
"Select rows from a Table or a View": {
"prefix": "sqlSelect",
"body": [
"-- Select rows from a Table or View '${1:TableOrViewName}' in schema '${2:SchemaName}'",
"SELECT * FROM ${2:SchemaName}.${1:TableOrViewName}",
"WHERE $3\t/* add search conditions here */",
"-- Select rows from a Table or View '[${1:TableOrViewName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"SELECT * FROM [${3:DatabaseName}].[${2:SchemaName}].[${1:TableOrViewName}]",
"WHERE ${4:/* add search conditions here */}",
"GO"
],
"description": "Select rows from a Table or a View"
@@ -114,19 +109,19 @@
"Insert rows into a Table": {
"prefix": "sqlInsertRows",
"body": [
"-- Insert rows into table '${1:TableName}'",
"INSERT INTO ${1:TableName}",
"( -- columns to insert data into",
" $2[Column1], [Column2], [Column3]",
"-- Insert rows into table '${1:TableName}' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"INSERT INTO [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
"( -- Columns to insert data into",
" ${4:[ColumnName1], [ColumnName2], [ColumnName3]}",
")",
"VALUES",
"( -- first row: values for the columns in the list above",
" $3Column1_Value, Column2_Value, Column3_Value",
"( -- First row: values for the columns in the list above",
" ${5:ColumnValue1, ColumnValue2, ColumnValue3}",
"),",
"( -- second row: values for the columns in the list above",
" $4Column1_Value, Column2_Value, Column3_Value",
"( -- Second row: values for the columns in the list above",
" ${6:ColumnValue1, ColumnValue2, ColumnValue3}",
")",
"-- add more rows here",
"-- Add more rows here",
"GO"
],
"description": "Insert rows into a Table"
@@ -135,9 +130,9 @@
"Delete rows from a Table": {
"prefix": "sqlDeleteRows",
"body": [
"-- Delete rows from table '${1:TableName}'",
"DELETE FROM ${1:TableName}",
"WHERE $2\t/* add search conditions here */",
"-- Delete rows from table '[${1:TableName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"DELETE FROM [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
"WHERE ${4:/* add search conditions here */}",
"GO"
],
"description": "Delete rows from a Table"
@@ -146,13 +141,13 @@
"Update rows in a Table": {
"prefix": "sqlUpdateRows",
"body": [
"-- Update rows in table '${1:TableName}'",
"UPDATE ${1:TableName}",
"-- Update rows in table '[${1:TableName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"UPDATE [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
"SET",
"\t$2[Colum1] = Colum1_Value,",
"\t$3[Colum2] = Colum2_Value",
"\t-- add more columns and values here",
"WHERE $4\t/* add search conditions here */",
"\t[${4:ColumnName1}] = ${5:ColumnValue1},",
"\t[${6:ColumnName2}] = ${7:ColumnValue2}",
"\t-- Add more columns and values here",
"WHERE ${8:/* add search conditions here */}",
"GO"
],
"description": "Update rows in a Table"
@@ -207,8 +202,8 @@
"prefix": "sqlListTablesAndViews",
"body": [
"-- Get a list of tables and views in the current database",
"SELECT table_catalog [database], table_schema [schema], table_name name, table_type type",
"FROM information_schema.tables",
"SELECT table_catalog [database], table_schema [schema], table_name [name], table_type [type]",
"FROM INFORMATION_SCHEMA.TABLES",
"GO"
],
"description": "List tables and vies in the current database"
@@ -218,7 +213,7 @@
"prefix": "sqlListDatabases",
"body": [
"-- Get a list of databases",
"SELECT name FROM sys.databases",
"SELECT [name] FROM sys.databases",
"GO"
],
"description": "List databases"
@@ -232,8 +227,8 @@
"\tTableName = tbl.table_schema + '.' + tbl.table_name, ",
"\tColumnName = col.column_name, ",
"\tColumnDataType = col.data_type",
"FROM information_schema.tables tbl",
"INNER JOIN information_schema.columns col ",
"FROM INFORMATION_SCHEMA.TABLES tbl",
"INNER JOIN INFORMATION_SCHEMA.COLUMNS col ",
"\tON col.table_name = tbl.table_name",
"\tAND col.table_schema = tbl.table_schema",
"",
@@ -247,20 +242,20 @@
"prefix": "sqlCursor",
"body": [
"-- Declare a cursor for a Table or a View '${1:TableOrViewName}' in schema '${2:SchemaName}'",
"DECLARE @Column1 NVARCHAR(50), @Column2 NVARCHAR(50)",
"DECLARE @ColumnName1 NVARCHAR(50), @ColumnName2 NVARCHAR(50)",
"",
"DECLARE db_cursor CURSOR FOR",
"SELECT Column1, Column2",
"SELECT ColumnName1, Column2",
"FROM $2.$1",
"",
"OPEN db_cursor",
"FETCH NEXT FROM db_cursor INTO @Column1, @Column2",
"FETCH NEXT FROM db_cursor INTO @ColumnName1, @ColumnName2",
"",
"WHILE @@FETCH_STATUS = 0",
"BEGIN",
"\t-- add instructions to be executed for every row",
"\t$3",
"\tFETCH NEXT FROM db_cursor INTO @Column1, @Column2",
"\tFETCH NEXT FROM db_cursor INTO @ColumnName1, @ColumnName2",
"END",
"",
"CLOSE db_cursor",
@@ -303,5 +298,34 @@
"GO"
],
"description": "Get Space Used by Tables"
}
},
"Create a new Index": {
"prefix": "sqlCreateIndex",
"body": [
"-- Create a nonclustered index with or without a unique constraint",
"-- Or create a clustered index on table '[${1:TableName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"CREATE ${5:/*UNIQUE or CLUSTERED*/} INDEX IX_${4:IndexName} ON [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}] ([${6:ColumnName1}] DESC /*Change sort order as needed*/",
"GO"
],
"description": "Create a new Index"
},
"Create a new Temporary Table": {
"prefix": "sqlCreateTempTable",
"body": [
"-- Drop a temporary table called '#${1:TableName}'",
"-- Drop the table if it already exists",
"IF OBJECT_ID('tempDB..#${1:TableName}', 'U') IS NOT NULL",
"DROP TABLE #${1:TableName}",
"GO",
"-- Create the temporary table from a physical table called '${4:TableName}' in schema '${3:SchemaName}' in database '${2:DatabaseName}'",
"SELECT *",
"INTO #${1:TableName}",
"FROM [${2:DatabaseName}].[${3:[SchemaName}].[${4:TableName}]",
"WHERE ${5:/* add search conditions here */}"
],
"description": "Create a new Temporary Table"
},
}

View File

@@ -1,6 +1,6 @@
{
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "1.4.0-alpha.35",
"version": "1.5.0-alpha.4",
"downloadFileNames": {
"Windows_86": "win-x86-netcoreapp2.1.zip",
"Windows_64": "win-x64-netcoreapp2.1.zip",
@@ -16,4 +16,4 @@
},
"installDirectory": "../sqltoolsservice/{#platform#}/{#version#}",
"executableFiles": ["MicrosoftSqlToolsServiceLayer.exe", "MicrosoftSqlToolsServiceLayer"]
}
}

View File

@@ -37,7 +37,7 @@ export default class ContextProvider {
public onDashboardOpen(e: sqlops.DashboardDocument): void {
let iscloud: boolean;
let edition: number;
if (e.profile.providerName.toLowerCase() === 'mssql' && !types.isUndefinedOrNull(e.serverInfo.engineEditionId)) {
if (e.profile.providerName.toLowerCase() === 'mssql' && !types.isUndefinedOrNull(e.serverInfo) && !types.isUndefinedOrNull(e.serverInfo.engineEditionId)) {
if (isCloudEditions.some(i => i === e.serverInfo.engineEditionId)) {
iscloud = true;
} else {

View File

@@ -5,9 +5,8 @@
'use strict';
import { NotificationType, RequestType } from 'vscode-languageclient';
import * as sqlops from 'sqlops';
import { ITelemetryEventProperties, ITelemetryEventMeasures } from './telemetry';
import * as sqlops from 'sqlops';
// ------------------------------- < Telemetry Sent Event > ------------------------------------
@@ -31,50 +30,253 @@ export class TelemetryParams {
// ------------------------------- </ Telemetry Sent Event > ----------------------------------
// Job Management types
// ------------------------------- < Agent Management > ------------------------------------
// Job management parameters
export interface AgentJobsParams {
ownerUri: string;
jobId: string;
}
export interface AgentJobsResult {
succeeded: boolean;
errorMessage: string;
jobs: sqlops.AgentJobInfo[];
}
export interface AgentJobHistoryParams {
ownerUri: string;
jobId: string;
}
export interface AgentJobHistoryResult {
succeeded: boolean;
errorMessage: string;
jobs: sqlops.AgentJobHistoryInfo[];
}
export interface AgentJobActionParams {
ownerUri: string;
jobName: string;
action: string;
}
export interface AgentJobActionResult {
succeeded: boolean;
errorMessage: string;
export interface CreateAgentJobParams {
ownerUri: string;
job: sqlops.AgentJobInfo;
}
export interface UpdateAgentJobParams {
ownerUri: string;
originalJobName: string;
job: sqlops.AgentJobInfo;
}
export interface DeleteAgentJobParams {
ownerUri: string;
job: sqlops.AgentJobInfo;
}
export interface AgentJobDefaultsParams {
ownerUri: string;
}
// Job Step management parameters
export interface CreateAgentJobStepParams {
ownerUri: string;
step: sqlops.AgentJobStepInfo;
}
export interface UpdateAgentJobStepParams {
ownerUri: string;
originalJobStepName: string;
step: sqlops.AgentJobStepInfo;
}
export interface DeleteAgentJobStepParams {
ownerUri: string;
step: sqlops.AgentJobStepInfo;
}
// Alert management parameters
export interface AgentAlertsParams {
ownerUri: string;
}
export interface CreateAgentAlertParams {
ownerUri: string;
alert: sqlops.AgentAlertInfo;
}
export interface UpdateAgentAlertParams {
ownerUri: string;
originalAlertName: string;
alert: sqlops.AgentAlertInfo;
}
export interface DeleteAgentAlertParams {
ownerUri: string;
alert: sqlops.AgentAlertInfo;
}
// Operator management parameters
export interface AgentOperatorsParams {
ownerUri: string;
}
export interface CreateAgentOperatorParams {
ownerUri: string;
operator: sqlops.AgentOperatorInfo;
}
export interface UpdateAgentOperatorParams {
ownerUri: string;
originalOperatorName: string;
operator: sqlops.AgentOperatorInfo;
}
export interface DeleteAgentOperatorParams {
ownerUri: string;
operator: sqlops.AgentOperatorInfo;
}
// Proxy management parameters
export interface AgentProxiesParams {
ownerUri: string;
}
export interface CreateAgentProxyParams {
ownerUri: string;
proxy: sqlops.AgentProxyInfo;
}
export interface UpdateAgentProxyParams {
ownerUri: string;
originalProxyName: string;
proxy: sqlops.AgentProxyInfo;
}
export interface DeleteAgentProxyParams {
ownerUri: string;
proxy: sqlops.AgentProxyInfo;
}
// Job Schedule management parameters
export interface AgentJobScheduleParams {
ownerUri: string;
}
export interface CreateAgentJobScheduleParams {
ownerUri: string;
schedule: sqlops.AgentJobScheduleInfo;
}
export interface UpdateAgentJobScheduleParams {
ownerUri: string;
originalScheduleName: string;
schedule: sqlops.AgentJobScheduleInfo;
}
export interface DeleteAgentJobScheduleParams {
ownerUri: string;
schedule: sqlops.AgentJobScheduleInfo;
}
// Agent Job management requests
export namespace AgentJobsRequest {
export const type = new RequestType<AgentJobsParams, AgentJobsResult, void, void>('agent/jobs');
export const type = new RequestType<AgentJobsParams, sqlops.AgentJobsResult, void, void>('agent/jobs');
}
export namespace AgentJobHistoryRequest {
export const type = new RequestType<AgentJobHistoryParams, AgentJobHistoryResult, void, void>('agent/jobhistory');
export const type = new RequestType<AgentJobHistoryParams, sqlops.AgentJobHistoryResult, void, void>('agent/jobhistory');
}
export namespace AgentJobActionRequest {
export const type = new RequestType<AgentJobActionParams, AgentJobActionResult, void, void>('agent/jobaction');
export const type = new RequestType<AgentJobActionParams, sqlops.ResultStatus, void, void>('agent/jobaction');
}
export namespace CreateAgentJobRequest {
export const type = new RequestType<CreateAgentJobParams, sqlops.CreateAgentJobResult, void, void>('agent/createjob');
}
export namespace UpdateAgentJobRequest {
export const type = new RequestType<UpdateAgentJobParams, sqlops.UpdateAgentJobResult, void, void>('agent/updatejob');
}
export namespace DeleteAgentJobRequest {
export const type = new RequestType<DeleteAgentJobParams, sqlops.ResultStatus, void, void>('agent/deletejob');
}
export namespace AgentJobDefaultsRequest {
export const type = new RequestType<AgentJobDefaultsParams, sqlops.AgentJobDefaultsResult, void, void>('agent/jobdefaults');
}
// Job Step requests
export namespace CreateAgentJobStepRequest {
export const type = new RequestType<CreateAgentJobStepParams, sqlops.CreateAgentJobStepResult, void, void>('agent/createjobstep');
}
export namespace UpdateAgentJobStepRequest {
export const type = new RequestType<UpdateAgentJobStepParams, sqlops.UpdateAgentJobStepResult, void, void>('agent/updatejobstep');
}
export namespace DeleteAgentJobStepRequest {
export const type = new RequestType<DeleteAgentJobStepParams, sqlops.ResultStatus, void, void>('agent/deletejobstep');
}
// Alerts requests
export namespace AgentAlertsRequest {
export const type = new RequestType<CreateAgentAlertParams, sqlops.AgentAlertsResult, void, void>('agent/alerts');
}
export namespace CreateAgentAlertRequest {
export const type = new RequestType<CreateAgentAlertParams, sqlops.CreateAgentAlertResult, void, void>('agent/createalert');
}
export namespace UpdateAgentAlertRequest {
export const type = new RequestType<UpdateAgentAlertParams, sqlops.UpdateAgentAlertResult, void, void>('agent/updatealert');
}
export namespace DeleteAgentAlertRequest {
export const type = new RequestType<DeleteAgentAlertParams, sqlops.ResultStatus, void, void>('agent/deletealert');
}
// Operators requests
export namespace AgentOperatorsRequest {
export const type = new RequestType<CreateAgentOperatorParams, sqlops.AgentOperatorsResult, void, void>('agent/operators');
}
export namespace CreateAgentOperatorRequest {
export const type = new RequestType<CreateAgentOperatorParams, sqlops.CreateAgentOperatorResult, void, void>('agent/createoperator');
}
export namespace UpdateAgentOperatorRequest {
export const type = new RequestType<UpdateAgentOperatorParams, sqlops.UpdateAgentOperatorResult, void, void>('agent/updateoperator');
}
export namespace DeleteAgentOperatorRequest {
export const type = new RequestType<DeleteAgentOperatorParams, sqlops.ResultStatus, void, void>('agent/deleteoperator');
}
// Proxies requests
export namespace AgentProxiesRequest {
export const type = new RequestType<CreateAgentProxyParams, sqlops.AgentProxiesResult, void, void>('agent/proxies');
}
export namespace CreateAgentProxyRequest {
export const type = new RequestType<CreateAgentProxyParams, sqlops.CreateAgentProxyResult, void, void>('agent/createproxy');
}
export namespace UpdateAgentProxyRequest {
export const type = new RequestType<UpdateAgentProxyParams, sqlops.UpdateAgentProxyResult, void, void>('agent/updateproxy');
}
export namespace DeleteAgentProxyRequest {
export const type = new RequestType<DeleteAgentProxyParams, sqlops.ResultStatus, void, void>('agent/deleteproxy');
}
// Job Schedules requests
export namespace AgentJobSchedulesRequest {
export const type = new RequestType<AgentJobScheduleParams, sqlops.AgentJobSchedulesResult, void, void>('agent/schedules');
}
export namespace CreateAgentJobScheduleRequest {
export const type = new RequestType<CreateAgentJobScheduleParams, sqlops.CreateAgentJobScheduleResult, void, void>('agent/createschedule');
}
export namespace UpdateAgentJobScheduleRequest {
export const type = new RequestType<UpdateAgentJobScheduleParams, sqlops.UpdateAgentJobScheduleResult, void, void>('agent/updateschedule');
}
export namespace DeleteAgentJobScheduleRequest {
export const type = new RequestType<DeleteAgentJobScheduleParams, sqlops.ResultStatus, void, void>('agent/deleteschedule');
}
// ------------------------------- < Agent Management > ------------------------------------

View File

@@ -6,13 +6,12 @@
import { SqlOpsDataClient, SqlOpsFeature } from 'dataprotocol-client';
import { ClientCapabilities, StaticFeature, RPCMessageType, ServerCapabilities } from 'vscode-languageclient';
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
import { Disposable } from 'vscode';
import * as sqlops from 'sqlops';
import { Telemetry } from './telemetry';
import * as contracts from './contracts';
import * as sqlops from 'sqlops';
import * as Utils from './utils';
import { TelemetryNotification, AgentJobsRequest, AgentJobActionRequest, AgentJobHistoryRequest, AgentJobsParams, AgentJobHistoryParams, AgentJobActionParams } from './contracts';
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
export class TelemetryFeature implements StaticFeature {
@@ -23,7 +22,7 @@ export class TelemetryFeature implements StaticFeature {
}
initialize(): void {
this._client.onNotification(TelemetryNotification.type, e => {
this._client.onNotification(contracts.TelemetryNotification.type, e => {
Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures);
});
}
@@ -31,11 +30,13 @@ export class TelemetryFeature implements StaticFeature {
export class AgentServicesFeature extends SqlOpsFeature<undefined> {
private static readonly messagesTypes: RPCMessageType[] = [
AgentJobsRequest.type,
AgentJobHistoryRequest.type,
AgentJobActionRequest.type
contracts.AgentJobsRequest.type,
contracts.AgentJobHistoryRequest.type,
contracts.AgentJobActionRequest.type
];
private onUpdatedHandler: () => any;
constructor(client: SqlOpsDataClient) {
super(client, AgentServicesFeature.messagesTypes);
}
@@ -54,36 +55,454 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
protected registerProvider(options: undefined): Disposable {
const client = this._client;
let self = this;
// On updated registration
let registerOnUpdated = (handler: () => any): void => {
self.onUpdatedHandler = handler;
};
let fireOnUpdated = (): void => {
if (self.onUpdatedHandler) {
self.onUpdatedHandler();
}
};
// Job management methods
let getJobs = (ownerUri: string): Thenable<sqlops.AgentJobsResult> => {
let params: AgentJobsParams = { ownerUri: ownerUri, jobId: null };
return client.sendRequest(AgentJobsRequest.type, params).then(
let params: contracts.AgentJobsParams = { ownerUri: ownerUri, jobId: null };
return client.sendRequest(contracts.AgentJobsRequest.type, params).then(
r => r,
e => {
client.logFailedRequest(AgentJobsRequest.type, e);
client.logFailedRequest(contracts.AgentJobsRequest.type, e);
return Promise.resolve(undefined);
}
);
};
let getJobHistory = (connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> => {
let params: AgentJobHistoryParams = { ownerUri: connectionUri, jobId: jobID };
let getJobHistory = (ownerUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> => {
let params: contracts.AgentJobHistoryParams = { ownerUri: ownerUri, jobId: jobID };
return client.sendRequest(AgentJobHistoryRequest.type, params).then(
return client.sendRequest(contracts.AgentJobHistoryRequest.type, params).then(
r => r,
e => {
client.logFailedRequest(AgentJobHistoryRequest.type, e);
client.logFailedRequest(contracts.AgentJobHistoryRequest.type, e);
return Promise.resolve(undefined);
}
);
};
let jobAction = (connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> => {
let params: AgentJobActionParams = { ownerUri: connectionUri, jobName: jobName, action: action };
return client.sendRequest(AgentJobActionRequest.type, params).then(
let jobAction = (ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> => {
let params: contracts.AgentJobActionParams = { ownerUri: ownerUri, jobName: jobName, action: action };
return client.sendRequest(contracts.AgentJobActionRequest.type, params).then(
r => r,
e => {
client.logFailedRequest(AgentJobActionRequest.type, e);
client.logFailedRequest(contracts.AgentJobActionRequest.type, e);
return Promise.resolve(undefined);
}
);
};
let createJob = (ownerUri: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.CreateAgentJobResult> => {
let params: contracts.CreateAgentJobParams = {
ownerUri: ownerUri,
job: jobInfo
};
let requestType = contracts.CreateAgentJobRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let updateJob = (ownerUri: string, originalJobName: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.UpdateAgentJobResult> => {
let params: contracts.UpdateAgentJobParams = {
ownerUri: ownerUri,
originalJobName: originalJobName,
job: jobInfo
};
let requestType = contracts.UpdateAgentJobRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let deleteJob = (ownerUri: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.ResultStatus> => {
let params: contracts.DeleteAgentJobParams = {
ownerUri: ownerUri,
job: jobInfo
};
let requestType = contracts.DeleteAgentJobRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let getJobDefaults = (ownerUri: string): Thenable<sqlops.AgentJobDefaultsResult> => {
let params: contracts.AgentJobDefaultsParams = {
ownerUri: ownerUri
};
let requestType = contracts.AgentJobDefaultsRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
// Job Step management methods
let createJobStep = (ownerUri: string, stepInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.CreateAgentJobStepResult> => {
let params: contracts.CreateAgentJobStepParams = {
ownerUri: ownerUri,
step: stepInfo
};
let requestType = contracts.CreateAgentJobStepRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let updateJobStep = (ownerUri: string, originalJobStepName: string, stepInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.UpdateAgentJobStepResult> => {
let params: contracts.UpdateAgentJobStepParams = {
ownerUri: ownerUri,
originalJobStepName: originalJobStepName,
step: stepInfo
};
let requestType = contracts.UpdateAgentJobStepRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let deleteJobStep = (ownerUri: string, stepInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.ResultStatus> => {
let params: contracts.DeleteAgentJobStepParams = {
ownerUri: ownerUri,
step: stepInfo
};
let requestType = contracts.DeleteAgentJobStepRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
// Alert management methods
let getAlerts = (ownerUri: string): Thenable<sqlops.AgentAlertsResult> => {
let params: contracts.AgentAlertsParams = {
ownerUri: ownerUri
};
let requestType = contracts.AgentAlertsRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let createAlert = (ownerUri: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.CreateAgentAlertResult> => {
let params: contracts.CreateAgentAlertParams = {
ownerUri: ownerUri,
alert: alertInfo
};
let requestType = contracts.CreateAgentAlertRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let updateAlert = (ownerUri: string, originalAlertName: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.UpdateAgentAlertResult> => {
let params: contracts.UpdateAgentAlertParams = {
ownerUri: ownerUri,
originalAlertName: originalAlertName,
alert: alertInfo
};
let requestType = contracts.UpdateAgentAlertRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let deleteAlert = (ownerUri: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.ResultStatus> => {
let params: contracts.DeleteAgentAlertParams = {
ownerUri: ownerUri,
alert: alertInfo
};
let requestType = contracts.DeleteAgentAlertRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
// Operator management methods
let getOperators = (ownerUri: string): Thenable<sqlops.AgentOperatorsResult> => {
let params: contracts.AgentOperatorsParams = {
ownerUri: ownerUri
};
let requestType = contracts.AgentOperatorsRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let createOperator = (ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.CreateAgentOperatorResult> => {
let params: contracts.CreateAgentOperatorParams = {
ownerUri: ownerUri,
operator: operatorInfo
};
let requestType = contracts.CreateAgentOperatorRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let updateOperator = (ownerUri: string, originalOperatorName: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.UpdateAgentOperatorResult> => {
let params: contracts.UpdateAgentOperatorParams = {
ownerUri: ownerUri,
originalOperatorName: originalOperatorName,
operator: operatorInfo
};
let requestType = contracts.UpdateAgentOperatorRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let deleteOperator = (ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.ResultStatus> => {
let params: contracts.DeleteAgentOperatorParams = {
ownerUri: ownerUri,
operator: operatorInfo
};
let requestType = contracts.DeleteAgentOperatorRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
// Proxy management methods
let getProxies = (ownerUri: string): Thenable<sqlops.AgentProxiesResult> => {
let params: contracts.AgentProxiesParams = {
ownerUri: ownerUri
};
let requestType = contracts.AgentProxiesRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let createProxy = (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.CreateAgentOperatorResult> => {
let params: contracts.CreateAgentProxyParams = {
ownerUri: ownerUri,
proxy: proxyInfo
};
let requestType = contracts.CreateAgentProxyRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let updateProxy = (ownerUri: string, originalProxyName: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.UpdateAgentOperatorResult> => {
let params: contracts.UpdateAgentProxyParams = {
ownerUri: ownerUri,
originalProxyName: originalProxyName,
proxy: proxyInfo
};
let requestType = contracts.UpdateAgentProxyRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let deleteProxy = (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.ResultStatus> => {
let params: contracts.DeleteAgentProxyParams = {
ownerUri: ownerUri,
proxy: proxyInfo
};
let requestType = contracts.DeleteAgentProxyRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
// Job Schedule management methods
let getJobSchedules = (ownerUri: string): Thenable<sqlops.AgentJobSchedulesResult> => {
let params: contracts.AgentJobScheduleParams = {
ownerUri: ownerUri
};
let requestType = contracts.AgentJobSchedulesRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let createJobSchedule = (ownerUri: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.CreateAgentJobScheduleResult> => {
let params: contracts.CreateAgentJobScheduleParams = {
ownerUri: ownerUri,
schedule: scheduleInfo
};
let requestType = contracts.CreateAgentJobScheduleRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let updateJobSchedule = (ownerUri: string, originalScheduleName: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.UpdateAgentJobScheduleResult> => {
let params: contracts.UpdateAgentJobScheduleParams = {
ownerUri: ownerUri,
originalScheduleName: originalScheduleName,
schedule: scheduleInfo
};
let requestType = contracts.UpdateAgentJobScheduleRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
let deleteJobSchedule = (ownerUri: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.ResultStatus> => {
let params: contracts.DeleteAgentJobScheduleParams = {
ownerUri: ownerUri,
schedule: scheduleInfo
};
let requestType = contracts.DeleteAgentJobScheduleRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
@@ -93,7 +512,31 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
providerId: client.providerId,
getJobs,
getJobHistory,
jobAction
jobAction,
createJob,
updateJob,
deleteJob,
getJobDefaults,
createJobStep,
updateJobStep,
deleteJobStep,
getAlerts,
createAlert,
updateAlert,
deleteAlert,
getOperators,
createOperator,
updateOperator,
deleteOperator,
getProxies,
createProxy,
updateProxy,
deleteProxy,
getJobSchedules,
createJobSchedule,
updateJobSchedule,
deleteJobSchedule,
registerOnUpdated
});
}
}

View File

@@ -1,396 +1,396 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
agent-base@4, agent-base@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce"
dependencies:
es6-promisify "^5.0.0"
applicationinsights@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.1.tgz#53446b830fe8d5d619eee2a278b31d3d25030927"
dependencies:
diagnostic-channel "0.2.0"
diagnostic-channel-publishers "0.2.1"
zone.js "0.7.6"
base64-js@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978"
bl@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
dependencies:
readable-stream "^2.3.5"
safe-buffer "^5.1.1"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
buffer@^3.0.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb"
dependencies:
base64-js "0.0.8"
ieee754 "^1.1.4"
isarray "^1.0.0"
commander@~2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
dependencies:
graceful-readlink ">= 1.0.0"
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.1.7":
version "0.1.7"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/d50285b03d0d5073c086362c5c96afb279320607"
dependencies:
vscode-languageclient "3.5.0"
debug@3.1.0, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
dependencies:
file-type "^5.2.0"
is-stream "^1.1.0"
tar-stream "^1.5.2"
decompress-tarbz2@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
dependencies:
decompress-tar "^4.1.0"
file-type "^6.1.0"
is-stream "^1.1.0"
seek-bzip "^1.0.5"
unbzip2-stream "^1.0.9"
decompress-targz@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
dependencies:
decompress-tar "^4.1.1"
file-type "^5.2.0"
is-stream "^1.1.0"
decompress-unzip@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
dependencies:
file-type "^3.8.0"
get-stream "^2.2.0"
pify "^2.3.0"
yauzl "^2.4.2"
decompress@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d"
dependencies:
decompress-tar "^4.0.0"
decompress-tarbz2 "^4.0.0"
decompress-targz "^4.0.0"
decompress-unzip "^4.0.1"
graceful-fs "^4.1.10"
make-dir "^1.0.0"
pify "^2.3.0"
strip-dirs "^2.0.0"
diagnostic-channel-publishers@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
diagnostic-channel@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17"
dependencies:
semver "^5.3.0"
end-of-stream@^1.0.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
dependencies:
once "^1.4.0"
es6-promise@^4.0.3:
version "4.2.4"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
dependencies:
es6-promise "^4.0.3"
eventemitter2@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452"
fd-slicer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
dependencies:
pend "~1.2.0"
file-type@^3.8.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
file-type@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
file-type@^6.1.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
get-stream@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
dependencies:
object-assign "^4.0.1"
pinkie-promise "^2.0.0"
graceful-fs@^4.1.10:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
"graceful-readlink@>= 1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
http-proxy-agent@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies:
agent-base "4"
debug "3.1.0"
https-proxy-agent@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.0.tgz#7fbba856be8cd677986f42ebd3664f6317257887"
dependencies:
agent-base "^4.1.0"
debug "^3.1.0"
ieee754@^1.1.4:
version "1.1.11"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
is-natural-number@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
make-dir@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b"
dependencies:
pify "^3.0.0"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
minimist "0.0.8"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
dependencies:
wrappy "1"
opener@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
pinkie-promise@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
dependencies:
pinkie "^2.0.0"
pinkie@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
readable-stream@^2.0.0, readable-stream@^2.3.5:
version "2.3.5"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.0.3"
util-deprecate "~1.0.1"
safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
seek-bzip@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
dependencies:
commander "~2.8.1"
semver@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
"service-downloader@github:anthonydresser/service-downloader#0.1.2":
version "0.1.2"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/2aa9b336b6442e17e24693ddc907030575539798"
dependencies:
decompress "^4.2.0"
eventemitter2 "^5.0.1"
http-proxy-agent "^2.0.0"
https-proxy-agent "^2.1.1"
mkdirp "^0.5.1"
tmp "^0.0.33"
string_decoder@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
dependencies:
safe-buffer "~5.1.0"
strip-dirs@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
dependencies:
is-natural-number "^4.0.1"
tar-stream@^1.5.2:
version "1.5.5"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.5.tgz#5cad84779f45c83b1f2508d96b09d88c7218af55"
dependencies:
bl "^1.0.0"
end-of-stream "^1.0.0"
readable-stream "^2.0.0"
xtend "^4.0.0"
through@^2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
dependencies:
os-tmpdir "~1.0.2"
unbzip2-stream@^1.0.9:
version "1.2.5"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz#73a033a567bbbde59654b193c44d48a7e4f43c47"
dependencies:
buffer "^3.0.1"
through "^2.3.6"
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
vscode-extension-telemetry@^0.0.15:
version "0.0.15"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.15.tgz#685c32f3b67e8fb85ba689c1d7f88ff90ff87856"
dependencies:
applicationinsights "1.0.1"
vscode-jsonrpc@3.5.0, vscode-jsonrpc@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
vscode-languageclient@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-3.5.0.tgz#36d02cc186a8365a4467719a290fb200a9ae490a"
dependencies:
vscode-languageserver-protocol "^3.5.0"
vscode-languageserver-protocol@3.5.0, vscode-languageserver-protocol@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0.tgz#067c5cbe27709795398d119692c97ebba1452209"
dependencies:
vscode-jsonrpc "^3.5.0"
vscode-languageserver-types "^3.5.0"
vscode-languageserver-types@3.5.0, vscode-languageserver-types@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
xtend@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
yauzl@^2.4.2:
version "2.9.1"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f"
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.0.1"
zone.js@0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
agent-base@4, agent-base@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce"
dependencies:
es6-promisify "^5.0.0"
applicationinsights@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.1.tgz#53446b830fe8d5d619eee2a278b31d3d25030927"
dependencies:
diagnostic-channel "0.2.0"
diagnostic-channel-publishers "0.2.1"
zone.js "0.7.6"
base64-js@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978"
bl@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
dependencies:
readable-stream "^2.3.5"
safe-buffer "^5.1.1"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
buffer@^3.0.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb"
dependencies:
base64-js "0.0.8"
ieee754 "^1.1.4"
isarray "^1.0.0"
commander@~2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
dependencies:
graceful-readlink ">= 1.0.0"
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.1.9":
version "0.1.9"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/a1a79895cb79658b75d78aa5cfd745855019148d"
dependencies:
vscode-languageclient "3.5.0"
debug@3.1.0, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
dependencies:
file-type "^5.2.0"
is-stream "^1.1.0"
tar-stream "^1.5.2"
decompress-tarbz2@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
dependencies:
decompress-tar "^4.1.0"
file-type "^6.1.0"
is-stream "^1.1.0"
seek-bzip "^1.0.5"
unbzip2-stream "^1.0.9"
decompress-targz@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
dependencies:
decompress-tar "^4.1.1"
file-type "^5.2.0"
is-stream "^1.1.0"
decompress-unzip@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
dependencies:
file-type "^3.8.0"
get-stream "^2.2.0"
pify "^2.3.0"
yauzl "^2.4.2"
decompress@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d"
dependencies:
decompress-tar "^4.0.0"
decompress-tarbz2 "^4.0.0"
decompress-targz "^4.0.0"
decompress-unzip "^4.0.1"
graceful-fs "^4.1.10"
make-dir "^1.0.0"
pify "^2.3.0"
strip-dirs "^2.0.0"
diagnostic-channel-publishers@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
diagnostic-channel@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17"
dependencies:
semver "^5.3.0"
end-of-stream@^1.0.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
dependencies:
once "^1.4.0"
es6-promise@^4.0.3:
version "4.2.4"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
dependencies:
es6-promise "^4.0.3"
eventemitter2@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452"
fd-slicer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
dependencies:
pend "~1.2.0"
file-type@^3.8.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
file-type@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
file-type@^6.1.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
get-stream@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
dependencies:
object-assign "^4.0.1"
pinkie-promise "^2.0.0"
graceful-fs@^4.1.10:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
"graceful-readlink@>= 1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
http-proxy-agent@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies:
agent-base "4"
debug "3.1.0"
https-proxy-agent@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.0.tgz#7fbba856be8cd677986f42ebd3664f6317257887"
dependencies:
agent-base "^4.1.0"
debug "^3.1.0"
ieee754@^1.1.4:
version "1.1.11"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
is-natural-number@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
make-dir@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b"
dependencies:
pify "^3.0.0"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
minimist "0.0.8"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
dependencies:
wrappy "1"
opener@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
pinkie-promise@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
dependencies:
pinkie "^2.0.0"
pinkie@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
readable-stream@^2.0.0, readable-stream@^2.3.5:
version "2.3.5"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.0.3"
util-deprecate "~1.0.1"
safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
seek-bzip@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
dependencies:
commander "~2.8.1"
semver@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
"service-downloader@github:anthonydresser/service-downloader#0.1.2":
version "0.1.2"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/2aa9b336b6442e17e24693ddc907030575539798"
dependencies:
decompress "^4.2.0"
eventemitter2 "^5.0.1"
http-proxy-agent "^2.0.0"
https-proxy-agent "^2.1.1"
mkdirp "^0.5.1"
tmp "^0.0.33"
string_decoder@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
dependencies:
safe-buffer "~5.1.0"
strip-dirs@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
dependencies:
is-natural-number "^4.0.1"
tar-stream@^1.5.2:
version "1.5.5"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.5.tgz#5cad84779f45c83b1f2508d96b09d88c7218af55"
dependencies:
bl "^1.0.0"
end-of-stream "^1.0.0"
readable-stream "^2.0.0"
xtend "^4.0.0"
through@^2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
dependencies:
os-tmpdir "~1.0.2"
unbzip2-stream@^1.0.9:
version "1.2.5"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz#73a033a567bbbde59654b193c44d48a7e4f43c47"
dependencies:
buffer "^3.0.1"
through "^2.3.6"
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
vscode-extension-telemetry@^0.0.15:
version "0.0.15"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.15.tgz#685c32f3b67e8fb85ba689c1d7f88ff90ff87856"
dependencies:
applicationinsights "1.0.1"
vscode-jsonrpc@3.5.0, vscode-jsonrpc@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
vscode-languageclient@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-3.5.0.tgz#36d02cc186a8365a4467719a290fb200a9ae490a"
dependencies:
vscode-languageserver-protocol "^3.5.0"
vscode-languageserver-protocol@3.5.0, vscode-languageserver-protocol@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0.tgz#067c5cbe27709795398d119692c97ebba1452209"
dependencies:
vscode-jsonrpc "^3.5.0"
vscode-languageserver-types "^3.5.0"
vscode-languageserver-types@3.5.0, vscode-languageserver-types@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
xtend@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
yauzl@^2.4.2:
version "2.9.1"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f"
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.0.1"
zone.js@0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"

View File

@@ -10,6 +10,13 @@ Common SQL Profiler use-cases taken from https://docs.microsoft.com/en-us/sql/to
- Monitoring the performance of SQL Server to tune workloads.
- Correlating performance counters to diagnose problems.
## SQL Server Profiler 0.1.1 Release
The SQL Server Profiler for SQL Operations Studio *Preview* extension is now available. This is the initial preview release for a new lightweight XEvent-based profiler. The SQL Server Profiler extension tries to make it simple to quickly trace server activity for troubleshooting and monitoring.
We'll continue to enhance this extension over the next couple releases. Take a look at the below screenshot to see what's currently available.
<img width="850" src="https://user-images.githubusercontent.com/599935/41578613-fa10e8bc-7347-11e8-8b97-9fb7d186c9f6.png">
## Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

View File

@@ -2,7 +2,7 @@
"name": "profiler",
"displayName": "SQL Server Profiler",
"description": "SQL Server Profiler for SQL Operations Studio",
"version": "0.30.0",
"version": "0.1.2",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
@@ -32,6 +32,16 @@
"command": "profiler.newProfiler",
"title": "New Profiler",
"category": "Profiler"
},
{
"command": "profiler.start",
"title": "Start",
"category": "Profiler"
},
{
"command": "profiler.stop",
"title": "Stop",
"category": "Profiler"
}
],
"outputChannels": [

View File

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.30.4",
"version": "0.31.2",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"
@@ -60,7 +60,7 @@
"reflect-metadata": "^0.1.8",
"rxjs": "5.4.0",
"semver": "4.3.6",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.17",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.22",
"spdlog": "0.6.0",
"sudo-prompt": "^8.0.0",
"svg.js": "^2.2.5",

View File

@@ -27,14 +27,16 @@
"reportIssueUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=customer%20reported%20issue",
"requestFeatureUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=feature-request"
},
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
"gettingStartedUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=875578",
"documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277",
"commit": "9ca6200018fc206d67a47229f991901a8a453781",
"date": "2017-12-15T12:00:00.000Z",
"recommendedExtensions": [
"Microsoft.agent",
"Microsoft.whoisactive",
"Microsoft.profiler",
"Microsoft.server-report",
"Microsoft.whoisactive",
"Redgate.sql-search"
],
"extensionsGallery": {

View File

@@ -9,7 +9,7 @@ declare @dbsize table
Free_Space_MB decimal(20,2) default (0))
insert into @dbsize
(Dbname,file_Size_MB,Space_Used_MB,Free_Space_MB)
exec sp_msforeachdb
exec sp_MSforeachdb
'use [?];
select DB_NAME() AS DbName,
sum(size)/128.0 AS File_Size_MB,
@@ -24,7 +24,7 @@ declare @logsize table
log_Free_Space_MB decimal(20,2)default (0))
insert into @logsize
(Dbname,Log_File_Size_MB,log_Space_Used_MB,log_Free_Space_MB)
exec sp_msforeachdb
exec sp_MSforeachdb
'use [?];
select DB_NAME() AS DbName,
sum(size)/128.0 AS Log_File_Size_MB,
@@ -38,7 +38,7 @@ declare @dbfreesize table
Freespace varchar(50)default (0.00))
insert into @dbfreesize
(name,database_size,Freespace)
exec sp_msforeachdb
exec sp_MSforeachdb
'use [?];SELECT database_name = db_name()
,database_size = ltrim(str((convert(DECIMAL(15, 2), dbsize) + convert(DECIMAL(15, 2), logsize)) * 8192 / 1048576, 15, 2) + ''MB'')
,''unallocated space'' = ltrim(str((

View File

@@ -19,7 +19,7 @@
"commands": [
{
"command": "sqlservices.openDialog",
"title": "openDialog"
"title": "sqlservices.openDialog"
},
{
"command": "sqlservices.openEditor",
@@ -32,6 +32,10 @@
{
"command": "sqlservices.openEditorWithWebView2",
"title": "sqlservices.openEditorWithWebView2"
},
{
"command": "sqlservices.openWizard",
"title": "sqlservices.openWizard"
}
],
"dashboard.tabs": [

View File

@@ -60,9 +60,212 @@ export default class MainController implements vscode.Disposable {
this.openEditorWithWebview2();
});
vscode.commands.registerCommand('sqlservices.openWizard', () => {
this.openWizard();
});
return Promise.resolve(true);
}
private async getTabContent(view: sqlops.ModelView, customButton1: sqlops.window.modelviewdialog.Button, customButton2: sqlops.window.modelviewdialog.Button, componentWidth: number | string): Promise<void> {
let inputBox = view.modelBuilder.inputBox()
.withProperties({
multiline: true,
height: 100
}).component();
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
inputBoxWrapper.loading = false;
customButton1.onClick(() => {
inputBoxWrapper.loading = true;
setTimeout(() => inputBoxWrapper.loading = false, 5000);
});
let inputBox2 = view.modelBuilder.inputBox().component();
let backupFilesInputBox = view.modelBuilder.inputBox().component();
let checkbox = view.modelBuilder.checkBox()
.withProperties({
label: 'Copy-only backup'
})
.component();
checkbox.onChanged(e => {
console.info("inputBox.enabled " + inputBox.enabled);
inputBox.enabled = !inputBox.enabled;
});
let button = view.modelBuilder.button()
.withProperties({
label: '+'
}).component();
let button3 = view.modelBuilder.button()
.withProperties({
label: '-'
}).component();
let button2 = view.modelBuilder.button()
.component();
button.onDidClick(e => {
backupFilesInputBox.value = 'Button clicked';
});
let dropdown = view.modelBuilder.dropDown()
.withProperties({
value: 'Full',
values: ['Full', 'Differential', 'Transaction Log']
})
.component();
let f = 0;
inputBox.onTextChanged((params) => {
vscode.window.showInformationMessage(inputBox.value);
f = f + 1;
inputBox2.value = f.toString();
});
dropdown.onValueChanged((params) => {
vscode.window.showInformationMessage(inputBox2.value);
inputBox.value = dropdown.value.toString();
});
let radioButton = view.modelBuilder.radioButton()
.withProperties({
value: 'option1',
name: 'radioButtonOptions',
label: 'Option 1',
checked: true
//width: 300
}).component();
let radioButton2 = view.modelBuilder.radioButton()
.withProperties({
value: 'option2',
name: 'radioButtonOptions',
label: 'Option 2'
}).component();
let inputBox3 = view.modelBuilder.inputBox().component();
let inputBox4 = view.modelBuilder.inputBox().component();
let form2Model = view.modelBuilder.formContainer()
.withFormItems([{
component: inputBox3,
title: 'inputBox3'
}, {
component: inputBox4,
title: 'inputBox4'
}], {
horizontal: true
}).component();
let groupModel1 = view.modelBuilder.groupContainer()
.withLayout({
}).withItems([
form2Model
]).component();
radioButton.onDidClick(() => {
inputBox.value = radioButton.value;
groupModel1.enabled = true;
});
radioButton2.onDidClick(() => {
inputBox.value = radioButton.value;
groupModel1.enabled = false;
});
let table = view.modelBuilder.table().withProperties({
data: [
['1', '2', '2'],
['4', '5', '6'],
['7', '8', '9']
], columns: ['c1', 'c2', 'c3'],
height: 250,
selectedRows: [0]
}).component();
table.onRowSelected(e => {
// TODO:
});
let listBox = view.modelBuilder.listBox().withProperties({
values: ['1', '2', '3'],
selectedRow: 2
}).component();
let declarativeTable = view.modelBuilder.declarativeTable()
.withProperties({
columns: [{
displayName: 'Column 1',
valueType: sqlops.DeclarativeDataType.string,
width: '20px',
isReadOnly: true
}, {
displayName: 'Column 2',
valueType: sqlops.DeclarativeDataType.string,
width: '100px',
isReadOnly: false
}, {
displayName: 'Column 3',
valueType: sqlops.DeclarativeDataType.boolean,
width: '20px',
isReadOnly: false
}, {
displayName: 'Column 4',
valueType: sqlops.DeclarativeDataType.category,
isReadOnly: false,
width: '120px',
categoryValues: [
{ name: 'options1', displayName: 'option 1' },
{ name: 'options2', displayName: 'option 2' }
]
}
],
data: [
['Data00', 'Data01', false, 'options2'],
['Data10', 'Data11', true, 'options1']
]
}).component();
declarativeTable.onDataChanged(e => {
inputBox2.value = e.row.toString() + ' ' + e.column.toString() + ' ' + e.value.toString();
inputBox3.value = declarativeTable.data[e.row][e.column];
});
let flexRadioButtonsModel = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'column',
alignItems: 'left',
height: 150
}).withItems([
radioButton, groupModel1, radioButton2]
, { flex: '1 1 50%' }).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: inputBoxWrapper,
title: 'Backup name'
}, {
component: inputBox2,
title: 'Recovery model'
}, {
component: dropdown,
title: 'Backup type'
}, {
component: checkbox,
title: ''
}, {
component: backupFilesInputBox,
title: 'Backup files',
actions: [button, button3]
}, {
component: flexRadioButtonsModel,
title: 'Options'
}, {
component: declarativeTable,
title: 'Declarative Table'
}, {
component: table,
title: 'Table'
}, {
component: listBox,
title: 'List Box'
}], {
horizontal: false,
componentWidth: componentWidth
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
customButton2.onClick(() => {
formWrapper.loading = true;
setTimeout(() => formWrapper.loading = false, 5000);
});
await view.initializeModel(formWrapper);
}
private openDialog(): void {
let dialog = sqlops.window.modelviewdialog.createDialog('Test dialog');
let tab1 = sqlops.window.modelviewdialog.createTab('Test tab 1');
@@ -80,142 +283,29 @@ export default class MainController implements vscode.Disposable {
customButton2.onClick(() => console.log('button 2 clicked!'));
dialog.customButtons = [customButton1, customButton2];
tab1.registerContent(async (view) => {
let inputBox = view.modelBuilder.inputBox()
.withProperties({
//width: 300
}).component();
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
inputBoxWrapper.loading = false;
customButton1.onClick(() => {
inputBoxWrapper.loading = true;
setTimeout(() => inputBoxWrapper.loading = false, 5000);
});
let inputBox2 = view.modelBuilder.inputBox().component();
let backupFilesInputBox = view.modelBuilder.inputBox().component();
let checkbox = view.modelBuilder.checkBox()
.withProperties({
label: 'Copy-only backup'
})
.component();
checkbox.onChanged(e => {
console.info("inputBox.enabled " + inputBox.enabled);
inputBox.enabled = !inputBox.enabled;
});
let button = view.modelBuilder.button()
.withProperties({
label: '+'
}).component();
let button3 = view.modelBuilder.button()
.withProperties({
label: '-'
}).component();
let button2 = view.modelBuilder.button()
.component();
button.onDidClick(e => {
backupFilesInputBox.value = 'Button clicked';
});
let dropdown = view.modelBuilder.dropDown()
.withProperties({
value: 'Full',
values: ['Full', 'Differential', 'Transaction Log']
})
.component();
let f = 0;
inputBox.onTextChanged((params) => {
vscode.window.showInformationMessage(inputBox.value);
f = f + 1;
inputBox2.value = f.toString();
});
dropdown.onValueChanged((params) => {
vscode.window.showInformationMessage(inputBox2.value);
inputBox.value = dropdown.value;
});
let radioButton = view.modelBuilder.radioButton()
.withProperties({
value: 'option1',
name: 'radioButtonOptions',
label: 'Option 1',
checked: true
//width: 300
}).component();
let radioButton2 = view.modelBuilder.radioButton()
.withProperties({
value: 'option2',
name: 'radioButtonOptions',
label: 'Option 2'
}).component();
let inputBox3 = view.modelBuilder.inputBox().component();
let inputBox4 = view.modelBuilder.inputBox().component();
let form2Model = view.modelBuilder.formContainer()
.withFormItems([{
component: inputBox3,
title: 'inputBox3'
}, {
component: inputBox4,
title: 'inputBox4'
}], {
horizontal: true
}).component();
let groupModel1 = view.modelBuilder.groupContainer()
.withLayout({
}).withItems([
form2Model
]).component();
radioButton.onDidClick(() => {
inputBox.value = radioButton.value;
groupModel1.enabled = true;
});
radioButton2.onDidClick(() => {
inputBox.value = radioButton.value;
groupModel1.enabled = false;
});
let flexRadioButtonsModel = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'column',
alignItems: 'left',
height: 50
}).withItems([
radioButton, groupModel1, radioButton2]
, { flex: '1 1 50%' }).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: inputBoxWrapper,
title: 'Backup name'
}, {
component: inputBox2,
title: 'Recovery model'
}, {
component: dropdown,
title: 'Backup type'
}, {
component: checkbox,
title: ''
}, {
component: backupFilesInputBox,
title: 'Backup files',
actions: [button, button3]
}, {
component: flexRadioButtonsModel,
title: 'Options'
}], {
horizontal: false,
componentWidth: 400
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
customButton2.onClick(() => {
formWrapper.loading = true;
setTimeout(() => formWrapper.loading = false, 5000);
});
await view.initializeModel(formWrapper);
await this.getTabContent(view, customButton1, customButton2, 400);
});
sqlops.window.modelviewdialog.openDialog(dialog);
}
private openWizard(): void {
let wizard = sqlops.window.modelviewdialog.createWizard('Test wizard');
let page1 = sqlops.window.modelviewdialog.createWizardPage('First wizard page');
let page2 = sqlops.window.modelviewdialog.createWizardPage('Second wizard page');
page2.content = 'sqlservices';
let customButton1 = sqlops.window.modelviewdialog.createButton('Load name');
customButton1.onClick(() => console.log('button 1 clicked!'));
let customButton2 = sqlops.window.modelviewdialog.createButton('Load all');
customButton2.onClick(() => console.log('button 2 clicked!'));
wizard.customButtons = [customButton1, customButton2];
page1.registerContent(async (view) => {
await this.getTabContent(view, customButton1, customButton2, 800);
});
wizard.pages = [page1, page2];
wizard.open();
}
private openEditor(): void {
let editor = sqlops.workspace.createModelViewEditor('Test Model View');
editor.registerContent(async view => {
@@ -322,7 +412,7 @@ export default class MainController implements vscode.Disposable {
height: '100%'
});
let templateValues = {url: 'http://whoisactive.com/docs/'};
let templateValues = { url: 'http://whoisactive.com/docs/' };
Utils.renderTemplateHtml(path.join(__dirname, '..'), 'templateTab.html', templateValues)
.then(html => {
webview.html = html;

View File

@@ -20,9 +20,9 @@ import { IDisposable } from 'vs/base/common/lifecycle';
<ng-template ngFor let-item let-first="first" let-last="last" [ngForOf]="menuItems">
<span style="padding: 5px; display: flex; align-items: center">
<span *ngIf="item.icon" class="icon" style="display: inline-block; margin-right: 5px" [ngClass]="item.icon"></span>
<span *ngIf="first" style="font-weight: 200">{{item.label}}</span>
<span *ngIf="first">{{item.label}}</span>
<span *ngIf="last" style="">{{item.label}}</span>
<a class="router-link" *ngIf="!last && !first" (click)="route(item.routerLink)" style=" font-weight: 200" >{{item.label}}</a>
<a class="router-link" *ngIf="!last && !first" (click)="route(item.routerLink)" >{{item.label}}</a>
</span>
<span *ngIf="!last" class="icon chevron-right"></span>
</ng-template>

View File

@@ -33,4 +33,12 @@ export class Button extends vsButton {
public set title(value: string) {
this.$el.title(value);
}
public setHeight(value: string) {
this.$el.style('height', value);
}
public setWidth(value: string) {
this.$el.style('width', value);
}
}

View File

@@ -11,7 +11,7 @@ export class ToggleDropdownAction extends Action {
private static readonly ID = 'dropdownAction.toggle';
private static readonly ICON = 'dropdown-arrow';
constructor(label, private _fn: () => any) {
constructor(private _fn: () => any, label: string) {
super(ToggleDropdownAction.ID, label, ToggleDropdownAction.ICON);
}

View File

@@ -53,6 +53,10 @@ export interface IDropdownOptions extends IDropdownStyles {
* Value to use as aria-label for the input box
*/
ariaLabel?: string;
/**
* Label for the dropdown action
*/
actionLabel: string;
}
export interface IDropdownStyles {
@@ -66,7 +70,8 @@ const defaults: IDropdownOptions = {
strictSelection: true,
maxHeight: 300,
errorMessage: errorMessage,
contextBorder: Color.fromHex('#696969')
contextBorder: Color.fromHex('#696969'),
actionLabel: nls.localize('dropdownAction.toggle', "Toggle dropdown")
};
interface ListResource {
@@ -115,11 +120,11 @@ export class Dropdown extends Disposable {
this.$input = $('.dropdown-input').style('width', '100%').appendTo(this.$el);
this.$treeContainer = $('.dropdown-tree');
this._toggleAction = new ToggleDropdownAction(nls.localize('dropdown.toggle', '{0} Toggle Dropdown', this._options.ariaLabel), () => {
this._toggleAction = new ToggleDropdownAction(() => {
this._showList();
this._tree.domFocus();
this._tree.focusFirst();
});
}, opt.actionLabel);
this._input = new InputBox(this.$input.getHTMLElement(), contextViewService, {
validationOptions: {
@@ -132,7 +137,7 @@ export class Dropdown extends Disposable {
ariaLabel: this._options.ariaLabel
});
this._register(DOM.addDisposableListener(this._input.inputElement, DOM.EventType.FOCUS, () => {
this._register(DOM.addDisposableListener(this._input.inputElement, DOM.EventType.CLICK, () => {
this._showList();
}));
@@ -145,8 +150,12 @@ export class Dropdown extends Disposable {
this._register(DOM.addStandardDisposableListener(this._input.inputElement, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => {
switch (e.keyCode) {
case KeyCode.Enter:
if (this._input.validate()) {
this._onValueChange.fire(this._input.value);
if (this._contextView.isVisible()) {
if (this._input.validate()) {
this._onValueChange.fire(this._input.value);
}
} else {
this._showList();
}
e.stopPropagation();
break;
@@ -192,6 +201,11 @@ export class Dropdown extends Disposable {
this._contextView.hide();
});
this._controller.onDropdownEscape(() => {
this._input.focus();
this._contextView.hide();
});
this._input.onDidChange(e => {
if (this._dataSource.options) {
this._filter.filterString = e;
@@ -231,17 +245,19 @@ export class Dropdown extends Disposable {
}
private _layoutTree(): void {
let filteredLength = this._dataSource.options.reduce((p, i) => {
if (this._filter.isVisible(undefined, i)) {
return p + 1;
} else {
return p;
}
}, 0);
let height = filteredLength * this._renderer.getHeight(undefined, undefined) > this._options.maxHeight ? this._options.maxHeight : filteredLength * this._renderer.getHeight(undefined, undefined);
this.$treeContainer.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) - 2 + 'px');
this._tree.layout(parseInt(this.$treeContainer.style('height')));
this._tree.refresh();
if (this._dataSource && this._dataSource.options && this._dataSource.options.length > 0) {
let filteredLength = this._dataSource.options.reduce((p, i) => {
if (this._filter.isVisible(undefined, i)) {
return p + 1;
} else {
return p;
}
}, 0);
let height = filteredLength * this._renderer.getHeight(undefined, undefined) > this._options.maxHeight ? this._options.maxHeight : filteredLength * this._renderer.getHeight(undefined, undefined);
this.$treeContainer.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) - 2 + 'px');
this._tree.layout(parseInt(this.$treeContainer.style('height')));
this._tree.refresh();
}
}
public set values(vals: string[]) {

View File

@@ -101,10 +101,19 @@ export class DropdownController extends TreeDefaults.DefaultController {
private _onSelectionChange = new Emitter<Resource>();
public readonly onSelectionChange: Event<Resource> = this._onSelectionChange.event;
private _onDropdownEscape = new Emitter<void>();
public readonly onDropdownEscape: Event<void> = this._onDropdownEscape.event;
constructor() {
super();
}
protected onEscape(tree: tree.ITree, event: IKeyboardEvent): boolean {
let response = super.onEscape(tree, event);
this._onDropdownEscape.fire();
return response;
}
protected onLeftClick(tree: tree.ITree, element: any, eventish: TreeDefaults.ICancelableEvent, origin: string): boolean {
let response = super.onLeftClick(tree, element, eventish, origin);
if (response) {

View File

@@ -30,6 +30,7 @@ export class InputBox extends AngularDisposable implements OnInit, OnChanges {
@Input() type: string;
@Input() placeholder: string;
@Input() ariaLabel: string;
@Input() value: string;
@Output() onDidChange = new EventEmitter<string | number>();
@@ -49,6 +50,9 @@ export class InputBox extends AngularDisposable implements OnInit, OnChanges {
placeholder: this.placeholder,
ariaLabel: this.ariaLabel
});
if (this.value) {
this._inputbox.value = this.value;
}
this._inputbox.onDidChange(e => {
switch (this.type) {
case 'number':

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { InputBox as vsInputBox, IInputOptions, IInputBoxStyles as vsIInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
import { InputBox as vsInputBox, IInputOptions, IInputBoxStyles as vsIInputBoxStyles, IMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { Color } from 'vs/base/common/color';
import { Event, Emitter } from 'vs/base/common/event';
@@ -32,6 +32,8 @@ export class InputBox extends vsInputBox {
private _onLoseFocus = this._register(new Emitter<OnLoseFocusParams>());
public onLoseFocus: Event<OnLoseFocusParams> = this._onLoseFocus.event;
private _isTextAreaInput: boolean;
private _hideErrors = false;
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) {
super(container, contextViewProvider, options);
@@ -48,6 +50,10 @@ export class InputBox extends vsInputBox {
self._onLoseFocus.fire({ value: self.value, hasChanged: self._lastLoseFocusValue !== self.value });
self._lastLoseFocusValue = self.value;
});
if (options && options.type === 'textarea') {
this._isTextAreaInput = true;
}
}
public style(styles: IInputBoxStyles): void {
@@ -67,6 +73,20 @@ export class InputBox extends vsInputBox {
this.applyStyles();
}
public set rows(value: number) {
this.inputElement.setAttribute('rows', value.toString());
}
public set columns(value: number) {
this.inputElement.setAttribute('cols', value.toString());
}
public layout(): void {
if (!this._isTextAreaInput) {
super.layout();
}
}
public disable(): void {
super.disable();
this.inputBackground = this.disabledInputBackground;
@@ -75,7 +95,30 @@ export class InputBox extends vsInputBox {
this.applyStyles();
}
public setHeight(value: string) {
if (this._isTextAreaInput) {
this.inputElement.style.height = value;
}
}
public isEnabled(): boolean {
return !this.inputElement.hasAttribute('disabled');
}
public get hideErrors(): boolean {
return this._hideErrors;
}
public set hideErrors(hideErrors: boolean) {
this._hideErrors = hideErrors;
if (hideErrors) {
this.hideMessage();
}
}
public showMessage(message: IMessage, force?: boolean): void {
if (!this.hideErrors) {
super.showMessage(message, force);
}
}
}

View File

@@ -172,4 +172,22 @@
margin-right: 10px;
width: 20px;
height: 20px;
}
.modal .modal-footer .dialogErrorMessage {
align-items: center;
max-height: 30px;
margin-right: 5px;
}
.modal .dialogErrorMessage .icon {
float: left;
margin-right: 10px;
width: auto;
height: 20px;
}
.modal .modal-footer .dialogErrorMessage .errorMessage {
max-height: 100%;
overflow-y: scroll;
}

View File

@@ -21,9 +21,13 @@ import { Button } from 'sql/base/browser/ui/button/button';
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
import * as TelemetryKeys from 'sql/common/telemetryKeys';
import { localize } from 'vs/nls';
import { MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes';
export const MODAL_SHOWING_KEY = 'modalShowing';
export const MODAL_SHOWING_CONTEXT = new RawContextKey<Array<string>>(MODAL_SHOWING_KEY, []);
const INFO_ALT_TEXT = localize('infoAltText', 'Info');
const WARNING_ALT_TEXT = localize('warningAltText', 'Warning');
const ERROR_ALT_TEXT = localize('errorAltText', 'Error');
export interface IModalDialogStyles {
dialogForeground?: Color;
@@ -145,7 +149,7 @@ export abstract class Modal extends Disposable implements IThemable {
/**
* Build and render the modal, will call {@link Modal#renderBody}
*/
public render() {
public render(errorMessagesInFooter: boolean = false) {
let modalBodyClass = (this._modalOptions.isAngular === false ? 'modal-body' : 'modal-body-and-footer');
let parts: Array<HTMLElement> = [];
// This modal header section refers to the header of of the dialog
@@ -182,17 +186,6 @@ export abstract class Modal extends Disposable implements IThemable {
this.renderBody(body.getHTMLElement());
if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) {
body.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => {
errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => {
this._errorIconElement = iconContainer.getHTMLElement();
this._errorIconElement.style.visibility = 'hidden';
});
errorMessageContainer.div({ class: 'errorMessage' }, (messageContainer) => {
this._errorMessage = messageContainer;
});
});
}
// This modal footer section refers to the footer of of the dialog
if (this._modalOptions.isAngular === false) {
this._modalFooterSection = $().div({ class: 'modal-footer' }, (modelFooter) => {
@@ -221,6 +214,19 @@ export abstract class Modal extends Disposable implements IThemable {
builderClass += ' wide';
}
if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) {
let builder = errorMessagesInFooter ? this._leftFooter : body;
builder.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => {
errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => {
this._errorIconElement = iconContainer.getHTMLElement();
this._errorIconElement.style.visibility = 'hidden';
});
errorMessageContainer.div({ class: 'errorMessage' }, (messageContainer) => {
this._errorMessage = messageContainer;
});
});
}
// The builder builds the dialog. It append header, body and footer sections.
this._builder = $().div({ class: builderClass, 'role': 'dialog' }, (dialogContainer) => {
this._modalDialog = dialogContainer.div({ class: 'modal-dialog ', role: 'document' }, (modalDialog) => {
@@ -355,14 +361,33 @@ export abstract class Modal extends Disposable implements IThemable {
* Show an error in the error message element
* @param err Text to show in the error message
*/
protected setError(err: string) {
protected setError(err: string, level: MessageLevel = MessageLevel.Error) {
if (this._modalOptions.hasErrors) {
if (err === '') {
this._errorIconElement.style.visibility = 'hidden';
} else {
const levelClasses = ['info', 'warning', 'error'];
let selectedLevel = levelClasses[2];
let altText = ERROR_ALT_TEXT;
if (level === MessageLevel.Information) {
selectedLevel = levelClasses[0];
altText = INFO_ALT_TEXT;
} else if (level === MessageLevel.Warning) {
selectedLevel = levelClasses[1];
altText = WARNING_ALT_TEXT;
}
levelClasses.forEach(level => {
if (selectedLevel === level) {
this._errorIconElement.classList.add(level);
} else {
this._errorIconElement.classList.remove(level);
}
});
this._errorIconElement.title = altText;
this._errorIconElement.style.visibility = 'visible';
}
this._errorMessage.innerHtml(err);
this._errorMessage.text(err);
}
}

View File

@@ -115,8 +115,7 @@ export class SelectBox extends vsSelectBox {
}
public enable(): void {
//@SQLTODO
//this.selectElement.disabled = false;
this.selectElement.disabled = false;
this.selectBackground = this.enabledSelectBackground;
this.selectForeground = this.enabledSelectForeground;
this.selectBorder = this.enabledSelectBorder;
@@ -124,8 +123,7 @@ export class SelectBox extends vsSelectBox {
}
public disable(): void {
//@SQLTODO
//this.selectElement.disabled = true;
this.selectElement.disabled = true;
this.selectBackground = this.disabledSelectBackground;
this.selectForeground = this.disabledSelectForeground;
this.selectBorder = this.disabledSelectBorder;

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#2d2d30;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>CollapseChevronDown_md_16x</title><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/><path class="icon-vs-out" d="M15.444,6.061,8,13.5.556,6.061,3.03,3.586,8,8.556l4.97-4.97Z" style="display: none;"/><path class="icon-vs-bg" d="M14.03,6.061,8,12.091,1.97,6.061,3.03,5,8,9.97,12.97,5Z"/></svg>

After

Width:  |  Height:  |  Size: 507 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>CollapseChevronDown_md_16x</title><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/><path class="icon-vs-out" d="M15.444,6.061,8,13.5.556,6.061,3.03,3.586,8,8.556l4.97-4.97Z" style="display: none;"/><path class="icon-vs-bg" d="M14.03,6.061,8,12.091,1.97,6.061,3.03,5,8,9.97,12.97,5Z"/></svg>

After

Width:  |  Height:  |  Size: 507 B

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{font-size:12px;font-family:FullMDL2Assets, Full MDL2 Assets;}</style></defs><title>filter_16x16</title><text class="cls-1" transform="translate(0 12)"> </text><path d="M0,1.53H16V3.24l-6,6v6.27H6V9.22l-6-6ZM15,2.82V2.53H1v.29l6,6v5.69H9V8.8Z"/></svg>

After

Width:  |  Height:  |  Size: 363 B

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{font-size:12px;font-family:FullMDL2Assets, Full MDL2 Assets;}.cls-1,.cls-2{fill:#fff;}</style></defs><title>filter_inverse_16x16</title><text class="cls-1" transform="translate(0.03 12.1)"> </text><path class="cls-2" d="M.05,1.63H16V3.33l-6,6v6.27H6V9.31l-6-6ZM15,2.91V2.62H1v.29l6,6v5.69H9V8.89Z"/></svg>

After

Width:  |  Height:  |  Size: 418 B

View File

@@ -31,4 +31,117 @@
height: 5px;
margin-left: 4px;
margin-top: 6px;
}
.slick-header-menubutton {
background-position: center center;
background-repeat: no-repeat;
bottom: 0;
cursor: pointer;
display: inline-block;
position: absolute;
right: 10px;
top: 0;
width: 18px;
background-image: url('down.svg');
}
.vs-dark .slick-header-menubutton {
background-image: url('down-inverse.svg');
}
.slick-header-menubutton.filtered {
background-image: url('filter.svg');
}
.vs-dark .slick-header-menubutton.filtered {
background-image: url('filter_inverse.svg');
}
.slick-header-menu {
background: none repeat scroll 0 0 white;
border: 1px solid #BFBDBD;
min-width: 175px;
padding: 4px;
z-index: 100000;
cursor: default;
display: inline-block;
margin: 0;
position: absolute;
}
.vs-dark .slick-header-menu {
background: none repeat scroll 0 0 #333333;
}
.slick-header-menu a.monaco-button.monaco-text-button {
width: 60px;
margin: 6px 6px 6px 6px;
padding: 4px;
}
.slick-header-menu .filter
{
border: 1px solid #BFBDBD;
font-size: 8pt;
height: 400px;
margin-top: 6px;
overflow: scroll;
padding: 4px;
white-space: nowrap;
width: 200px;
}
label {
display: block;
margin-bottom: 5px;
}
.slick-header-menuitem
{
border: 1px solid transparent;
padding: 2px 4px;
cursor: pointer;
list-style: none outside none;
margin: 0;
}
.slick-header-menuicon
{
display: inline-block;
height: 16px;
margin-right: 4px;
vertical-align: middle;
width: 16px;
}
.slick-header-menuicon.ascending {
background: url('sort-asc.gif');
background-position: center center;
background-repeat: no-repeat;
}
.slick-header-menuicon.descending {
background: url('sort-desc.gif');
background-position: center center;
background-repeat: no-repeat;
}
.slick-header-menucontent {
display: inline-block;
vertical-align: middle;
}
.slick-header-menuitem:hover {
border-color: #BFBDBD;
}
.header-overlay, .cell-overlay, .selection-cell-overlay {
display: block;
position: absolute;
z-index: 999;
}
.vs-dark .slick-header-menu > input.input {
color: #4a4a4a;
}

View File

@@ -0,0 +1,353 @@
// Adopted and converted to typescript from https://github.com/danny-sg/slickgrid-spreadsheet-plugins/blob/master/ext.headerfilter.js
// heavily modified
import 'vs/css!sql/base/browser/ui/table/media/table';
import { mixin } from 'vs/base/common/objects';
import { SlickGrid } from 'angular2-slickgrid';
import { Button } from '../../button/button';
import { attachButtonStyler } from 'sql/common/theme/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
export class HeaderFilter {
public onFilterApplied = new Slick.Event();
public onCommand = new Slick.Event();
private grid;
private handler = new Slick.EventHandler();
private defaults = {
filterImage: 'src/sql/media/icons/filter.svg',
sortAscImage: 'sort-asc.gif',
sortDescImage: 'sort-desc.gif'
};
private $menu;
private options: any;
private okButton: Button;
private clearButton: Button;
private cancelButton: Button;
private workingFilters: any;
private columnDef: any;
constructor(options: any, private _themeService: IThemeService) {
this.options = mixin(options, this.defaults, false);
}
public init(grid: Slick.Grid<any>): void {
this.grid = grid;
this.handler.subscribe(this.grid.onHeaderCellRendered, (e, args) => this.handleHeaderCellRendered(e , args))
.subscribe(this.grid.onBeforeHeaderCellDestroy, (e, args) => this.handleBeforeHeaderCellDestroy(e, args))
.subscribe(this.grid.onClick, (e) => this.handleBodyMouseDown)
.subscribe(this.grid.onColumnsResized, () => this.columnsResized());
this.grid.setColumns(this.grid.getColumns());
$(document.body).bind('mousedown', this.handleBodyMouseDown);
}
public destroy() {
this.handler.unsubscribeAll();
$(document.body).unbind('mousedown', this.handleBodyMouseDown);
}
private handleBodyMouseDown = (e) => {
if (this.$menu && this.$menu[0] !== e.target && !$.contains(this.$menu[0], e.target)) {
this.hideMenu();
e.preventDefault();
e.stopPropagation();
}
}
private hideMenu() {
if (this.$menu) {
this.$menu.remove();
this.$menu = null;
}
}
private handleHeaderCellRendered(e, args) {
let column = args.column;
if (column.id === '_detail_selector') {
return;
}
let $el = $('<div></div>')
.addClass('slick-header-menubutton')
.data('column', column);
$el.bind('click', (e) => this.showFilter(e)).appendTo(args.node);
}
private handleBeforeHeaderCellDestroy(e, args) {
$(args.node)
.find('.slick-header-menubutton')
.remove();
}
private addMenuItem(menu, columnDef, title, command, image) {
let $item = $('<div class="slick-header-menuitem">')
.data('command', command)
.data('column', columnDef)
.bind('click', (e) => this.handleMenuItemClick(e, command, columnDef))
.appendTo(menu);
let $icon = $('<div class="slick-header-menuicon">')
.appendTo($item);
if (title === 'Sort Ascending') {
$icon.get(0).className += ' ascending';
} else if (title === 'Sort Descending') {
$icon.get(0).className += ' descending';
}
$('<span class="slick-header-menucontent">')
.text(title)
.appendTo($item);
}
private addMenuInput(menu, columnDef) {
const self = this;
$('<input class="input" placeholder="Search" style="margin-top: 5px; width: 206px">')
.data('column', columnDef)
.bind('keyup', (e) => {
let filterVals = this.getFilterValuesByInput($(e.target));
self.updateFilterInputs(menu, columnDef, filterVals);
})
.appendTo(menu);
}
private updateFilterInputs(menu, columnDef, filterItems) {
let filterOptions = '<label><input type="checkbox" value="-1" />(Select All)</label>';
columnDef.filterValues = columnDef.filterValues || [];
// WorkingFilters is a copy of the filters to enable apply/cancel behaviour
this.workingFilters = columnDef.filterValues.slice(0);
for (let i = 0; i < filterItems.length; i++) {
let filtered = _.contains(this.workingFilters, filterItems[i]);
filterOptions += '<label><input type="checkbox" value="' + i + '"'
+ (filtered ? ' checked="checked"' : '')
+ '/>' + filterItems[i] + '</label>';
}
let $filter = menu.find('.filter');
$filter.empty().append($(filterOptions));
$(':checkbox', $filter).bind('click', (e) => {
this.workingFilters = this.changeWorkingFilter(filterItems, this.workingFilters, $(e.target));
});
}
private showFilter(e) {
let $menuButton = $(e.target);
this.columnDef = $menuButton.data('column');
this.columnDef.filterValues = this.columnDef.filterValues || [];
// WorkingFilters is a copy of the filters to enable apply/cancel behaviour
this.workingFilters = this.columnDef.filterValues.slice(0);
let filterItems;
if (this.workingFilters.length === 0) {
// Filter based all available values
filterItems = this.getFilterValues(this.grid.getData(), this.columnDef);
}
else {
// Filter based on current dataView subset
filterItems = this.getAllFilterValues(this.grid.getData().getItems(), this.columnDef);
}
if (!this.$menu) {
this.$menu = $('<div class="slick-header-menu">').appendTo(document.body);
}
this.$menu.empty();
this.addMenuItem(this.$menu, this.columnDef, 'Sort Ascending', 'sort-asc', this.options.sortAscImage);
this.addMenuItem(this.$menu, this.columnDef, 'Sort Descending', 'sort-desc', this.options.sortDescImage);
this.addMenuInput(this.$menu, this.columnDef);
let filterOptions = '<label><input type="checkbox" value="-1" />(Select All)</label>';
for (let i = 0; i < filterItems.length; i++) {
let filtered = _.contains(this.workingFilters, filterItems[i]);
if (filterItems[i] && filterItems[i].indexOf('Error:') < 0) {
filterOptions += '<label><input type="checkbox" value="' + i + '"'
+ (filtered ? ' checked="checked"' : '')
+ '/>' + filterItems[i] + '</label>';
}
}
let $filter = $('<div class="filter">')
.append($(filterOptions))
.appendTo(this.$menu);
this.okButton = new Button(this.$menu.get(0));
this.okButton.label = 'OK';
this.okButton.title = 'OK';
this.okButton.element.id = 'filter-ok-button';
let okElement = $('#filter-ok-button');
okElement.bind('click', (ev) => {
this.columnDef.filterValues = this.workingFilters.splice(0);
this.setButtonImage($menuButton, this.columnDef.filterValues.length > 0);
this.handleApply(ev, this.columnDef);
});
this.clearButton = new Button(this.$menu.get(0));
this.clearButton.label = 'Clear';
this.clearButton.title = 'Clear';
this.clearButton.element.id = 'filter-clear-button';
let clearElement = $('#filter-clear-button');
clearElement.bind('click', (ev) => {
this.columnDef.filterValues.length = 0;
this.setButtonImage($menuButton, false);
this.handleApply(ev, this.columnDef);
});
this.cancelButton = new Button(this.$menu.get(0));
this.cancelButton.label = 'Cancel';
this.cancelButton.title = 'Cancel';
this.cancelButton.element.id = 'filter-cancel-button';
let cancelElement = $('#filter-cancel-button');
cancelElement.bind('click', () => this.hideMenu());
attachButtonStyler(this.okButton, this._themeService);
attachButtonStyler(this.clearButton, this._themeService);
attachButtonStyler(this.cancelButton, this._themeService);
$(':checkbox', $filter).bind('click', (e) => {
this.workingFilters = this.changeWorkingFilter(filterItems, this.workingFilters, $(e.target));
});
let offset = $(e.target).offset();
let left = offset.left - this.$menu.width() + $(e.target).width() - 8;
let menutop = offset.top + $(e.target).height();
if (menutop + offset.top > $(window).height()) {
menutop -= (this.$menu.height() + $(e.target).height() + 8);
}
this.$menu.css('top', menutop)
.css('left', (left > 0 ? left : 0));
}
private columnsResized() {
this.hideMenu();
}
private changeWorkingFilter(filterItems, workingFilters, $checkbox) {
let value = $checkbox.val();
let $filter = $checkbox.parent().parent();
if ($checkbox.val() < 0) {
// Select All
if ($checkbox.prop('checked')) {
$(':checkbox', $filter).prop('checked', true);
workingFilters = filterItems.slice(0);
} else {
$(':checkbox', $filter).prop('checked', false);
workingFilters.length = 0;
}
} else {
let index = _.indexOf(workingFilters, filterItems[value]);
if ($checkbox.prop('checked') && index < 0) {
workingFilters.push(filterItems[value]);
let nextRow = filterItems[(parseInt(value)+1).toString()];
if (nextRow && nextRow.indexOf('Error:') >= 0) {
workingFilters.push(nextRow);
}
}
else {
if (index > -1) {
workingFilters.splice(index, 1);
}
}
}
return workingFilters;
}
private setButtonImage($el, filtered) {
let element: HTMLElement = $el.get(0);
if (filtered) {
element.className += ' filtered';
} else {
let classList = element.classList;
if (classList.contains('filtered')) {
classList.remove('filtered');
}
}
}
private handleApply(e, columnDef) {
this.hideMenu();
this.onFilterApplied.notify({ 'grid': this.grid, 'column': columnDef }, e, self);
e.preventDefault();
e.stopPropagation();
}
private getFilterValues(dataView, column) {
let seen = [];
for (let i = 0; i < dataView.getLength() ; i++) {
let value = dataView.getItem(i)[column.field];
if (!_.contains(seen, value)) {
seen.push(value);
}
}
return seen;
}
private getFilterValuesByInput($input) {
let column = $input.data('column'),
filter = $input.val(),
dataView = this.grid.getData(),
seen = [];
for (let i = 0; i < dataView.getLength() ; i++) {
let value = dataView.getItem(i)[column.field];
if (filter.length > 0) {
let itemValue = !value ? '' : value;
let lowercaseFilter = filter.toString().toLowerCase();
let lowercaseVal = itemValue.toString().toLowerCase();
if (!_.contains(seen, value) && lowercaseVal.indexOf(lowercaseFilter) > -1) {
seen.push(value);
}
}
else {
if (!_.contains(seen, value)) {
seen.push(value);
}
}
}
return _.sortBy(seen, (v) => { return v; });
}
private getAllFilterValues(data, column) {
let seen = [];
for (let i = 0; i < data.length; i++) {
let value = data[i][column.field];
if (!_.contains(seen, value)) {
seen.push(value);
}
}
return _.sortBy(seen, (v) => { return v; });
}
private handleMenuItemClick(e, command, columnDef) {
this.hideMenu();
this.onCommand.notify({
'grid': this.grid,
'column': columnDef,
'command': command
}, e, self);
e.preventDefault();
e.stopPropagation();
}
}

View File

@@ -277,8 +277,8 @@ export class RowDetailView {
item._isPadding = false;
item._parent = parent;
item._offset = offset;
item.jobId = parent.jobId;
item.name = parent.message ? parent.message : nls.localize('rowDetailView.loadError','Loading Error...');
parent._child = item;
return item;
}

View File

@@ -14,7 +14,7 @@ export interface IFindPosition {
row: number;
}
function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T> {
export function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T> {
let field = args.sortCol.field;
let sign = args.sortAsc ? 1 : -1;
return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign);

View File

@@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function defaultFormatter<T>(valueProperty: keyof T): Slick.Formatter<T> {
return (row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: Slick.SlickData): string => {
return value[valueProperty];
};
}

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
/**
* Converts HTML characters inside the string to use entities instead. Makes the string safe from
* being used e.g. in HTMLElement.innerHTML.
*/
export function escape(html: string): string {
return html.replace(/[<|>|&|"]/g, function (match) {
switch (match) {
case '<': return '&lt;';
case '>': return '&gt;';
case '&': return '&amp;';
case '"': return '&quot;';
default: return match;
}
});
}

View File

@@ -72,6 +72,7 @@ export function attachInputBoxStyler(widget: IThemable, themeService: IThemeServ
export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeService, style?:
{
selectBackground?: cr.ColorIdentifier,
selectListBackground?: cr.ColorIdentifier,
selectForeground?: cr.ColorIdentifier,
selectBorder?: cr.ColorIdentifier,
disabledSelectBackground?: cr.ColorIdentifier,
@@ -81,10 +82,17 @@ export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeSer
inputValidationWarningBorder?: cr.ColorIdentifier,
inputValidationWarningBackground?: cr.ColorIdentifier,
inputValidationErrorBorder?: cr.ColorIdentifier,
inputValidationErrorBackground?: cr.ColorIdentifier
inputValidationErrorBackground?: cr.ColorIdentifier,
focusBorder?: cr.ColorIdentifier,
listFocusBackground?: cr.ColorIdentifier,
listFocusForeground?: cr.ColorIdentifier,
listFocusOutline?: cr.ColorIdentifier,
listHoverBackground?: cr.ColorIdentifier,
listHoverForeground?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
selectBackground: (style && style.selectBackground) || cr.selectBackground,
selectListBackground: (style && style.selectListBackground) || cr.selectListBackground,
selectForeground: (style && style.selectForeground) || cr.selectForeground,
selectBorder: (style && style.selectBorder) || cr.selectBorder,
disabledSelectBackground: (style && style.disabledSelectBackground) || sqlcolors.disabledInputBackground,
@@ -94,7 +102,14 @@ export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeSer
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground,
inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder,
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground,
focusBorder: (style && style.focusBorder) || cr.focusBorder,
listFocusBackground: (style && style.listFocusBackground) || cr.listFocusBackground,
listFocusForeground: (style && style.listFocusForeground) || cr.listFocusForeground,
listFocusOutline: (style && style.listFocusOutline) || cr.activeContrastBorder,
listHoverBackground: (style && style.listHoverBackground) || cr.listHoverBackground,
listHoverForeground: (style && style.listHoverForeground) || cr.listHoverForeground,
listHoverOutline: (style && style.listFocusOutline) || cr.activeContrastBorder
}, widget);
}

View File

@@ -216,4 +216,19 @@
.vs .icon.unpin {
background: url('unpin.svg') center center no-repeat;
}
.small {
width: 16px;
height: 16px;
}
.medium {
width: 24px;
height: 24px;
}
.large {
width: 32px;
height: 32px;
}

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-blue{fill:#1BA1E2;} .icon-white{fill:#FFFFFF;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 8c0-4.418 3.582-8 8-8s8 3.582 8 8-3.582 8-8 8-8-3.582-8-8z" id="outline"/><path class="icon-vs-blue" d="M8 1c-3.865 0-7 3.135-7 7s3.135 7 7 7 7-3.135 7-7-3.135-7-7-7zm1 12h-2v-7h2v7zm0-8h-2v-2h2v2z" id="iconBg"/><path class="icon-white" d="M7 6h2v7h-2v-7zm0-1h2v-2h-2v2z" id="iconFg"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-blue{fill:#1BA1E2;} .icon-white{fill:#FFFFFF;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 8c0-4.418 3.582-8 8-8s8 3.582 8 8-3.582 8-8 8-8-3.582-8-8z" id="outline"/><path class="icon-vs-blue" d="M8 1c-3.865 0-7 3.135-7 7s3.135 7 7 7 7-3.135 7-7-3.135-7-7-7zm1 12h-2v-7h2v7zm0-8h-2v-2h2v2z" id="iconBg"/><path class="icon-white" d="M7 6h2v7h-2v-7zm0-1h2v-2h-2v2z" id="iconFg"/></svg>

Before

Width:  |  Height:  |  Size: 627 B

After

Width:  |  Height:  |  Size: 624 B

View File

@@ -130,7 +130,7 @@ export class AccountDialog extends Modal {
this._noaccountViewContainer = DOM.$('div.no-account-view');
let noAccountTitle = DOM.append(this._noaccountViewContainer, DOM.$('.no-account-view-label'));
let noAccountLabel = localize('accountDialog.noAccountLabel', 'There is no linked account. Please add an acount.');
let noAccountLabel = localize('accountDialog.noAccountLabel', 'There is no linked account. Please add an account.');
noAccountTitle.innerHTML = noAccountLabel;
// Show the add account button for the first provider

View File

@@ -6,12 +6,14 @@
import { NgModule, Inject, forwardRef, ApplicationRef, ComponentFactoryResolver, Type } from '@angular/core';
import { APP_BASE_HREF, CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { CreateLoginComponent, CREATELOGIN_SELECTOR } from 'sql/parts/admin/security/createLogin.component';
import { IBootstrapParams, providerIterator } from 'sql/services/bootstrap/bootstrapService';
import { CreateLoginComponent } from 'sql/parts/admin/security/createLogin.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
// Connection Dashboard main angular module
export const CreateLoginModule = (params: IBootstrapParams, selector: string): Type<any> => {
export const CreateLoginModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
@NgModule({
declarations: [
@@ -24,7 +26,8 @@ export const CreateLoginModule = (params: IBootstrapParams, selector: string): T
],
providers: [
{ provide: APP_BASE_HREF, useValue: '/' },
{ provide: IBootstrapParams, useValue: params }
{ provide: IBootstrapParams, useValue: params },
...providerIterator(instantiationService)
]
})
class ModuleClass {

View File

@@ -38,13 +38,13 @@ export function convertEditorInput(input: EditorInput, options: IQueryEditorOpti
let uri: URI = getQueryEditorFileUri(input);
if (uri) {
const queryResultsInput: QueryResultsInput = instantiationService.createInstance(QueryResultsInput, uri.toString());
let queryInput: QueryInput = instantiationService.createInstance(QueryInput, input.getName(), '', input, queryResultsInput, undefined);
let queryInput: QueryInput = instantiationService.createInstance(QueryInput, '', input, queryResultsInput, undefined);
return queryInput;
}
//QueryPlanInput
uri = getQueryPlanEditorUri(input);
if(uri) {
if (uri) {
let queryPlanXml: string = fs.readFileSync(uri.fsPath);
let queryPlanInput: QueryPlanInput = instantiationService.createInstance(QueryPlanInput, queryPlanXml, 'aaa', undefined);
return queryPlanInput;
@@ -60,14 +60,14 @@ export function convertEditorInput(input: EditorInput, options: IQueryEditorOpti
*/
export function getSupportedInputResource(input: IEditorInput): URI {
if (input instanceof UntitledEditorInput) {
let untitledCast: UntitledEditorInput = <UntitledEditorInput> input;
let untitledCast: UntitledEditorInput = <UntitledEditorInput>input;
if (untitledCast) {
return untitledCast.getResource();
}
}
if (input instanceof FileEditorInput) {
let fileCast: FileEditorInput = <FileEditorInput> input;
let fileCast: FileEditorInput = <FileEditorInput>input;
if (fileCast) {
return fileCast.getResource();
}
@@ -99,7 +99,7 @@ function getQueryEditorFileUri(input: EditorInput): URI {
if (uri) {
let isValidUri: boolean = !!uri && !!uri.toString;
if (isValidUri && (hasFileExtension(sqlFileTypes, input, true) || hasSqlFileMode(input)) ) {
if (isValidUri && (hasFileExtension(sqlFileTypes, input, true) || hasSqlFileMode(input))) {
return uri;
}
}
@@ -120,7 +120,7 @@ function getQueryPlanEditorUri(input: EditorInput): URI {
// If this editor is not already of type queryinput
if (!(input instanceof QueryPlanInput)) {
let uri: URI = getSupportedInputResource(input);
if(uri) {
if (uri) {
if (hasFileExtension(sqlPlanFileTypes, input, false)) {
return uri;
}
@@ -136,7 +136,7 @@ function getQueryPlanEditorUri(input: EditorInput): URI {
*/
function hasSqlFileMode(input: EditorInput): boolean {
if (input instanceof UntitledEditorInput) {
let untitledCast: UntitledEditorInput = <UntitledEditorInput> input;
let untitledCast: UntitledEditorInput = <UntitledEditorInput>input;
return untitledCast && (untitledCast.getModeId() === undefined || untitledCast.getModeId() === sqlModeId);
}

View File

@@ -21,6 +21,8 @@ export const capabilitiesOptions = 'OPTIONS_METADATA';
export const configMaxRecentConnections = 'maxRecentConnections';
export const mssqlProviderName = 'MSSQL';
export const anyProviderName = '*';
export const connectionProviderContextKey = 'connectionProvider';
export const applicationName = 'sqlops';

View File

@@ -280,26 +280,23 @@ export class ConnectionDialogService implements IConnectionDialogService {
return new Promise<void>((resolve, reject) => {
// only create the provider maps first time the dialog gets called
let capabilitiesPromise: Promise<void> = Promise.resolve();
if (this._providerTypes.length === 0) {
entries(this._capabilitiesService.providers).forEach(p => {
this._providerTypes.push(p[1].connection.displayName);
this._providerNameToDisplayNameMap[p[0]] = p[1].connection.displayName;
});
}
capabilitiesPromise.then(s => {
this.updateModelServerCapabilities(model);
// If connecting from a query editor set "save connection" to false
if (params && params.input && params.connectionType === ConnectionType.editor) {
this._model.saveProfile = false;
}
this.updateModelServerCapabilities(model);
// If connecting from a query editor set "save connection" to false
if (params && params.input && params.connectionType === ConnectionType.editor) {
this._model.saveProfile = false;
}
resolve(this.showDialogWithModel().then(() => {
if (connectionResult && connectionResult.errorMessage) {
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
}
}));
}, e => reject(e));
resolve(this.showDialogWithModel().then(() => {
if (connectionResult && connectionResult.errorMessage) {
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
}
}));
});
}

View File

@@ -172,7 +172,8 @@ export class ConnectionWidget {
strictSelection: false,
placeholder: this._defaultDatabaseName,
maxHeight: 125,
ariaLabel: databaseOption.displayName
ariaLabel: databaseOption.displayName,
actionLabel: localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
});
let serverGroupLabel = localize('serverGroup', 'Server group');

View File

@@ -19,9 +19,9 @@ import { WEBVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWebvi
import { MODELVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardModelViewContainer.contribution';
import { CONTROLHOST_CONTAINER } from 'sql/parts/dashboard/containers/dashboardControlHostContainer.contribution';
import { NAV_SECTION } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
import { IDashboardContainerRegistry, Extensions as DashboardContainerExtensions, IDashboardContainer, registerContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { IDashboardContainerRegistry, Extensions as DashboardContainerExtensions } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
import * as Constants from 'sql/parts/connection/common/constants';
const dashboardcontainerRegistry = Registry.as<IDashboardContainerRegistry>(DashboardContainerExtensions.dashboardContainerContributions);
const containerTypes = [
@@ -136,13 +136,17 @@ export function addProvider<T extends { connectionManagementService: SingleConne
*/
export function addEdition<T extends { connectionManagementService: SingleConnectionManagementService }>(config: WidgetConfig[], collection: DashboardServiceInterface): Array<WidgetConfig> {
let connectionInfo: ConnectionManagementInfo = collection.connectionManagementService.connectionInfo;
let edition = connectionInfo.serverInfo.engineEditionId;
return config.map((item) => {
if (item.edition === undefined) {
item.edition = edition;
}
return item;
});
if (connectionInfo.serverInfo) {
let edition = connectionInfo.serverInfo.engineEditionId;
return config.map((item) => {
if (item.edition === undefined) {
item.edition = edition;
}
return item;
});
} else {
return config;
}
}
/**
@@ -162,9 +166,11 @@ export function addContext(config: WidgetConfig[], collection: any, context: str
* Returns a filtered version of the widgets passed based on edition and provider
* @param config widgets to filter
*/
export function filterConfigs<T extends { when?: string }, K extends { contextKeyService: IContextKeyService }>(config: T[], collection: K): Array<T> {
export function filterConfigs<T extends { provider?: string | string[], when?: string }, K extends { contextKeyService: IContextKeyService }>(config: T[], collection: K): Array<T> {
return config.filter((item) => {
if (!item.when) {
if (!hasCompatibleProvider(item.provider, collection.contextKeyService)) {
return false;
} else if (!item.when) {
return true;
} else {
return collection.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(item.when));
@@ -172,6 +178,21 @@ export function filterConfigs<T extends { when?: string }, K extends { contextKe
});
}
/**
* Check whether the listed providers contain '*' indicating any provider will do, or that they are a match
* for the currently scoped 'connectionProvider' context key.
*/
function hasCompatibleProvider(provider: string | string[], contextKeyService: IContextKeyService): boolean {
let isCompatible = true;
let connectionProvider = contextKeyService.getContextKeyValue<string>(Constants.connectionProviderContextKey);
if (connectionProvider) {
let providers = (provider instanceof Array) ? provider : [provider];
let matchingProvider = providers.find((p) => p === connectionProvider || p === Constants.anyProviderName);
isCompatible = (matchingProvider !== undefined);
} // Else there's no connection context so skip the check
return isCompatible;
}
/**
* Get registered container if it is specified as the key
* @param container dashboard container

View File

@@ -11,29 +11,23 @@ import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, Que
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface, SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
import { WidgetConfig, TabConfig, TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
import { IPropertiesConfig } from 'sql/parts/dashboard/pages/serverDashboardPage.contribution';
import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
import { IDashboardRegistry, Extensions as DashboardExtensions, IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { PinUnpinTabAction, AddFeatureTabAction } from './actions';
import { TabComponent, TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { AngularEventType, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { DashboardTab, IConfigModifierCollection } from 'sql/parts/dashboard/common/interfaces';
import * as dashboardHelper from 'sql/parts/dashboard/common/dashboardHelper';
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import * as Constants from 'sql/parts/connection/common/constants';
import { Registry } from 'vs/platform/registry/common/platform';
import * as types from 'vs/base/common/types';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as nls from 'vs/nls';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { addDisposableListener, getContentHeight, EventType } from 'vs/base/browser/dom';
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as colors from 'vs/platform/theme/common/colorRegistry';
import * as themeColors from 'vs/workbench/common/theme';
import * as objects from 'vs/base/common/objects';
import { Event, Emitter } from 'vs/base/common/event';
import { Action } from 'vs/base/common/actions';
@@ -45,16 +39,11 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
const dashboardRegistry = Registry.as<IDashboardRegistry>(DashboardExtensions.DashboardContributions);
interface IConfigModifierCollection {
connectionManagementService: SingleConnectionManagementService;
contextKeyService: IContextKeyService;
}
@Component({
selector: 'dashboard-page',
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/common/dashboardPage.component.html'))
})
export abstract class DashboardPage extends AngularDisposable {
export abstract class DashboardPage extends AngularDisposable implements IConfigModifierCollection {
protected tabs: Array<TabConfig> = [];
@@ -144,22 +133,11 @@ export abstract class DashboardPage extends AngularDisposable {
this._tabsDispose.forEach(i => i.dispose());
this._tabsDispose = [];
// Create home tab
let homeTab: TabConfig = {
id: 'homeTab',
publisher: undefined,
title: this.homeTabTitle,
container: { 'widgets-container': homeWidgets },
context: this.context,
originalConfig: this._originalConfig,
editable: true,
canClose: false,
actions: []
};
this.addNewTab(homeTab);
let allTabs = dashboardHelper.filterConfigs(dashboardRegistry.tabs, this);
// Before separating tabs into pinned / shown, ensure that the home tab is always set up as expected
allTabs = this.setAndRemoveHomeTab(allTabs, homeWidgets);
// Load tab setting configs
this._tabSettingConfigs = this.dashboardService.getSettings<Array<TabSettingConfig>>([this.context, 'tabs'].join('.'));
@@ -206,6 +184,32 @@ export abstract class DashboardPage extends AngularDisposable {
}));
}
private setAndRemoveHomeTab(allTabs: IDashboardTab[], homeWidgets: WidgetConfig[]): IDashboardTab[] {
let homeTabConfig: TabConfig = {
id: 'homeTab',
provider: Constants.anyProviderName,
publisher: undefined,
title: this.homeTabTitle,
container: { 'widgets-container': homeWidgets },
context: this.context,
originalConfig: this._originalConfig,
editable: true,
canClose: false,
actions: []
};
let homeTabIndex = allTabs.findIndex((tab) => tab.isHomeTab === true);
if (homeTabIndex !== undefined && homeTabIndex > -1) {
// Have a tab: get its information and copy over to the home tab definition
let homeTab = allTabs.splice(homeTabIndex, 1)[0];
let tabConfig = this.initTabComponents(homeTab);
homeTabConfig.id = tabConfig.id;
homeTabConfig.container = tabConfig.container;
}
this.addNewTab(homeTabConfig);
return allTabs;
}
private rewriteConfig(): void {
let writeableConfig = objects.deepClone(this._tabSettingConfigs);
@@ -215,35 +219,12 @@ export abstract class DashboardPage extends AngularDisposable {
private loadNewTabs(dashboardTabs: IDashboardTab[], openLastTab: boolean = false) {
if (dashboardTabs && dashboardTabs.length > 0) {
let selectedTabs = dashboardTabs.map(v => {
let containerResult = dashboardHelper.getDashboardContainer(v.container);
if (!containerResult.result) {
return { id: v.id, title: v.title, container: { 'error-container': undefined }, alwaysShow: v.alwaysShow };
}
let key = Object.keys(containerResult.container)[0];
if (key === WIDGETS_CONTAINER || key === GRID_CONTAINER) {
let configs = <WidgetConfig[]>Object.values(containerResult.container)[0];
this._configModifiers.forEach(cb => {
configs = cb.apply(this, [configs, this.dashboardService, this.context]);
});
this._gridModifiers.forEach(cb => {
configs = cb.apply(this, [configs]);
});
if (key === WIDGETS_CONTAINER) {
return { id: v.id, title: v.title, container: { 'widgets-container': configs }, alwaysShow: v.alwaysShow };
} else {
return { id: v.id, title: v.title, container: { 'grid-container': configs }, alwaysShow: v.alwaysShow };
}
}
return { id: v.id, title: v.title, container: containerResult.container, alwaysShow: v.alwaysShow };
}).map(v => {
let selectedTabs = dashboardTabs.map(v => this.initTabComponents(v)).map(v => {
let actions = [];
let tabConfig = this._tabSettingConfigs.find(i => i.tabId === v.id);
let tabSettingConfig = this._tabSettingConfigs.find(i => i.tabId === v.id);
let isPinned = false;
if (tabConfig) {
isPinned = tabConfig.isPinned;
if (tabSettingConfig) {
isPinned = tabSettingConfig.isPinned;
} else if (v.alwaysShow) {
isPinned = true;
}
@@ -268,6 +249,30 @@ export abstract class DashboardPage extends AngularDisposable {
}
}
private initTabComponents(value: IDashboardTab): { id: string; title: string; container: object; alwaysShow: boolean; } {
let containerResult = dashboardHelper.getDashboardContainer(value.container);
if (!containerResult.result) {
return { id: value.id, title: value.title, container: { 'error-container': undefined }, alwaysShow: value.alwaysShow };
}
let key = Object.keys(containerResult.container)[0];
if (key === WIDGETS_CONTAINER || key === GRID_CONTAINER) {
let configs = <WidgetConfig[]>Object.values(containerResult.container)[0];
this._configModifiers.forEach(cb => {
configs = cb.apply(this, [configs, this, this.context]);
});
this._gridModifiers.forEach(cb => {
configs = cb.apply(this, [configs]);
});
if (key === WIDGETS_CONTAINER) {
return { id: value.id, title: value.title, container: { 'widgets-container': configs }, alwaysShow: value.alwaysShow };
}
else {
return { id: value.id, title: value.title, container: { 'grid-container': configs }, alwaysShow: value.alwaysShow };
}
}
return { id: value.id, title: value.title, container: containerResult.container, alwaysShow: value.alwaysShow };
}
private getContentType(tab: TabConfig): string {
return tab.container ? Object.keys(tab.container)[0] : '';
}

View File

@@ -5,8 +5,11 @@
import 'vs/css!./dashboardPanel';
import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_ACTIVE_BORDER, TAB_INACTIVE_BACKGROUND, TAB_INACTIVE_FOREGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND, TAB_BORDER } from 'vs/workbench/common/theme';
import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
import {
TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_ACTIVE_BORDER, TAB_INACTIVE_BACKGROUND,
TAB_INACTIVE_FOREGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND, TAB_BORDER, EDITOR_GROUP_BORDER
} from 'vs/workbench/common/theme';
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
@@ -98,4 +101,14 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
}
`);
}
const divider = theme.getColor(EDITOR_GROUP_BORDER);
if (divider) {
collector.addRule(`
panel.dashboard-panel > .tabbedPanel > .title .tabList .tab-header {
border-right-width: 1px;
border-right-style: solid;
}
`);
}
});

View File

@@ -7,6 +7,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { localize } from 'vs/nls';
import * as types from 'vs/base/common/types';
import * as Constants from 'sql/parts/connection/common/constants';
import { registerTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { generateContainerTypeSchemaProperties } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
import { NAV_SECTION, validateNavSectionContributionAndRegisterIcon } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
@@ -17,9 +18,11 @@ export interface IDashboardTabContrib {
id: string;
title: string;
container: object;
provider: string | string[];
when?: string;
description?: string;
alwaysShow?: boolean;
isHomeTab?: boolean;
}
const tabSchema: IJSONSchema = {
@@ -41,6 +44,11 @@ const tabSchema: IJSONSchema = {
description: localize('sqlops.extension.contributes.tab.when', 'Condition which must be true to show this item'),
type: 'string'
},
provider: {
description: localize('sqlops.extension.contributes.tab.provider', 'Defines the connection types this tab is compatible with. Defaults to "MSSQL" if not set'),
type: ['string', 'array']
},
container: {
description: localize('sqlops.extension.contributes.dashboard.tab.container', "The container that will be displayed in this tab."),
type: 'object',
@@ -49,6 +57,10 @@ const tabSchema: IJSONSchema = {
alwaysShow: {
description: localize('sqlops.extension.contributes.dashboard.tab.alwaysShow', "Whether or not this tab should always be shown or only when the user adds it."),
type: 'boolean'
},
isHomeTab: {
description: localize('sqlops.extension.contributes.dashboard.tab.isHomeTab', "Whether or not this tab should be used as the Home tab for a connection type."),
type: 'boolean'
}
}
};
@@ -67,7 +79,7 @@ const tabContributionSchema: IJSONSchema = {
ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabContrib[]>('dashboard.tabs', [], tabContributionSchema).setHandler(extensions => {
function handleCommand(tab: IDashboardTabContrib, extension: IExtensionPointUser<any>) {
let { description, container, title, when, id, alwaysShow } = tab;
let { description, container, provider, title, when, id, alwaysShow, isHomeTab } = tab;
// If always show is not specified, set it to true by default.
if (!types.isBoolean(alwaysShow)) {
@@ -88,6 +100,13 @@ ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabCo
return;
}
if (!provider) {
// Use a default. Consider warning extension developers about this in the future if in development mode
provider = Constants.mssqlProviderName;
// Cannot be a home tab if it did not specify a provider
isHomeTab = false;
}
if (Object.keys(container).length !== 1) {
extension.collector.error(localize('dashboardTab.contribution.moreThanOneDashboardContainersError', 'Exactly 1 dashboard container must be defined per space'));
return;
@@ -110,7 +129,7 @@ ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabCo
}
if (result) {
registerTab({ description, title, container, when, id, alwaysShow, publisher });
registerTab({ description, title, container, provider, when, id, alwaysShow, publisher, isHomeTab });
}
}

View File

@@ -7,9 +7,11 @@ import { OnDestroy } from '@angular/core';
import { Event } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
export enum Conditional {
'equals',
@@ -50,3 +52,8 @@ export abstract class DashboardTab extends TabChild implements OnDestroy {
this.dispose();
}
}
export interface IConfigModifierCollection {
connectionManagementService: SingleConnectionManagementService;
contextKeyService: IContextKeyService;
}

View File

@@ -5,28 +5,27 @@
import 'vs/css!./dashboardNavSection';
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, EventEmitter, OnChanges, AfterContentInit } from '@angular/core';
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, OnChanges, AfterContentInit } from '@angular/core';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { CommonServiceInterface, SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
import { WidgetConfig, TabConfig, NavSectionConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
import { TabComponent, TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { DashboardTab, IConfigModifierCollection } from 'sql/parts/dashboard/common/interfaces';
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
import * as dashboardHelper from 'sql/parts/dashboard/common/dashboardHelper';
import { Registry } from 'vs/platform/registry/common/platform';
import { Event, Emitter } from 'vs/base/common/event';
import * as nls from 'vs/nls';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@Component({
selector: 'dashboard-nav-section',
providers: [{ provide: TabChild, useExisting: forwardRef(() => DashboardNavSection) }],
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/containers/dashboardNavSection.component.html'))
})
export class DashboardNavSection extends DashboardTab implements OnDestroy, OnChanges, AfterContentInit {
export class DashboardNavSection extends DashboardTab implements OnDestroy, OnChanges, AfterContentInit, IConfigModifierCollection {
@Input() private tab: TabConfig;
protected tabs: Array<TabConfig> = [];
private _onResize = new Emitter<void>();
@@ -38,7 +37,7 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh
};
// a set of config modifiers
private readonly _configModifiers: Array<(item: Array<WidgetConfig>, dashboardServer: DashboardServiceInterface, context: string) => Array<WidgetConfig>> = [
private readonly _configModifiers: Array<(item: Array<WidgetConfig>, collection: IConfigModifierCollection, context: string) => Array<WidgetConfig>> = [
dashboardHelper.removeEmpty,
dashboardHelper.initExtensionConfigs,
dashboardHelper.addProvider,
@@ -103,7 +102,7 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh
if (key === WIDGETS_CONTAINER || key === GRID_CONTAINER) {
let configs = <WidgetConfig[]>Object.values(containerResult.container)[0];
this._configModifiers.forEach(cb => {
configs = cb.apply(this, [configs, this.dashboardService, this.tab.context]);
configs = cb.apply(this, [configs, this, this.tab.context]);
});
this._gridModifiers.forEach(cb => {
configs = cb.apply(this, [configs]);
@@ -169,4 +168,12 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh
});
}
}
public get connectionManagementService(): SingleConnectionManagementService {
return this.dashboardService.connectionManagementService;
}
public get contextKeyService(): IContextKeyService {
return this.dashboardService.scopedContextKeyService;
}
}

View File

@@ -7,11 +7,7 @@ import 'vs/css!./controlHostContent';
import { Component, forwardRef, Input, OnInit, Inject, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
import { Event, Emitter } from 'vs/base/common/event';
import { Parts } from 'vs/workbench/services/part/common/partService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import * as sqlops from 'sqlops';

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Inject, NgModule, forwardRef, ApplicationRef, ComponentFactoryResolver, NgModuleRef, NgModuleFactory } from '@angular/core';
import { Inject, NgModule, forwardRef, ApplicationRef, ComponentFactoryResolver } from '@angular/core';
import { CommonModule, APP_BASE_HREF } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule, Routes, UrlSerializer, Router, NavigationEnd } from '@angular/router';
@@ -14,7 +14,7 @@ import { ChartsModule } from 'ng2-charts/ng2-charts';
import CustomUrlSerializer from 'sql/common/urlSerializer';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { Extensions as ComponentExtensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -32,7 +32,7 @@ import { CommonServiceInterface } from 'sql/services/common/commonServiceInterfa
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
/* Base Components */
import { DashboardComponent, DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component';
import { DashboardComponent } from 'sql/parts/dashboard/dashboard.component';
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
import { DashboardGridContainer } from 'sql/parts/dashboard/containers/dashboardGridContainer.component';
@@ -51,12 +51,19 @@ import { ControlHostContent } from 'sql/parts/dashboard/contents/controlHostCont
import { DashboardControlHostContainer } from 'sql/parts/dashboard/containers/dashboardControlHostContainer.component';
import { JobsViewComponent } from 'sql/parts/jobManagement/views/jobsView.component';
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
import { AlertsViewComponent } from 'sql/parts/jobManagement/views/alertsView.component';
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsView.component';
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper];
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, AlertsViewComponent, ProxiesViewComponent, OperatorsViewComponent,
DashboardModelViewContainer, ModelComponentWrapper, Checkbox, SelectBox, InputBox,];
/* Panel */
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
@@ -75,7 +82,8 @@ import { ExplorerWidget } from 'sql/parts/dashboard/widgets/explorer/explorerWid
import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.component';
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
import { WebviewWidget } from 'sql/parts/dashboard/widgets/webview/webviewWidget.component';
import { JobStepsViewComponent } from '../jobManagement/views/jobStepsView.component';
import { JobStepsViewComponent } from 'sql/parts/jobManagement/views/jobStepsView.component';
import { IInstantiationService, _util } from 'vs/platform/instantiation/common/instantiation';
let widgetComponents = [
PropertiesWidgetComponent,
@@ -104,7 +112,7 @@ const appRoutes: Routes = [
];
// Connection Dashboard main angular module
export const DashboardModule = (params, selector: string): any => {
export const DashboardModule = (params, selector: string, instantiationService: IInstantiationService): any => {
@NgModule({
declarations: [
...baseComponents,
@@ -135,31 +143,31 @@ export const DashboardModule = (params, selector: string): any => {
{ provide: IBreadcrumbService, useClass: BreadcrumbService },
{ provide: CommonServiceInterface, useClass: DashboardServiceInterface },
{ provide: UrlSerializer, useClass: CustomUrlSerializer },
{ provide: IBootstrapParams, useValue: params }
{ provide: IBootstrapParams, useValue: params },
{ provide: ISelector, useValue: selector },
...providerIterator(instantiationService)
]
})
class ModuleClass {
private _bootstrap: DashboardServiceInterface;
private navigations = 0;
constructor(
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
@Inject(forwardRef(() => CommonServiceInterface)) bootstrap: CommonServiceInterface,
@Inject(forwardRef(() => Router)) private _router: Router,
@Inject(ITelemetryService) private telemetryService: ITelemetryService
@Inject(ITelemetryService) private telemetryService: ITelemetryService,
@Inject(ISelector) private selector: string
) {
this._bootstrap = bootstrap as DashboardServiceInterface;
}
ngDoBootstrap(appRef: ApplicationRef) {
const factory = this._resolver.resolveComponentFactory(DashboardComponent);
this._bootstrap.selector = selector;
(<any>factory).factory.selector = selector;
(<any>factory).factory.selector = this.selector;
appRef.bootstrap(factory);
this._router.events.subscribe(e => {
if (e instanceof NavigationEnd) {
this._bootstrap.handlePageNavigation();
this.navigations++;
TelemetryUtils.addTelemetry(this.telemetryService, TelemetryKeys.DashboardNavigated, {
numberOfNavigations: this._bootstrap.getNumberOfPageNavigations(),
numberOfNavigations: this.navigations,
routeUrl: e.url
});
}

View File

@@ -29,7 +29,6 @@ export class DatabaseDashboardPage extends DashboardPage implements OnInit {
background_color: colors.editorBackground,
border: 'none',
fontSize: '14px',
fontWeight: '200',
padding: '5px 0 0 0',
provider: undefined,
edition: undefined

View File

@@ -29,7 +29,6 @@ export class ServerDashboardPage extends DashboardPage implements OnInit {
background_color: colors.editorBackground,
border: 'none',
fontSize: '14px',
fontWeight: '200',
padding: '5px 0 0 0',
provider: undefined,
edition: undefined

View File

@@ -96,16 +96,12 @@ let defaultVal = [
},
{
widget: {
'backup-history-server-insight': {
cacheId: '0c7cba8b-c87a-4bcc-ae54-2f40a5503a90'
}
'backup-history-server-insight': null
}
},
{
widget: {
'all-database-size-server-insight': {
cacheId: '1d7cba8b-c87a-4bcc-ae54-2f40a5503a90'
}
'all-database-size-server-insight': null
}
}
];
@@ -121,8 +117,7 @@ export const serverDashboardTabsSchema: IJSONSchema = {
type: ['array'],
description: nls.localize('dashboardServerTabs', 'Customizes the Server dashboard tabs'),
items: generateDashboardTabSchema('server'),
default: [
]
default: []
};
export const SERVER_DASHBOARD_SETTING = 'dashboard.server.widgets';

View File

@@ -4,50 +4,29 @@
*--------------------------------------------------------------------------------------------*/
/* Node Modules */
import { Injectable, Inject, forwardRef, OnDestroy } from '@angular/core';
import { Injectable, Inject, forwardRef } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
/* SQL imports */
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { IMetadataService } from 'sql/services/metadata/metadataService';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import { IAdminService } from 'sql/parts/admin/common/adminService';
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { ConnectionContextkey } from 'sql/parts/connection/common/connectionContextKey';
import { SingleConnectionMetadataService, SingleConnectionManagementService, SingleAdminService, SingleQueryManagementService, CommonServiceInterface }
from 'sql/services/common/commonServiceInterface.service';
import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'sqlops';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
/* VS imports */
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ConfigurationEditingService, IConfigurationValue } from 'vs/workbench/services/configuration/node/configurationEditingService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Event, Emitter } from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
import * as nls from 'vs/nls';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { deepClone } from 'vs/base/common/objects';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { INotificationService } from 'vs/platform/notification/common/notification';
const DASHBOARD_SETTINGS = 'dashboard';
@@ -85,45 +64,22 @@ export class DashboardServiceInterface extends CommonServiceInterface {
private _numberOfPageNavigations = 0;
constructor(
@Inject(forwardRef(() => Router)) private _router: Router,
@Inject(INotificationService) private _notificationService: INotificationService,
@Inject(IMetadataService) metadataService: IMetadataService,
@Inject(IConnectionManagementService) connectionManagementService: IConnectionManagementService,
@Inject(IAdminService) adminService: IAdminService,
@Inject(IQueryManagementService) queryManagementService: IQueryManagementService,
@Inject(IBootstrapParams) params: IDashboardComponentParams,
@Inject(forwardRef(() => Router)) private _router: Router,
@Inject(INotificationService) private _notificationService: INotificationService,
@Inject(IAngularEventingService) private angularEventingService: IAngularEventingService,
@Inject(IConfigurationService) private _configService: IConfigurationService,
@Inject(IBootstrapParams) _params: IDashboardComponentParams
@Inject(IConfigurationService) private _configService: IConfigurationService
) {
super(_params, metadataService, connectionManagementService, adminService, queryManagementService);
}
private get params(): IDashboardComponentParams {
return this._params;
}
/**
* Set the selector for this dashboard instance, should only be set once
*/
public set selector(selector: string) {
this._uniqueSelector = selector;
this._getbootstrapParams();
}
protected _getbootstrapParams(): void {
this.scopedContextKeyService = this.params.scopedContextService;
this._connectionContextKey = this.params.connectionContextKey;
this.dashboardContextKey = this._dashboardContextKey.bindTo(this.scopedContextKeyService);
this.uri = this.params.ownerUri;
}
/**
* Set the uri for this dashboard instance, should only be set once
* Inits all the services that depend on knowing a uri
*/
protected set uri(uri: string) {
super.setUri(uri);
this._register(toDisposableSubscription(this.angularEventingService.onAngularEvent(this._uri, (event) => this.handleDashboardEvent(event))));
super(params, metadataService, connectionManagementService, adminService, queryManagementService);
// during testing there may not be params
if (this._params) {
this.dashboardContextKey = this._dashboardContextKey.bindTo(this.scopedContextKeyService);
this._register(toDisposableSubscription(this.angularEventingService.onAngularEvent(this._uri, (event) => this.handleDashboardEvent(event))));
}
}
/**

View File

@@ -28,6 +28,7 @@ import { IContextViewService, IContextMenuService } from 'vs/platform/contextvie
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressService } from 'vs/platform/progress/common/progress';
import * as types from 'vs/base/common/types';
@Component({
selector: 'explorer-widget',
@@ -120,6 +121,8 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget,
let currentProfile = this._bootstrap.connectionManagementService.connectionInfo.connectionProfile;
this._register(toDisposableSubscription(this._bootstrap.metadataService.databaseNames.subscribe(
data => {
// Handle the case where there is no metadata service
data = data || [];
let profileData = data.map(d => {
let profile = new ConnectionProfile(this.capabilitiesService, currentProfile);
profile.databaseName = d;

View File

@@ -9,7 +9,7 @@
<span #child style="white-space : nowrap; width: fit-content">
<ng-template ngFor let-item [ngForOf]="properties">
<span style="margin-left: 10px; display: inline-block;">
<div style="font-size: 11px; font-weight: lighter">{{item.displayName}}</div>
<div style="font-size: 11px;">{{item.displayName}}</div>
<div>{{item.value}}</div>
</span>
</ng-template>

View File

@@ -86,7 +86,7 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
}).filter(i => !!i);
}
this._tasks = tasks.map(i => MenuRegistry.getCommand(i));
this._tasks = tasks.map(i => MenuRegistry.getCommand(i)).filter(v => !!v);
}
ngOnInit() {
@@ -166,7 +166,6 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
}
public runTask(task: ICommandAction) {
let serverInfo = this._bootstrap.connectionManagementService.connectionInfo.serverInfo;
this.commandService.executeCommand(task.id, this._profile);
}

View File

@@ -4,20 +4,23 @@
*--------------------------------------------------------------------------------------------*/
import {
ApplicationRef, ComponentFactoryResolver, ModuleWithProviders, NgModule,
ApplicationRef, ComponentFactoryResolver, NgModule,
Inject, forwardRef, Type
} from '@angular/core';
import { APP_BASE_HREF, CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { BackupComponent, BACKUP_SELECTOR } from 'sql/parts/disasterRecovery/backup/backup.component';
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
import { BackupComponent } from 'sql/parts/disasterRecovery/backup/backup.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
// work around
const BrowserAnimationsModule = (<any>require.__$__nodeRequire('@angular/platform-browser/animations')).BrowserAnimationsModule;
// Backup wizard main angular module
export const BackupModule = (params: IBootstrapParams, selector: string): Type<any> => {
export const BackupModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
@NgModule({
declarations: [
BackupComponent
@@ -31,19 +34,22 @@ export const BackupModule = (params: IBootstrapParams, selector: string): Type<a
],
providers: [
{ provide: APP_BASE_HREF, useValue: '/' },
{ provide: IBootstrapParams, useValue: params }
{ provide: IBootstrapParams, useValue: params },
{ provide: ISelector, useValue: selector },
...providerIterator(instantiationService)
]
})
class ModuleClass {
constructor(
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
@Inject(ISelector) private selector: string
) {
}
ngDoBootstrap(appRef: ApplicationRef) {
const factory = this._resolver.resolveComponentFactory(BackupComponent);
(<any>factory).factory.selector = selector;
(<any>factory).factory.selector = this.selector;
appRef.bootstrap(factory);
}
}

View File

@@ -235,7 +235,8 @@ export class RestoreDialog extends Modal {
this._databaseDropdown = new Dropdown(inputCellContainer.getHTMLElement(), this._contextViewService, this._themeService,
{
strictSelection: false,
ariaLabel: LocalizedStrings.TARGETDATABASE
ariaLabel: LocalizedStrings.TARGETDATABASE,
actionLabel: localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
}
);
this._databaseDropdown.onValueChange(s => {

View File

@@ -23,7 +23,7 @@ import { ITree } from 'vs/base/parts/tree/browser/tree';
/**
* Implements tree view for file browser
*/
export class FileBrowserTreeView {
export class FileBrowserTreeView implements IDisposable {
private _tree: ITree;
private _toDispose: IDisposable[] = [];

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as Strings from 'vs/base/common/strings';
import { escape } from 'sql/base/common/strings';
export class DBCellValue {
displayValue: string;
@@ -25,7 +25,7 @@ export function hyperLinkFormatter(row: number, cell: any, value: any, columnDef
valueToDisplay = 'NULL';
if (!value.isNull) {
cellClasses += ' xmlLink';
valueToDisplay = Strings.escape(value.displayValue);
valueToDisplay = escape(value.displayValue);
return `<a class="${cellClasses}" href="#" >${valueToDisplay}</a>`;
} else {
cellClasses += ' missing-value';
@@ -44,12 +44,12 @@ export function textFormatter(row: number, cell: any, value: any, columnDef: any
if (DBCellValue.isDBCellValue(value)) {
valueToDisplay = 'NULL';
if (!value.isNull) {
valueToDisplay = Strings.escape(value.displayValue.replace(/(\r\n|\n|\r)/g, ' '));
valueToDisplay = escape(value.displayValue.replace(/(\r\n|\n|\r)/g, ' '));
} else {
cellClasses += ' missing-value';
}
} else if (typeof value === 'string') {
valueToDisplay = Strings.escape(value);
valueToDisplay = escape(value);
}
return `<span title="${valueToDisplay}" class="${cellClasses}">${valueToDisplay}</span>`;

View File

@@ -21,7 +21,7 @@ import { IEditDataComponentParams } from 'sql/services/bootstrap/bootstrapParams
import { GridParentComponent } from 'sql/parts/grid/views/gridParentComponent';
import { EditDataGridActionProvider } from 'sql/parts/grid/views/editData/editDataGridActions';
import { error } from 'sql/base/common/log';
import { clone } from 'sql/base/common/objects';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
@@ -195,7 +195,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
let gridData: IGridDataRow[] = result.subset.map(row => {
self.idMapping[rowIndex] = row.id;
rowIndex++;
return { values: row.cells, row: row.id };
return {
values: row.cells.map(c => {
return mixin({ ariaLabel: c.displayValue }, c);
}), row: row.id
};
});
// Append a NULL row to the end of gridData
@@ -210,23 +214,21 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
onDeleteRow(): (index: number) => void {
const self = this;
return (index: number): void => {
self.dataService.deleteRow(index)
.then(() => self.dataService.commitEdit())
.then(() => self.removeRow(index));
// If the user is deleting a new row that hasn't been committed yet then use the revert code
if (self.newRowVisible && index === self.dataSet.dataRows.getLength() - 2) {
self.revertCurrentRow();
} else {
self.dataService.deleteRow(index)
.then(() => self.dataService.commitEdit())
.then(() => self.removeRow(index));
}
};
}
onRevertRow(): (index: number) => void {
onRevertRow(): () => void {
const self = this;
return (index: number): void => {
// Force focus to the first cell (completing any active edit operation)
self.focusCell(index, 0, false);
this.currentEditCellValue = null;
// Perform a revert row operation
self.dataService.revertRow(index)
.then(() => {
self.refreshResultsets();
});
return (): void => {
self.revertCurrentRow();
};
}
@@ -275,24 +277,27 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
}
if (this.currentCell.row !== row) {
// We're changing row, commit the changes
cellSelectTasks = cellSelectTasks.then(() => {
return self.dataService.commitEdit()
.then(
result => {
// If we're currently adding a new row, only commit it if it has changes or the user is trying to add another new row
if (this.newRowVisible && this.currentCell.row === this.dataSet.dataRows.getLength() - 2 && !this.isNullRow(row) && this.currentEditCellValue === null) {
cellSelectTasks = cellSelectTasks.then(() => {
return this.revertCurrentRow().then(() => this.focusCell(row, column));
});
} else {
// We're changing row, commit the changes
cellSelectTasks = cellSelectTasks.then(() => {
return self.dataService.commitEdit().then(result => {
// Committing was successful, clean the grid
self.setGridClean();
self.rowIdMappings = {};
self.newRowVisible = false;
return Promise.resolve();
},
error => {
}, error => {
// Committing failed, jump back to the last selected cell
self.focusCell(self.currentCell.row, self.currentCell.column);
return Promise.reject(null);
}
);
});
});
});
}
}
if (this.isNullRow(row) && !this.removingNewRow) {
@@ -428,7 +433,18 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
// If the esc key was pressed while in a create session
let currentNewRowIndex = this.dataSet.totalRows - 2;
if (e.keyCode === KeyCode.Escape && this.newRowVisible && this.currentCell.row === currentNewRowIndex) {
if (e.keyCode === KeyCode.Escape) {
this.revertCurrentRow();
handled = true;
}
return handled;
}
// Private Helper Functions ////////////////////////////////////////////////////////////////////////////
private async revertCurrentRow(): Promise<void> {
let currentNewRowIndex = this.dataSet.totalRows - 2;
if (this.newRowVisible && this.currentCell.row === currentNewRowIndex) {
// revert our last new row
this.removingNewRow = true;
@@ -437,17 +453,21 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.removeRow(currentNewRowIndex);
this.newRowVisible = false;
});
handled = true;
} else if (e.keyCode === KeyCode.Escape) {
this.currentEditCellValue = null;
this.onRevertRow()(this.currentCell.row);
handled = true;
} else {
try {
// Perform a revert row operation
if (this.currentCell) {
await this.dataService.revertRow(this.currentCell.row);
}
} finally {
// The operation may fail if there were no changes sent to the service to revert,
// so clear any existing client-side edit and refresh the table regardless
this.currentEditCellValue = null;
this.refreshResultsets();
}
}
return handled;
}
// Private Helper Functions ////////////////////////////////////////////////////////////////////////////
// Checks if input row is our NULL new row
private isNullRow(row: number): boolean {
// Null row is always at index (totalRows - 1)
@@ -535,9 +555,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
// refresh results view
this.onScroll(0);
// Set focus to the row index column of the removed row
// Set focus to the row index column of the removed row if the current selection is in the removed row
setTimeout(() => {
this.focusCell(row, 0);
if (this.currentCell.row === row) {
this.focusCell(row, 0);
}
this.removingNewRow = false;
}, this.scrollTimeOutTime);
}

View File

@@ -7,12 +7,14 @@
import { ApplicationRef, ComponentFactoryResolver, NgModule, Inject, forwardRef, Type } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { EditDataComponent, EDITDATA_SELECTOR } from 'sql/parts/grid/views/editData/editData.component';
import { SlickGrid } from 'angular2-slickgrid';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
export const EditDataModule = (params: IBootstrapParams, selector: string): Type<any> => {
import { EditDataComponent } from 'sql/parts/grid/views/editData/editData.component';
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
export const EditDataModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
@NgModule({
@@ -30,19 +32,22 @@ export const EditDataModule = (params: IBootstrapParams, selector: string): Type
EditDataComponent
],
providers: [
{ provide: IBootstrapParams, useValue: params }
{ provide: IBootstrapParams, useValue: params },
{ provide: ISelector, useValue: selector },
...providerIterator(instantiationService)
]
})
class ModuleClass {
constructor(
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
@Inject(ISelector) private selector: string
) {
}
ngDoBootstrap(appRef: ApplicationRef) {
const factory = this._resolver.resolveComponentFactory(EditDataComponent);
(<any>factory).factory.selector = selector;
(<any>factory).factory.selector = this.selector;
appRef.bootstrap(factory);
}
}

View File

@@ -19,7 +19,7 @@ export class EditDataGridActionProvider extends GridActionProvider {
dataService: DataService,
selectAllCallback: (index: number) => void,
private _deleteRowCallback: (index: number) => void,
private _revertRowCallback: (index: number) => void,
private _revertRowCallback: () => void,
@IInstantiationService instantiationService: IInstantiationService
) {
super(dataService, selectAllCallback, instantiationService);
@@ -56,18 +56,18 @@ export class DeleteRowAction extends Action {
export class RevertRowAction extends Action {
public static ID = 'grid.revertRow';
public static LABEL = localize('revertRow', 'Revert Row');
public static LABEL = localize('revertRow', 'Revert Current Row');
constructor(
id: string,
label: string,
private callback: (index: number) => void
private callback: () => void
) {
super(id, label);
}
public run(gridInfo: IGridInfo): TPromise<boolean> {
this.callback(gridInfo.rowIndex);
this.callback();
return TPromise.as(true);
}
}

View File

@@ -5,4 +5,9 @@
.editdata-component * {
box-sizing: border-box;
}
#workbench\.editor\.editDataEditor .monaco-toolbar .monaco-select-box {
margin-top: 4px;
margin-bottom: 4px;
}

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