Compare commits

...

42 Commits

Author SHA1 Message Date
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
105 changed files with 3689 additions and 971 deletions

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

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

@@ -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

@@ -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.4.0-alpha.46",
"downloadFileNames": {
"Windows_86": "win-x86-netcoreapp2.1.zip",
"Windows_64": "win-x64-netcoreapp2.1.zip",

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,207 @@ 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;
}
// 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;
}
// 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');
}
// 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');
}
// ------------------------------- < 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,9 +30,9 @@ 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
];
constructor(client: SqlOpsDataClient) {
@@ -55,35 +54,312 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
protected registerProvider(options: undefined): Disposable {
const client = this._client;
// 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 => 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 => 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 => 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 => 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 => 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 => 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 => 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 => 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 => 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 => 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 => 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 => 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 => 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 => 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 => r,
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
@@ -93,7 +369,25 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
providerId: client.providerId,
getJobs,
getJobHistory,
jobAction
jobAction,
createJob,
updateJob,
deleteJob,
createJobStep,
updateJobStep,
deleteJobStep,
getAlerts,
createAlert,
updateAlert,
deleteAlert,
getOperators,
createOperator,
updateOperator,
deleteOperator,
getProxies,
createProxy,
updateProxy,
deleteProxy
});
}
}

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.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"

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.1",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",

View File

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.30.4",
"version": "0.30.6",
"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.20",
"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

@@ -82,7 +82,8 @@ export default class MainController implements vscode.Disposable {
tab1.registerContent(async (view) => {
let inputBox = view.modelBuilder.inputBox()
.withProperties({
//width: 300
multiline: true,
height: 100
}).component();
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
inputBoxWrapper.loading = false;
@@ -171,12 +172,67 @@ export default class MainController implements vscode.Disposable {
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: 50
height: 150
}).withItems([
radioButton, groupModel1, radioButton2]
, { flex: '1 1 50%' }).component();
@@ -200,6 +256,15 @@ export default class MainController implements vscode.Disposable {
}, {
component: flexRadioButtonsModel,
title: 'Options'
}, {
component: declarativeTable,
title: 'Declarative Table'
}, {
component: table,
title: 'Table'
}, {
component: listBox,
title: 'List Box'
}], {
horizontal: false,
componentWidth: 400
@@ -322,7 +387,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

@@ -132,7 +132,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 +145,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 +196,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 +240,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

@@ -32,6 +32,7 @@ export class InputBox extends vsInputBox {
private _onLoseFocus = this._register(new Emitter<OnLoseFocusParams>());
public onLoseFocus: Event<OnLoseFocusParams> = this._onLoseFocus.event;
private _isTextAreaInput: boolean;
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) {
super(container, contextViewProvider, options);
@@ -48,6 +49,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 +72,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,6 +94,12 @@ 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');
}

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

@@ -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

@@ -11,8 +11,6 @@ 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';
@@ -27,13 +25,8 @@ import { AngularDisposable } from 'sql/base/common/lifecycle';
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';

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

@@ -52,11 +52,16 @@ import { DashboardControlHostContainer } from 'sql/parts/dashboard/containers/da
import { JobsViewComponent } from 'sql/parts/jobManagement/views/jobsView.component';
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.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, DashboardModelViewContainer, ModelComponentWrapper, Checkbox,
SelectBox,
InputBox,];
/* Panel */
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';

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

@@ -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() {

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

@@ -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;
}

View File

@@ -25,7 +25,7 @@ import { GridActionProvider } from 'sql/parts/grid/views/gridActions';
import { IQueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { error } from 'sql/base/common/log';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { clone } from 'sql/base/common/objects';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import * as strings from 'vs/base/common/strings';
@@ -301,7 +301,9 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
for (let row = 0; row < rows.rows.length; row++) {
// Push row values onto end of gridData for slickgrid
gridData.push({
values: rows.rows[row]
values: rows.rows[row].map(c => {
return mixin({ ariaLabel: c.displayValue }, c);
})
});
}

View File

@@ -168,13 +168,16 @@ export class InsightsDialogView extends Modal {
this._splitView = new SplitView(container);
const itemsHeaderTitle = nls.localize("insights.dialog.items", "Items");
const itemsDetailHeaderTitle = nls.localize("insights.dialog.itemDetails", "Item Details");
this._topTableData = new TableDataView();
this._bottomTableData = new TableDataView();
let topTableView = new TableCollapsibleView(nls.localize("insights.dialog.items", "Items"), { sizing: ViewSizing.Flexible, ariaHeaderLabel: 'title' }, this._topTableData, this._topColumns, { forceFitColumns: true });
let topTableView = new TableCollapsibleView(itemsHeaderTitle, { sizing: ViewSizing.Flexible, ariaHeaderLabel: itemsHeaderTitle }, this._topTableData, this._topColumns, { forceFitColumns: true });
this._topTable = topTableView.table;
topTableView.addContainerClass('insights');
this._topTable.setSelectionModel(new RowSelectionModel<ListResource>());
let bottomTableView = new TableCollapsibleView(nls.localize("insights.dialog.itemDetails", "Item Details"), { sizing: ViewSizing.Flexible, ariaHeaderLabel: 'title' }, this._bottomTableData, this._bottomColumns, { forceFitColumns: true });
let bottomTableView = new TableCollapsibleView(itemsDetailHeaderTitle, { sizing: ViewSizing.Flexible, ariaHeaderLabel: itemsDetailHeaderTitle }, this._bottomTableData, this._bottomColumns, { forceFitColumns: true });
this._bottomTable = bottomTableView.table;
this._bottomTable.setSelectionModel(new RowSelectionModel<ListResource>());

View File

@@ -82,7 +82,6 @@ export class AgentViewComponent {
public set jobId(value: string) {
this._jobId = value;
this._cd.detectChanges();
}
public set showHistory(value: boolean) {
@@ -92,7 +91,6 @@ export class AgentViewComponent {
public set agentJobInfo(value: AgentJobInfo) {
this._agentJobInfo = value;
this._cd.detectChanges();
}
public set refresh(value: boolean) {
@@ -104,6 +102,10 @@ export class AgentViewComponent {
this._expanded.set(jobId, errorMessage);
}
public set expanded(value: Map<string, string>) {
this._expanded = value;
}
public layout() {
this._panel.layout();
}

View File

@@ -92,4 +92,58 @@ export class AgentJobUtilities {
return;
}
}
public static convertDurationToSeconds(duration: string): number {
let split = duration.split(':');
let seconds = (+split[0]) * 60 * 60 + (+split[1]) * 60 + (+split[2]);
return seconds;
}
public static convertColFieldToName(colField: string) {
switch(colField) {
case('name'):
return 'Name';
case('lastRun'):
return 'Last Run';
case('nextRun'):
return 'Next Run';
case('enabled'):
return 'Enabled';
case('status'):
return 'Status';
case('category'):
return 'Category';
case('runnable'):
return 'Runnable';
case('schedule'):
return 'Schedule';
case('lastRunOutcome'):
return 'Last Run Outcome';
}
return '';
}
public static convertColNameToField(columnName: string) {
switch(columnName) {
case('Name'):
return 'name';
case('Last Run'):
return 'lastRun';
case('Next Run'):
return 'nextRun';
case('Enabled'):
return 'enabled';
case('Status'):
return 'status';
case('Category'):
return 'category';
case('Runnable'):
return 'runnable';
case('Schedule'):
return 'schedule';
case('Last Run Outcome'):
return 'lastRunOutcome';
}
return '';
}
}

View File

@@ -23,7 +23,7 @@ export interface IJobManagementService {
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult>;
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult>;
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus>;
addToCache(server: string, cache: JobCacheObject);

View File

@@ -39,7 +39,7 @@ export class JobManagementService implements IJobManagementService {
});
}
public jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> {
public jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
return this._runAction(connectionUri, (runner) => {
return runner.jobAction(connectionUri, jobName, action);
});
@@ -82,6 +82,7 @@ export class JobCacheObject {
private _jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = {};
private _prevJobID: string;
private _serverName: string;
private _dataView: Slick.Data.DataView<any>;
/* Getters */
public get jobs(): sqlops.AgentJobInfo[] {
@@ -104,6 +105,10 @@ export class JobCacheObject {
return this._serverName;
}
public get dataView(): Slick.Data.DataView<any> {
return this._dataView;
}
/* Setters */
public set jobs(value: sqlops.AgentJobInfo[]) {
this._jobs = value;
@@ -125,4 +130,7 @@ export class JobCacheObject {
this._serverName = value;
}
}
public set dataView(value: Slick.Data.DataView<any>) {
this._dataView = value;
}
}

View File

@@ -49,12 +49,12 @@ jobhistory-component {
font-size: larger;
}
.vs-dark #jobsDiv .slick-cell {
background:#333333 !important;
.vs-dark #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background:#333333;
}
#jobsDiv .slick-cell {
background: white !important;
#jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background: white;
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
@@ -205,4 +205,37 @@ agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.od
#jobsDiv jobsview-component .jobview-grid .slick-cell.l1.r1.error-row td.jobview-jobnameindicatorfailure {
width: 0;
background: none;
}
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
background: #dcdcdc !important;
}
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
background: #444444 !important;
}
table.jobprevruns div.bar1, table.jobprevruns div.bar2, table.jobprevruns div.bar3,
table.jobprevruns div.bar4, table.jobprevruns div.bar5 {
padding-top: 3px;
padding-left: 5px;
width: 10px;
}
.jobview-grid .slick-cell.l10.r10 {
text-align: center;
display: inline-flex;
}
table.jobprevruns {
margin: auto;
height: 100%;
}
table.jobprevruns > tbody {
vertical-align: bottom;
}

View File

@@ -7,31 +7,22 @@ import 'vs/css!./jobHistory';
import 'vs/css!sql/media/icons/common-icons';
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
import { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops';
import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { RunJobAction, StopJobAction } from 'sql/parts/jobManagement/views/jobHistoryActions';
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
import { AgentJobUtilities } from '../common/agentJobUtilities';
import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
import { IJobManagementService } from '../common/interfaces';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
import { JobHistoryController, JobHistoryDataSource,
JobHistoryRenderer, JobHistoryFilter, JobHistoryModel, JobHistoryRow } from 'sql/parts/jobManagement/views/jobHistoryTree';
import { JobStepsViewComponent } from 'sql/parts/jobManagement/views/jobStepsView.component';
import { JobStepsViewRow } from './jobStepsViewTree';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { Disposable } from 'vs/base/common/lifecycle';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { ITreeOptions } from 'vs/base/parts/tree/browser/tree';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
@@ -301,7 +292,4 @@ export class JobHistoryComponent extends Disposable implements OnInit {
this._showSteps = value;
this._cd.detectChanges();
}
}

View File

@@ -245,6 +245,3 @@ jobhistory-component > .actionbar-container .monaco-action-bar > ul.actions-cont
border-top: 3px solid #444444;
}

View File

@@ -8,9 +8,7 @@ import { Action } from 'vs/base/common/actions';
import * as nls from 'vs/nls';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { BaseActionContext } from '../../../workbench/common/actions';
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
import { JobManagementService } from '../common/jobManagementService';
import { IJobManagementService } from '../common/interfaces';
export enum JobHistoryActions {
@@ -33,7 +31,7 @@ export class RunJobAction extends Action {
let ownerUri = context.ownerUri;
return new TPromise<boolean>((resolve, reject) => {
this.jobManagementService.jobAction(ownerUri, jobName, JobHistoryActions.Run).then(result => {
if (result.succeeded) {
if (result.success) {
var startMsg = nls.localize('jobSuccessfullyStarted', ': The job was successfully started.');
this.notificationService.notify({
severity: Severity.Info,
@@ -68,7 +66,7 @@ export class StopJobAction extends Action {
let ownerUri = context.ownerUri;
return new TPromise<boolean>((resolve, reject) => {
this.jobManagementService.jobAction(ownerUri, jobName, JobHistoryActions.Stop).then(result => {
if (result.succeeded) {
if (result.success) {
var stopMsg = nls.localize('jobSuccessfullyStopped', ': The job was successfully stopped.');
this.notificationService.notify({
severity: Severity.Info,

View File

@@ -3,33 +3,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Router } from '@angular/router';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { MetadataType } from 'sql/parts/connection/common/connectionManagement';
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
import {
NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, ScriptExecuteAction, ScriptAlterAction,
BackupAction, ManageActionContext, BaseActionContext, ManageAction, RestoreAction
} from 'sql/workbench/common/actions';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import * as Constants from 'sql/parts/connection/common/constants';
import * as tree from 'vs/base/parts/tree/browser/tree';
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
import { Promise, TPromise } from 'vs/base/common/winjs.base';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { generateUuid } from 'vs/base/common/uuid';
import * as DOM from 'vs/base/browser/dom';
import { OEAction } from 'sql/parts/objectExplorer/viewlet/objectExplorerActions';
import { $ } from 'vs/base/browser/builder';
import { AgentJobHistoryInfo } from 'sqlops';
import { Agent } from 'vs/base/node/request';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { JobHistoryComponent } from './jobHistory.component';
export class JobHistoryRow {
runDate: string;

View File

@@ -6,22 +6,14 @@
import 'vs/css!./jobStepsView';
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable, AfterContentChecked } from '@angular/core';
import { AgentJobHistoryInfo } from 'sqlops';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IJobManagementService } from '../common/interfaces';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter,
JobStepsViewRenderer, JobStepsViewRow, JobStepsViewModel} from 'sql/parts/jobManagement/views/jobStepsViewTree';
JobStepsViewRenderer, JobStepsViewModel} from 'sql/parts/jobManagement/views/jobStepsViewTree';
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component';

View File

@@ -3,29 +3,17 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Router } from '@angular/router';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { MetadataType } from 'sql/parts/connection/common/connectionManagement';
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
import {
NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, ScriptExecuteAction, ScriptAlterAction,
BackupAction, ManageActionContext, BaseActionContext, ManageAction, RestoreAction
} from 'sql/workbench/common/actions';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import * as Constants from 'sql/parts/connection/common/constants';
import * as tree from 'vs/base/parts/tree/browser/tree';
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
import { Promise, TPromise } from 'vs/base/common/winjs.base';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { generateUuid } from 'vs/base/common/uuid';
import * as DOM from 'vs/base/browser/dom';
import { OEAction } from 'sql/parts/objectExplorer/viewlet/objectExplorerActions';
import { AgentJobHistoryInfo } from 'sqlops';
import { Agent } from 'vs/base/node/request';
import { AgentJobUtilities } from 'sql/parts/jobManagement/common/agentJobUtilities';
export class JobStepsViewRow {

View File

@@ -10,26 +10,26 @@ import 'vs/css!sql/parts/grid/media/slick.grid';
import 'vs/css!sql/parts/grid/media/slickGrid';
import 'vs/css!../common/media/jobs';
import 'vs/css!sql/media/icons/common-icons';
import 'vs/css!sql/base/browser/ui/table/media/table';
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { Table } from 'sql/base/browser/ui/table/table';
import { AgentViewComponent } from '../agent/agentView.component';
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowdetailview';
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
import { AgentJobUtilities } from '../common/agentJobUtilities';
import { AgentJobUtilities } from 'sql/parts/jobManagement/common/agentJobUtilities';
import { HeaderFilter } from 'sql/base/browser/ui/table/plugins/headerFilter.plugin';
import * as dom from 'vs/base/browser/dom';
import { IJobManagementService } from '../common/interfaces';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
export const ROW_HEIGHT: number = 45;
@Component({
selector: JOBSVIEW_SELECTOR,
@@ -44,23 +44,35 @@ export class JobsViewComponent implements AfterContentChecked {
private _disposables = new Array<vscode.Disposable>();
private columns: Array<Slick.Column<any>> = [
{ name: nls.localize('jobColumns.name', 'Name'), field: 'name', formatter: this.renderName, width: 200, id: 'name' },
{ name: nls.localize('jobColumns.lastRun', 'Last Run'), field: 'lastRun', width: 150, id: 'lastRun' },
{ name: nls.localize('jobColumns.nextRun', 'Next Run'), field: 'nextRun', width: 150, id: 'nextRun' },
{ name: nls.localize('jobColumns.enabled', 'Enabled'), field: 'enabled', width: 70, id: 'enabled' },
{ name: nls.localize('jobColumns.status', 'Status'), field: 'currentExecutionStatus', width: 60, id: 'currentExecutionStatus' },
{ name: nls.localize('jobColumns.category', 'Category'), field: 'category', width: 150, id: 'category' },
{ name: nls.localize('jobColumns.runnable', 'Runnable'), field: 'runnable', width: 50, id: 'runnable' },
{ name: nls.localize('jobColumns.schedule', 'Schedule'), field: 'hasSchedule', width: 50, id: 'hasSchedule' },
{ name: nls.localize('jobColumns.lastRunOutcome', 'Last Run Outcome'), field: 'lastRunOutcome', width: 150, id: 'lastRunOutcome' },
{ name: nls.localize('jobColumns.name','Name'), field: 'name', formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext), width: 200 , id: 'name' },
{ name: nls.localize('jobColumns.lastRun','Last Run'), field: 'lastRun', width: 120, id: 'lastRun' },
{ name: nls.localize('jobColumns.nextRun','Next Run'), field: 'nextRun', width: 120, id: 'nextRun' },
{ name: nls.localize('jobColumns.enabled','Enabled'), field: 'enabled', width: 50, id: 'enabled' },
{ name: nls.localize('jobColumns.status','Status'), field: 'currentExecutionStatus', width: 60, id: 'currentExecutionStatus' },
{ name: nls.localize('jobColumns.category','Category'), field: 'category', width: 120, id: 'category' },
{ name: nls.localize('jobColumns.runnable','Runnable'), field: 'runnable', width: 70, id: 'runnable' },
{ name: nls.localize('jobColumns.schedule','Schedule'), field: 'hasSchedule', width: 60, id: 'hasSchedule' },
{ name: nls.localize('jobColumns.lastRunOutcome', 'Last Run Outcome'), field: 'lastRunOutcome', width: 120, id: 'lastRunOutcome' },
{ name: nls.localize('jobColumns.previousRuns', 'Previous Runs'), formatter: this.renderChartsPostHistory, field: 'previousRuns', width: 80, id: 'previousRuns'}
];
private options: Slick.GridOptions<any> = {
syncColumnCellResize: true,
enableColumnReorder: false,
rowHeight: 45,
enableCellNavigation: true,
editable: true
};
private rowDetail: RowDetailView;
private dataView: Slick.Data.DataView<any>;
private filterPlugin: any;
private dataView: any;
@ViewChild('jobsgrid') _gridEl: ElementRef;
private isVisible: boolean = false;
private isInitialized: boolean = false;
private isRefreshing: boolean = false;
private _table: Table<any>;
public jobs: sqlops.AgentJobInfo[];
public jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = Object.create(null);
@@ -68,13 +80,18 @@ export class JobsViewComponent implements AfterContentChecked {
private _isCloud: boolean;
private _showProgressWheel: boolean;
private _tabHeight: number;
private filterStylingMap: { [columnName: string]: [any] ;} = {};
private filterStack = ['start'];
private filterValueMap: { [columnName: string]: string[] ;} = {};
private sortingStylingMap: { [columnName: string]: any; } = {};
constructor(
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
@Inject(IThemeService) private _themeService: IThemeService
) {
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
@@ -111,10 +128,14 @@ export class JobsViewComponent implements AfterContentChecked {
} else if (this.isVisible === true && this._agentViewComponent.refresh === true) {
this._showProgressWheel = true;
this.onFirstVisible(false);
this.isRefreshing = true;
this._agentViewComponent.refresh = false;
} else if (this.isVisible === true && this._agentViewComponent.refresh === false) {
this._showProgressWheel = true;
this.onFirstVisible(true);
if (!this.isRefreshing) {
this._showProgressWheel = true;
this.onFirstVisible(true);
}
this.isRefreshing = false;
} else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) {
this.isVisible = false;
}
@@ -129,7 +150,7 @@ export class JobsViewComponent implements AfterContentChecked {
let options = <Slick.GridOptions<any>>{
syncColumnCellResize: true,
enableColumnReorder: false,
rowHeight: 45,
rowHeight: ROW_HEIGHT,
enableCellNavigation: true,
forceFitColumns: true
};
@@ -147,9 +168,11 @@ export class JobsViewComponent implements AfterContentChecked {
panelRows: 1
});
this.rowDetail = rowDetail;
columns.unshift(this.rowDetail.getColumnDefinition());
this._table = new Table(this._gridEl.nativeElement, undefined, columns, options);
let filterPlugin = new HeaderFilter({}, this._themeService);
this.filterPlugin = filterPlugin;
$(this._gridEl.nativeElement).empty();
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
this._table.grid.setData(this.dataView, true);
this._table.grid.onClick.subscribe((e, args) => {
let job = self.getJob(args);
@@ -158,7 +181,7 @@ export class JobsViewComponent implements AfterContentChecked {
self._agentViewComponent.showHistory = true;
});
if (cached && this._agentViewComponent.refresh !== true) {
this.onJobsAvailable(this._jobCacheObject.jobs);
this.onJobsAvailable(null);
} else {
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
this._jobManagementService.getJobs(ownerUri).then((result) => {
@@ -172,50 +195,137 @@ export class JobsViewComponent implements AfterContentChecked {
}
private onJobsAvailable(jobs: sqlops.AgentJobInfo[]) {
let jobViews = jobs.map((job) => {
return {
id: job.jobId,
jobId: job.jobId,
name: job.name,
lastRun: AgentJobUtilities.convertToLastRun(job.lastRun),
nextRun: AgentJobUtilities.convertToNextRun(job.nextRun),
enabled: AgentJobUtilities.convertToResponse(job.enabled),
currentExecutionStatus: AgentJobUtilities.convertToExecutionStatusString(job.currentExecutionStatus),
category: job.category,
runnable: AgentJobUtilities.convertToResponse(job.runnable),
hasSchedule: AgentJobUtilities.convertToResponse(job.hasSchedule),
lastRunOutcome: AgentJobUtilities.convertToStatusString(job.lastRunOutcome)
};
});
let jobViews: any;
let start: boolean = true;
if (!jobs) {
let dataView = this._jobCacheObject.dataView;
jobViews = dataView.getItems();
start = false;
} else {
jobViews = jobs.map((job) => {
return {
id: job.jobId,
jobId: job.jobId,
name: job.name,
lastRun: AgentJobUtilities.convertToLastRun(job.lastRun),
nextRun: AgentJobUtilities.convertToNextRun(job.nextRun),
enabled: AgentJobUtilities.convertToResponse(job.enabled),
currentExecutionStatus: AgentJobUtilities.convertToExecutionStatusString(job.currentExecutionStatus),
category: job.category,
runnable: AgentJobUtilities.convertToResponse(job.runnable),
hasSchedule: AgentJobUtilities.convertToResponse(job.hasSchedule),
lastRunOutcome: AgentJobUtilities.convertToStatusString(job.lastRunOutcome)
};
});
}
this._table.registerPlugin(<any>this.rowDetail);
this.filterPlugin.onFilterApplied.subscribe((e, args) => {
this.dataView.refresh();
this._table.grid.resetActiveCell();
let filterValues = args.column.filterValues;
if (filterValues) {
if (filterValues.length === 0) {
// if an associated styling exists with the current filters
if (this.filterStylingMap[args.column.name]) {
let filterLength = this.filterStylingMap[args.column.name].length;
// then remove the filtered styling
for (let i = 0; i < filterLength; i++) {
let lastAppliedStyle = this.filterStylingMap[args.column.name].pop();
this._table.grid.removeCellCssStyles(lastAppliedStyle[0]);
}
delete this.filterStylingMap[args.column.name];
let index = this.filterStack.indexOf(args.column.name, 0);
if (index > -1) {
this.filterStack.splice(index, 1);
delete this.filterValueMap[args.column.name];
}
// apply the previous filter styling
let currentItems = this.dataView.getFilteredItems();
let styledItems = this.filterValueMap[this.filterStack[this.filterStack.length-1]][1];
if (styledItems === currentItems) {
let lastColStyle = this.filterStylingMap[this.filterStack[this.filterStack.length-1]];
for (let i = 0; i < lastColStyle.length; i++) {
this._table.grid.setCellCssStyles(lastColStyle[i][0], lastColStyle[i][1]);
}
} else {
// style it all over again
let seenJobs = 0;
for (let i = 0; i < currentItems.length; i++) {
this._table.grid.removeCellCssStyles('error-row'+i.toString());
let item = this.dataView.getFilteredItems()[i];
if (item.lastRunOutcome === 'Failed') {
this.addToStyleHash(seenJobs, false, this.filterStylingMap, args.column.name);
if (this.filterStack.indexOf(args.column.name) < 0) {
this.filterStack.push(args.column.name);
this.filterValueMap[args.column.name] = [filterValues];
}
// one expansion for the row and one for
// the error detail
seenJobs ++;
i++;
}
seenJobs++;
}
this.dataView.refresh();
this.filterValueMap[args.column.name].push(this.dataView.getFilteredItems());
this._table.grid.resetActiveCell();
}
if (this.filterStack.length === 0) {
this.filterStack = ['start'];
}
}
} else {
let seenJobs = 0;
for (let i = 0; i < this.jobs.length; i++) {
this._table.grid.removeCellCssStyles('error-row'+i.toString());
let item = this.dataView.getItemByIdx(i);
// current filter
if (_.contains(filterValues, item[args.column.field])) {
// check all previous filters
if (this.checkPreviousFilters(item)) {
if (item.lastRunOutcome === 'Failed') {
this.addToStyleHash(seenJobs, false, this.filterStylingMap, args.column.name);
if (this.filterStack.indexOf(args.column.name) < 0) {
this.filterStack.push(args.column.name);
this.filterValueMap[args.column.name] = [filterValues];
}
// one expansion for the row and one for
// the error detail
seenJobs ++;
i++;
}
seenJobs++;
}
}
}
this.dataView.refresh();
if (this.filterValueMap[args.column.name]) {
this.filterValueMap[args.column.name].push(this.dataView.getFilteredItems());
} else {
this.filterValueMap[args.column.name] = this.dataView.getFilteredItems();
}
this.rowDetail.onBeforeRowDetailToggle.subscribe(function (e, args) {
this._table.grid.resetActiveCell();
}
} else {
this.expandJobs(false);
}
});
this.rowDetail.onAfterRowDetailToggle.subscribe(function (e, args) {
});
this.rowDetail.onAsyncEndUpdate.subscribe(function (e, args) {
this.filterPlugin.onCommand.subscribe((e, args: any) => {
this.columnSort(args.column.name, args.command === 'sort-asc');
});
this._table.registerPlugin(<HeaderFilter>this.filterPlugin);
this.dataView.beginUpdate();
this.dataView.setItems(jobViews);
this.dataView.setFilter((item) => this.filter(item));
this.dataView.endUpdate();
this._table.autosizeColumns();
this._table.resizeCanvas();
let expandedJobs = this._agentViewComponent.expanded;
let expansions = 0;
for (let i = 0; i < jobs.length; i++) {
let job = jobs[i];
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
this.expandJobRowDetails(i + expandedJobs.size);
this.addToStyleHash(i + expandedJobs.size);
this._agentViewComponent.setExpanded(job.jobId, 'Loading Error...');
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
this.expandJobRowDetails(i + expansions);
this.addToStyleHash(i + expansions);
expansions++;
}
}
this.expandJobs(start);
// tooltip for job name
$('.jobview-jobnamerow').hover(e => {
let currentTarget = e.currentTarget;
currentTarget.title = currentTarget.innerText;
@@ -244,7 +354,56 @@ export class JobsViewComponent implements AfterContentChecked {
$('#jobsDiv .jobview-grid .slick-cell.l1.r1 .jobview-jobnametext').css('width', `${nameWidth - 10}px`);
// adjust error message when resized
$('#jobsDiv .jobview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext').css('width', '100%');
// generate job charts again
self.jobs.forEach(job => {
let jobId = job.jobId;
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
let previousRuns = jobHistories.slice(jobHistories.length-5, jobHistories.length);
self.createJobChart(job.jobId, previousRuns);
});
});
$('#jobsDiv .jobview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e) => {
// highlight the error row as well if a failing job row is hovered
if (e.currentTarget.children.item(0).classList.contains('job-with-error')) {
let target = $(e.currentTarget);
let targetChildren = $(e.currentTarget.children);
let siblings = target.nextAll().toArray();
let top = parseInt(target.css('top'), 10);
for (let i = 0; i < siblings.length; i++) {
let sibling = siblings[i];
let siblingTop = parseInt($(sibling).css('top'), 10);
if (siblingTop === top + ROW_HEIGHT) {
$(sibling.children).addClass('hovered');
sibling.onmouseenter = (e) => {
targetChildren.addClass('hovered');
};
sibling.onmouseleave = (e) => {
targetChildren.removeClass('hovered');
}
break;
}
}
}
}, (e) => {
// switch back to original background
if (e.currentTarget.children.item(0).classList.contains('job-with-error')) {
let target = $(e.currentTarget);
let siblings = target.nextAll().toArray();
let top = parseInt(target.css('top'), 10);
for (let i = 0; i < siblings.length; i++) {
let sibling = siblings[i];
let siblingTop = parseInt($(sibling).css('top'), 10);
if (siblingTop === top + ROW_HEIGHT) {
$(sibling.children).removeClass('hovered');
break;
}
}
}
});
// cache the dataview for future use
this._jobCacheObject.dataView = this.dataView;
this.filterValueMap['start'] = [[], this.dataView.getItems()];
this.loadJobHistories();
}
@@ -261,20 +420,34 @@ export class JobsViewComponent implements AfterContentChecked {
'category': errorClass,
'runnable': errorClass,
'hasSchedule': errorClass,
'lastRunOutcome': errorClass
'lastRunOutcome': errorClass,
'previousRuns': errorClass
};
return hash;
}
private addToStyleHash(row: number) {
let hash: {
private addToStyleHash(row: number, start: boolean, map: any, columnName: string) {
let hash : {
[index: number]: {
[id: string]: string;
}
} = {};
hash = this.setRowWithErrorClass(hash, row, 'job-with-error');
hash = this.setRowWithErrorClass(hash, row + 1, 'error-row');
this._table.grid.setCellCssStyles('error-row' + row.toString(), hash);
hash = this.setRowWithErrorClass(hash, row+1, 'error-row');
if (start) {
if (map['start']) {
map['start'].push(['error-row'+row.toString(), hash]);
} else {
map['start'] = [['error-row'+row.toString(), hash]];
}
} else {
if (map[columnName]) {
map[columnName].push(['error-row'+row.toString(), hash]);
} else {
map[columnName] = [['error-row'+row.toString(), hash]];
}
}
this._table.grid.setCellCssStyles('error-row'+row.toString(), hash);
}
private renderName(row, cell, value, columnDef, dataContext) {
@@ -303,6 +476,17 @@ export class JobsViewComponent implements AfterContentChecked {
'</tr></table>';
}
private renderChartsPostHistory(row, cell, value, columnDef, dataContext) {
return `<table class="jobprevruns" id="${dataContext.id}">
<tr>
<td><div class="bar1"></div></td>
<td><div class="bar2"></div></td>
<td><div class="bar3"></div></td>
<td><div class="bar4"></div></td>
</tr>
</table>`;
}
private expandJobRowDetails(rowIdx: number, message?: string): void {
let item = this.dataView.getItemByIdx(rowIdx);
item.message = this._agentViewComponent.expanded.get(item.jobId);
@@ -336,6 +520,17 @@ export class JobsViewComponent implements AfterContentChecked {
return [failing, nonFailing];
}
private checkPreviousFilters(item): boolean {
for (let column in this.filterValueMap) {
if (column !== 'start' && this.filterValueMap[column][0].length > 0) {
if (!_.contains(this.filterValueMap[column][0], item[AgentJobUtilities.convertColNameToField(column)])) {
return false;
}
}
}
return true;
}
private isErrorRow(cell: HTMLElement) {
return cell.classList.contains('error-row');
}
@@ -361,11 +556,14 @@ export class JobsViewComponent implements AfterContentChecked {
if (result && result.jobs) {
self.jobHistories[job.jobId] = result.jobs;
self._jobCacheObject.setJobHistory(job.jobId, result.jobs);
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
let previousRuns = jobHistories.slice(jobHistories.length-5, jobHistories.length);
self.createJobChart(job.jobId, previousRuns);
if (self._agentViewComponent.expanded.has(job.jobId)) {
let jobHistory = self._jobCacheObject.getJobHistory(job.jobId)[result.jobs.length - 1];
let lastJobHistory = jobHistories[result.jobs.length-1];
let item = self.dataView.getItemById(job.jobId + '.error');
let noStepsMessage = nls.localize('jobsView.noSteps', 'No Steps available for this job.');
let errorMessage = jobHistory ? jobHistory.message : noStepsMessage;
let errorMessage = lastJobHistory ? lastJobHistory.message: noStepsMessage;
item['name'] = nls.localize('jobsView.error', 'Error: ') + errorMessage;
self._agentViewComponent.setExpanded(job.jobId, item['name']);
self.dataView.updateItem(job.jobId + '.error', item);
@@ -374,4 +572,216 @@ export class JobsViewComponent implements AfterContentChecked {
});
}
}
private createJobChart(jobId: string, jobHistories: sqlops.AgentJobHistoryInfo[]): void {
let chartHeights = this.getChartHeights(jobHistories);
for (let i = 0; i < jobHistories.length; i++) {
let runGraph = $(`table#${jobId}.jobprevruns > tbody > tr > td > div.bar${i+1}`);
if (jobHistories && jobHistories.length > 0) {
runGraph.css('height', chartHeights[i]);
let bgColor = jobHistories[i].runStatus === 0 ? 'red' : 'green';
runGraph.css('background', bgColor);
runGraph.hover((e) => {
let currentTarget = e.currentTarget;
currentTarget.title = jobHistories[i].runDuration;
});
} else {
runGraph.css('height', '5px');
runGraph.css('background', 'red');
runGraph.hover((e) => {
let currentTarget = e.currentTarget;
currentTarget.title = 'Job not run.';
});
}
}
}
// chart height normalization logic
private getChartHeights(jobHistories: sqlops.AgentJobHistoryInfo[]): string[] {
if (!jobHistories || jobHistories.length === 0) {
return ['5px','5px','5px','5px','5px'];
}
let maxDuration: number = 0;
jobHistories.forEach(history => {
let historyDuration = AgentJobUtilities.convertDurationToSeconds(history.runDuration) ;
if (historyDuration > maxDuration) {
maxDuration = historyDuration;
}
});
maxDuration = maxDuration === 0 ? 1 : maxDuration;
let maxBarHeight: number = 24;
let chartHeights = [];
for (let i = 0; i < jobHistories.length; i++) {
let duration = jobHistories[i].runDuration;
let chartHeight = (maxBarHeight * AgentJobUtilities.convertDurationToSeconds(duration))/maxDuration;
chartHeights.push(`${chartHeight}px`);
}
return chartHeights;
}
private expandJobs(start: boolean): void {
if (start) {
this._agentViewComponent.expanded = new Map<string, string>();
}
let expandedJobs = this._agentViewComponent.expanded;
let expansions = 0;
for (let i = 0; i < this.jobs.length; i++){
let job = this.jobs[i];
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
this.expandJobRowDetails(i+expandedJobs.size);
this.addToStyleHash(i+expandedJobs.size, start, this.filterStylingMap, undefined);
this._agentViewComponent.setExpanded(job.jobId, 'Loading Error...');
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
this.addToStyleHash(i+expansions, start, this.filterStylingMap, undefined);
expansions++;
}
}
}
private filter(item: any) {
let columns = this._table.grid.getColumns();
let value = true;
for (let i = 0; i < columns.length; i++) {
let col: any = columns[i];
let filterValues = col.filterValues;
if (filterValues && filterValues.length > 0) {
if (item._parent) {
value = value && _.contains(filterValues, item._parent[col.field]);
} else {
value = value && _.contains(filterValues, item[col.field]);
}
}
}
return value;
}
private columnSort(column: string, isAscending: boolean) {
let items = this.dataView.getItems();
// get error items here and remove them
let jobItems = items.filter(x => x._parent === undefined);
let errorItems = items.filter(x => x._parent !== undefined);
this.sortingStylingMap[column] = items;
switch(column) {
case('Name'): {
this.dataView.setItems(jobItems);
// sort the actual jobs
this.dataView.sort((item1, item2) => {
return item1.name.localeCompare(item2.name);
}, isAscending);
break;
}
case('Last Run'): {
this.dataView.setItems(jobItems);
// sort the actual jobs
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, true), isAscending);
break;
}
case ('Next Run') : {
this.dataView.setItems(jobItems);
// sort the actual jobs
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, false), isAscending);
break;
}
case ('Enabled'): {
this.dataView.setItems(jobItems);
// sort the actual jobs
this.dataView.sort((item1, item2) => {
return item1.enabled.localeCompare(item2.enabled);
}, isAscending);
break;
}
case ('Status'): {
this.dataView.setItems(jobItems);
// sort the actual jobs
this.dataView.sort((item1, item2) => {
return item1.currentExecutionStatus.localeCompare(item2.currentExecutionStatus);
}, isAscending);
break;
}
case ('Category'): {
this.dataView.setItems(jobItems);
// sort the actual jobs
this.dataView.sort((item1, item2) => {
return item1.category.localeCompare(item2.category);
}, isAscending);
break;
}
case ('Runnable'): {
this.dataView.setItems(jobItems);
// sort the actual jobs
this.dataView.sort((item1, item2) => {
return item1.runnable.localeCompare(item2.runnable);
}, isAscending);
break;
}
case ('Schedule'): {
this.dataView.setItems(jobItems);
// sort the actual jobs
this.dataView.sort((item1, item2) => {
return item1.hasSchedule.localeCompare(item2.hasSchedule);
}, isAscending);
break;
}
case ('Last Run Outcome'): {
this.dataView.setItems(jobItems);
// sort the actual jobs
this.dataView.sort((item1, item2) => {
return item1.lastRunOutcome.localeCompare(item2.lastRunOutcome);
}, isAscending);
break;
}
}
// insert the errors back again
let jobItemsLength = jobItems.length;
for (let i = 0; i < jobItemsLength; i++) {
let item = jobItems[i];
if (item._child) {
let child = errorItems.find(error => error === item._child);
jobItems.splice(i+1, 0, child);
jobItemsLength++;
}
}
this.dataView.setItems(jobItems);
// remove old style
if (this.filterStylingMap[column]) {
let filterLength = this.filterStylingMap[column].length;
for (let i = 0; i < filterLength; i++) {
let lastAppliedStyle = this.filterStylingMap[column].pop();
this._table.grid.removeCellCssStyles(lastAppliedStyle[0]);
}
} else {
for (let i = 0; i < this.jobs.length; i++) {
this._table.grid.removeCellCssStyles('error-row'+i.toString());
}
}
// add new style to the items back again
items = this.filterStack.length > 1 ? this.dataView.getFilteredItems() : this.dataView.getItems();
for (let i = 0; i < items.length; i ++) {
let item = items[i];
if (item.lastRunOutcome === 'Failed') {
this.addToStyleHash(i, false, this.sortingStylingMap, column);
}
}
}
private dateCompare(item1: any, item2: any, lastRun: boolean): number {
let exceptionString = lastRun ? 'Never Run' : 'Not Scheduled';
if (item2.lastRun === exceptionString && item1.lastRun !== exceptionString) {
return -1;
} else if (item1.lastRun === exceptionString && item2.lastRun !== exceptionString) {
return 1;
} else if (item1.lastRun === exceptionString && item2.lastRun === exceptionString) {
return 0;
} else {
let date1 = new Date(item1.lastRun);
let date2 = new Date(item2.lastRun);
if (date1 > date2) {
return 1;
} else if (date1 === date2) {
return 0;
} else {
return -1;
}
}
}
}

View File

@@ -15,7 +15,7 @@ import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } fro
import { attachButtonStyler } from 'sql/common/theme/styler';
import { Button } from 'sql/base/browser/ui/button/button';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { SIDE_BAR_BACKGROUND, SIDE_BAR_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import URI from 'vs/base/common/uri';
@@ -55,13 +55,11 @@ export default class ButtonComponent extends ComponentBase implements IComponent
ngAfterViewInit(): void {
if (this._inputContainer) {
this._button = new Button(this._inputContainer.nativeElement);
this._register(this._button);
this._register(attachButtonStyler(this._button, this.themeService, {
buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND
buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND, buttonForeground: SIDE_BAR_TITLE_FOREGROUND
}));
this._register(this._button.onDidClick(e => {
this._onEventEmitter.fire({
@@ -94,6 +92,12 @@ export default class ButtonComponent extends ComponentBase implements IComponent
super.setProperties(properties);
this._button.enabled = this.enabled;
this._button.label = this.label;
if (this.width) {
this._button.setWidth(this.convertSize(this.width.toString()));
}
if (this.height) {
this._button.setWidth(this.convertSize(this.height.toString()));
}
this.updateIcon();
}

View File

@@ -20,7 +20,7 @@ import { attachInputBoxStyler, attachListStyler } from 'vs/platform/theme/common
@Component({
selector: 'modelview-checkbox',
template: `
<div #input style="width: 100%"></div>
<div #input [style.width]="getWidth()"></div>
`
})
export default class CheckBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@@ -50,7 +50,7 @@ export default class CheckBoxComponent extends ComponentBase implements ICompone
this._register(this._input);
this._register(this._input.onChange(e => {
this.value = this._input.checked;
this.checked = this._input.checked;
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidChange,
args: e
@@ -88,10 +88,10 @@ export default class CheckBoxComponent extends ComponentBase implements ICompone
// CSS-bound properties
public get checked(): boolean {
return this.getPropertyOrDefault<sqlops.CheckBoxProperties, boolean>((props) => props.value, false);
return this.getPropertyOrDefault<sqlops.CheckBoxProperties, boolean>((props) => props.checked, false);
}
public set value(newValue: boolean) {
public set checked(newValue: boolean) {
this.setPropertyFromUI<sqlops.CheckBoxProperties, boolean>((properties, value) => { properties.checked = value; }, newValue);
}

View File

@@ -12,7 +12,7 @@ import {
import * as types from 'vs/base/common/types';
import { IComponent, IComponentDescriptor, IModelStore, IComponentEventArgs, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
import { FlexLayout, FlexItemLayout } from 'sqlops';
import * as sqlops from 'sqlops';
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { Event, Emitter } from 'vs/base/common/event';
@@ -112,6 +112,52 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
this.setProperties(properties);
}
public get height(): number | string {
return this.getPropertyOrDefault<sqlops.ComponentProperties, number | string>((props) => props.height, undefined);
}
public set height(newValue: number | string) {
this.setPropertyFromUI<sqlops.ComponentProperties, number | string>((props, value) => props.height = value, newValue);
}
public get width(): number | string {
return this.getPropertyOrDefault<sqlops.ComponentProperties, number | string>((props) => props.width, undefined);
}
public set width(newValue: number | string) {
this.setPropertyFromUI<sqlops.ComponentProperties, number | string>((props, value) => props.width = value, newValue);
}
protected convertSizeToNumber(size: number | string): number {
if (size && typeof (size) === 'string') {
if (size.toLowerCase().endsWith('px')) {
return +size.replace('px', '');
}
return 0;
}
return +size;
}
protected getWidth(): string {
return this.width ? this.convertSize(this.width) : '';
}
protected getHeight(): string {
return this.height ? this.convertSize(this.height) : '';
}
protected convertSize(size: number | string, defaultValue?: string): string {
defaultValue = defaultValue || '';
if (types.isUndefinedOrNull(size)) {
return defaultValue;
}
let convertedSize: string = size ? size.toString() : defaultValue;
if (!convertedSize.toLowerCase().endsWith('px') && !convertedSize.toLowerCase().endsWith('%')) {
convertedSize = convertedSize + 'px';
}
return convertedSize;
}
public get valid(): boolean {
return this._valid;
}

View File

@@ -10,6 +10,8 @@ import GroupContainer from './groupContainer.component';
import CardComponent from './card.component';
import InputBoxComponent from './inputbox.component';
import DropDownComponent from './dropdown.component';
import DeclarativeTableComponent from './declarativeTable.component';
import ListBoxComponent from './listbox.component';
import ButtonComponent from './button.component';
import CheckBoxComponent from './checkbox.component';
import RadioButtonComponent from './radioButton.component';
@@ -41,6 +43,12 @@ registerComponentType(INPUTBOX_COMPONENT, ModelComponentTypes.InputBox, InputBox
export const DROPDOWN_COMPONENT = 'dropdown-component';
registerComponentType(DROPDOWN_COMPONENT, ModelComponentTypes.DropDown, DropDownComponent);
export const DECLARATIVETABLE_COMPONENT = 'declarativeTable-component';
registerComponentType(DECLARATIVETABLE_COMPONENT, ModelComponentTypes.DeclarativeTable, DeclarativeTableComponent);
export const LISTBOX_COMPONENT = 'lisbox-component';
registerComponentType(LISTBOX_COMPONENT, ModelComponentTypes.ListBox, ListBoxComponent);
export const BUTTON_COMPONENT = 'button-component';
registerComponentType(BUTTON_COMPONENT, ModelComponentTypes.Button, ButtonComponent);

View File

@@ -0,0 +1,194 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./declarativeTable';
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
} from '@angular/core';
import * as sqlops from 'sqlops';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { Event, Emitter } from 'vs/base/common/event';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
import * as nls from 'vs/nls';
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
}
@Component({
selector: 'modelview-declarativeTable',
template: `
<table role=grid aria-labelledby="ID_REF" #container *ngIf="columns" class="declarative-table">
<ng-container *ngFor="let column of columns;let h = index">
<th class="declarative-table-header" tabindex="-1" role="button" aria-sort="none">{{column.displayName}}</th>
</ng-container>
<ng-container *ngIf="data">
<ng-container *ngFor="let row of data;let r = index">
<tr class="declarative-table-row">
<ng-container *ngFor="let cellData of row;let c = index">
<td class="declarative-table-cell" tabindex="-1" role="button" [style.width]="getColumnWidth(c)">
<checkbox *ngIf="isCheckBox(c)" label="" (onChange)="onCheckBoxChanged($event,r,c)" [enabled]="isControlEnabled(c)" [checked]="isChecked(r,c)"></checkbox>
<select-box *ngIf="isSelectBox(c)" [options]="GetOptions(c)" (onDidSelect)="onSelectBoxChanged($event,r,c)" [selectedOption]="GetSelectedOptionDisplayName(r,c)"></select-box>
<input-box *ngIf="isInputBox(c)" [value]="cellData" (onDidChange)="onInputBoxChanged($event,r,c)"></input-box>
<ng-container *ngIf="isLabel(c)" >{{cellData}}</ng-container>
</td>
</ng-container>
</tr>
</ng-container>
</ng-container>
</table>
`
})
export default class DeclarativeTableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
@ViewChild('container', { read: ElementRef }) private _tableContainer: ElementRef;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@Inject(IContextViewService) private contextViewService: IContextViewService
) {
super(changeRef);
}
ngOnInit(): void {
this.baseInit();
}
ngAfterViewInit(): void {
}
public validate(): Thenable<boolean> {
return super.validate().then(valid => {
return valid;
});
}
ngOnDestroy(): void {
this.baseDestroy();
}
private isCheckBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.boolean;
}
private isControlEnabled(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return !column.isReadOnly;
}
private isLabel(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.isReadOnly && column.valueType === DeclarativeDataType.string;
}
private isChecked(row: number, cell: number): boolean {
let cellData = this.data[row][cell];
return cellData;
}
private onInputBoxChanged(e: string, row: number, cell: number): void {
this.onCellDataChanged(e, row, cell);
}
private onCheckBoxChanged(e: boolean, row: number, cell: number): void {
this.onCellDataChanged(e, row, cell);
}
private onSelectBoxChanged(e: ISelectData, row: number, cell: number): void {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
if (column.categoryValues) {
this.onCellDataChanged(column.categoryValues[e.index].name, row, cell);
}
}
private onCellDataChanged(newValue: any, row: number, cell: number): void {
this.data[row][cell] = newValue;
this.data = this.data;
let newCellData : sqlops.TableCell = {
row: row,
column: cell,
value: newValue
};
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidChange,
args: newCellData
});
}
private isSelectBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.category;
}
private isInputBox(cell: number): boolean {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.valueType === DeclarativeDataType.string && !column.isReadOnly;
}
private getColumnWidth(cell: number): string {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return this.convertSize(column.width);
}
private GetOptions(cell: number): string[] {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
return column.categoryValues ? column.categoryValues.map(x => x.displayName) : [];
}
private GetSelectedOptionDisplayName(row: number, cell: number): string {
let column: sqlops.DeclarativeTableColumn = this.columns[cell];
let cellData = this.data[row][cell];
if (cellData && column.categoryValues) {
let category = column.categoryValues.find(v => v.name === cellData);
return category.displayName;
} else {
return '';
}
}
/// IComponent implementation
public layout(): void {
this._changeRef.detectChanges();
}
public setLayout(layout: any): void {
// TODO allow configuring the look and feel
this.layout();
}
public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
}
public get data(): any[][] {
return this.getPropertyOrDefault<sqlops.DeclarativeTableProperties, any[]>((props) => props.data, []);
}
public set data(newValue: any[][]) {
this.setPropertyFromUI<sqlops.DeclarativeTableProperties, any[][]>((props, value) => props.data = value, newValue);
}
public get columns(): sqlops.DeclarativeTableColumn[] {
return this.getPropertyOrDefault<sqlops.DeclarativeTableProperties, sqlops.DeclarativeTableColumn[]>((props) => props.columns, []);
}
public set columns(newValue: sqlops.DeclarativeTableColumn[]) {
this.setPropertyFromUI<sqlops.DeclarativeTableProperties, sqlops.DeclarativeTableColumn[]>((props, value) => props.columns = value, newValue);
}
}

View File

@@ -0,0 +1,41 @@
.declarative-table {
padding: 5px 30px 0px 30px;
box-sizing: border-box;
width:100%;
border-collapse: collapse;
}
.declarative-table-row {
}
.declarative-table-header {
padding: 5px;
border: 1px solid gray;
background-color: #F5F5F5;
}
.vs-dark .declarative-table-header {
padding: 5px;
border: 1px solid gray;
background-color: #333334;
}
.hc-black .declarative-table-header {
padding: 5px;
border: 1px solid gray;
background-color: #333334;
}
.declarative-table-cell {
padding: 5px;
border: 1px solid gray;
}
[role="gridcell"]:focus,
[role="gridcell"] *:focus,
[role="grid"] [tabindex="0"]:focus {
outline: #005a9c;
outline-style: dotted;
outline-width: 3px;
}

View File

@@ -15,7 +15,8 @@ import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } fro
import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { attachEditableDropdownStyler , attachSelectBoxStyler} from 'sql/common/theme/styler';
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
@@ -26,7 +27,7 @@ import { attachListStyler } from 'vs/platform/theme/common/styler';
selector: 'modelview-dropdown',
template: `
<div>
<div [style.width]="getWidth()">
<div [style.display]="getEditableDisplay()" #editableDropDown style="width: 100%;"></div>
<div [style.display]="getNotEditableDisplay()" #dropDown style="width: 100%;"></div>
</div>
@@ -68,7 +69,7 @@ export default class DropDownComponent extends ComponentBase implements ICompone
this._register(attachEditableDropdownStyler(this._editableDropdown, this.themeService));
this._register(this._editableDropdown.onValueChange(e => {
if (this.editable) {
this.value = this._editableDropdown.value;
this.setSelectedValue(this._editableDropdown.value);
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidChange,
args: e
@@ -77,14 +78,14 @@ export default class DropDownComponent extends ComponentBase implements ICompone
}));
}
if (this._dropDownContainer) {
this._selectBox = new SelectBox(this.values || [], this.value, this.contextViewService, this._dropDownContainer.nativeElement);
this._selectBox = new SelectBox(this.getValues(), this.getSelectedValue(), this.contextViewService, this._dropDownContainer.nativeElement);
this._selectBox.render(this._dropDownContainer.nativeElement);
this._register(this._selectBox);
this._register(attachSelectBoxStyler(this._selectBox, this.themeService));
this._register(this._selectBox.onDidSelect(e => {
if (!this.editable) {
this.value = this._selectBox.value;
this.setSelectedValue(this._selectBox.value);
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidChange,
args: e
@@ -112,14 +113,14 @@ export default class DropDownComponent extends ComponentBase implements ICompone
public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
if (this.editable) {
this._editableDropdown.values = this.values ? this.values : [];
this._editableDropdown.values = this.getValues();
if (this.value) {
this._editableDropdown.value = this.value;
this._editableDropdown.value = this.getSelectedValue();
}
this._editableDropdown.enabled = this.enabled;
} else {
this._selectBox.setOptions(this.values || []);
this._selectBox.selectWithOptionName(this.value);
this._selectBox.setOptions(this.getValues());
this._selectBox.selectWithOptionName(this.getSelectedValue());
if (this.enabled) {
this._selectBox.enable();
} else {
@@ -128,34 +129,75 @@ export default class DropDownComponent extends ComponentBase implements ICompone
}
}
private getValues(): string[] {
if (this.values && this.values.length > 0) {
if (!this.valuesHaveDisplayName()) {
return this.values as string[];
} else {
return (<sqlops.CategoryValue[]>this.values).map(v => v.displayName);
}
}
return [];
}
private valuesHaveDisplayName(): boolean {
return typeof (this.values[0]) !== 'string';
}
private getSelectedValue(): string {
if (this.values && this.values.length > 0 && this.valuesHaveDisplayName()) {
let selectedValue = <sqlops.CategoryValue>this.value || <sqlops.CategoryValue>this.values[0];
if (!this.value) {
this.value = selectedValue;
}
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.name === selectedValue.name);
return valueCategory && valueCategory.displayName;
} else {
if (!this.value && this.values && this.values.length > 0) {
this.value = <string>this.values[0];
}
return <string>this.value;
}
}
private setSelectedValue(newValue: string): void {
if (this.values && this.valuesHaveDisplayName()) {
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.displayName === newValue);
this.value = valueCategory;
} else {
this.value = newValue;
}
}
// CSS-bound properties
private get value(): string {
return this.getPropertyOrDefault<sqlops.DropDownProperties, string>((props) => props.value, '');
private get value(): string | sqlops.CategoryValue {
return this.getPropertyOrDefault<sqlops.DropDownProperties, string | sqlops.CategoryValue>((props) => props.value, '');
}
private get editable(): boolean {
return this.getPropertyOrDefault<sqlops.DropDownProperties, boolean>((props) => props.editable, false);
}
public getEditableDisplay() : string {
public getEditableDisplay(): string {
return this.editable ? '' : 'none';
}
public getNotEditableDisplay() : string {
public getNotEditableDisplay(): string {
return !this.editable ? '' : 'none';
}
private set value(newValue: string) {
this.setPropertyFromUI<sqlops.DropDownProperties, string>(this.setValueProperties, newValue);
private set value(newValue: string | sqlops.CategoryValue) {
this.setPropertyFromUI<sqlops.DropDownProperties, string | sqlops.CategoryValue>(this.setValueProperties, newValue);
}
private get values(): string[] {
return this.getPropertyOrDefault<sqlops.DropDownProperties, string[]>((props) => props.values, undefined);
private get values(): string[] | sqlops.CategoryValue[] {
return this.getPropertyOrDefault<sqlops.DropDownProperties, string[] | sqlops.CategoryValue[]>((props) => props.values, undefined);
}
private set values(newValue: string[]) {
this.setPropertyFromUI<sqlops.DropDownProperties, string[]>(this.setValuesProperties, newValue);
private set values(newValue: string[] | sqlops.CategoryValue[]) {
this.setPropertyFromUI<sqlops.DropDownProperties, string[] | sqlops.CategoryValue[]>(this.setValuesProperties, newValue);
}
private setValueProperties(properties: sqlops.DropDownProperties, value: string): void {

View File

@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./flexContainer';
import { Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList,
} from '@angular/core';
@@ -18,14 +19,14 @@ import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentW
import types = require('vs/base/common/types');
class FlexItem {
constructor(public descriptor: IComponentDescriptor, public config: FlexItemLayout) {}
constructor(public descriptor: IComponentDescriptor, public config: FlexItemLayout) { }
}
@Component({
template: `
<div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent"
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height">
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.order]="getItemOrder(item)" >
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height" [style.width]="width">
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.textAlign]="textAlign" [style.order]="getItemOrder(item)" >
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
</model-component-wrapper>
</div>
@@ -39,7 +40,9 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
private _justifyContent: string;
private _alignItems: string;
private _alignContent: string;
private _textAlign: string;
private _height: string;
private _width: string;
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
super(changeRef);
@@ -58,18 +61,15 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
/// IComponent implementation
public setLayout (layout: FlexLayout): void {
public setLayout(layout: FlexLayout): void {
this._flexFlow = layout.flexFlow ? layout.flexFlow : '';
this._justifyContent = layout.justifyContent ? layout.justifyContent : '';
this._alignItems = layout.alignItems ? layout.alignItems : '';
this._alignContent = layout.alignContent ? layout.alignContent : '';
if (types.isUndefinedOrNull(layout.height)) {
this._height = '';
} else if (types.isNumber(layout.height)) {
this._height = layout.height + 'px';
} else {
this._height = layout.height;
}
this._textAlign = layout.textAlign ? layout.textAlign : '';
this._height = this.convertSize(layout.height);
this._width = this.convertSize(layout.width);
this.layout();
}
@@ -90,10 +90,18 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
return this._height;
}
public get width(): string {
return this._width;
}
public get alignContent(): string {
return this._alignContent;
}
public get textAlign(): string {
return this._textAlign;
}
private getItemFlex(item: FlexItem): string {
return item.config ? item.config.flex : '1 1 auto';
}

View File

@@ -16,13 +16,15 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { getContentHeight, getContentWidth, Dimension } from 'vs/base/browser/dom';
export interface TitledFormItemLayout {
title: string;
actions?: string[];
isFormComponent: Boolean;
horizontal: boolean;
componentWidth: number;
componentWidth?: number | string;
componentHeight?: number | string;
}
export interface FormLayout {
@@ -35,10 +37,10 @@ class FormItem {
@Component({
template: `
<div #container *ngIf="items" class="form-table" [style.width]="getFormWidth()">
<div #container *ngIf="items" class="form-table" [style.width]="getFormWidth()" [style.height]="getFormHeight()">
<ng-container *ngFor="let item of items">
<div class="form-row" >
<ng-container *ngIf="isFormComponent(item)">
<div class="form-row" *ngIf="isFormComponent(item)" [style.height]="getRowHeight(item)">
<ng-container *ngIf="isHorizontal(item)">
<div class="form-cell">{{getItemTitle(item)}}</div>
<div class="form-cell">
@@ -56,10 +58,10 @@ class FormItem {
</div>
</div>
</ng-container>
<div class="form-vertical-container" *ngIf="isVertical(item)">
<div class="form-vertical-container" *ngIf="isVertical(item)" [style.height]="getRowHeight(item)">
<div class="form-item-row">{{getItemTitle(item)}}</div>
<div class="form-item-row" [style.width]="getComponentWidth(item)">
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)">
<div class="form-item-row" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
</model-component-wrapper>
</div>
<div *ngIf="itemHasActions(item)" class="form-item-row form-actions-table form-item-last-row">
@@ -69,7 +71,6 @@ class FormItem {
</div>
</div>
</div>
</ng-container>
</div>
</ng-container>
</div>
@@ -102,6 +103,10 @@ export default class FormContainer extends ContainerBase<FormItemLayout> impleme
ngAfterViewInit(): void {
}
public layout(): void {
super.layout();
}
/// IComponent implementation
public get alignItems(): string {
@@ -113,12 +118,21 @@ export default class FormContainer extends ContainerBase<FormItemLayout> impleme
}
private getFormWidth(): string {
return this._formLayout && this._formLayout.width ? +this._formLayout.width + 'px' : '100%';
return this.convertSize(this._formLayout && this._formLayout.width, '');
}
private getFormHeight(): string {
return this.convertSize(this._formLayout && this._formLayout.height, '');
}
private getComponentWidth(item: FormItem): string {
let itemConfig = item.config;
return (itemConfig && itemConfig.componentWidth) ? itemConfig.componentWidth + 'px' : '';
return (itemConfig && itemConfig.componentWidth) ? this.convertSize(itemConfig.componentWidth, '') : '';
}
private getRowHeight(item: FormItem): string {
let itemConfig = item.config;
return (itemConfig && itemConfig.componentHeight) ? this.convertSize(itemConfig.componentHeight, '') : '';
}
private getItemTitle(item: FormItem): string {

View File

@@ -20,19 +20,23 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { Event, Emitter } from 'vs/base/common/event';
import * as nls from 'vs/nls';
import { TextAreaInput } from 'vs/editor/browser/controller/textAreaInput';
@Component({
selector: 'modelview-inputBox',
template: `
<div #input style="width: 100%"></div>
<div [style.display]="getInputBoxDisplay()" #input style="width: 100%"></div>
<div [style.display]="getTextAreaDisplay()" #textarea style="width: 100%"></div>
`
})
export default class InputBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
private _input: InputBox;
private _textAreaInput: InputBox;
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
@ViewChild('textarea', { read: ElementRef }) private _textareaContainer: ElementRef;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@@ -43,47 +47,71 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
ngOnInit(): void {
this.baseInit();
}
ngAfterViewInit(): void {
if (this._inputContainer) {
let inputOptions: IInputOptions = {
placeholder: '',
ariaLabel: '',
validationOptions: {
validation: () => {
if (this.valid) {
return undefined;
} else {
return {
content: this._input.inputElement.validationMessage || nls.localize('invalidValueError', 'Invalid value'),
type: MessageType.ERROR
};
}
let inputOptions: IInputOptions = {
placeholder: '',
ariaLabel: '',
validationOptions: {
validation: () => {
if (this.valid) {
return undefined;
} else {
return {
content: this.inputElement.inputElement.validationMessage || nls.localize('invalidValueError', 'Invalid value'),
type: MessageType.ERROR
};
}
},
useDefaultValidation: true
};
}
},
useDefaultValidation: true
};
if (this._inputContainer) {
this._input = new InputBox(this._inputContainer.nativeElement, this.contextViewService, inputOptions);
this._validations.push(() => !this._input.inputElement.validationMessage);
this.registerInput(this._input, () => !this.multiline);
this._register(this._input);
this._register(attachInputBoxStyler(this._input, this.themeService));
this._register(this._input.onDidChange(e => {
this.value = this._input.value;
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidChange,
args: e
});
}
if (this._textareaContainer) {
let textAreaInputOptions = Object.assign({}, inputOptions, { flexibleHeight: true, type: 'textarea' });
this._textAreaInput = new InputBox(this._textareaContainer.nativeElement, this.contextViewService, textAreaInputOptions);
this.registerInput(this._textAreaInput, () => this.multiline);
}
}
private get inputElement(): InputBox {
return this.multiline ? this._textAreaInput : this._input;
}
private registerInput(input: InputBox, checkOption: () => boolean): void {
if (input) {
this._validations.push(() => !input.inputElement.validationMessage);
this._register(input);
this._register(attachInputBoxStyler(input, this.themeService));
this._register(input.onDidChange(e => {
if (checkOption()) {
this.value = input.value;
this._onEventEmitter.fire({
eventType: ComponentEventType.onDidChange,
args: e
});
}
}));
}
}
public getInputBoxDisplay(): string {
return !this.multiline ? '' : 'none';
}
public getTextAreaDisplay(): string {
return this.multiline ? '' : 'none';
}
public validate(): Thenable<boolean> {
return super.validate().then(valid => {
this._input.validate();
this.inputElement.validate();
return valid;
});
}
@@ -96,6 +124,16 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
public layout(): void {
this._changeRef.detectChanges();
this.layoutInputBox();
}
private layoutInputBox(): void {
if (this.width) {
this.inputElement.width = this.convertSizeToNumber(this.width);
}
if (this.height) {
this.inputElement.setHeight(this.convertSize(this.height));
}
}
public setLayout(layout: any): void {
@@ -105,21 +143,41 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
this._input.inputElement.type = this.inputType;
if (this.inputType === 'number') {
this._input.inputElement.step = 'any';
}
this._input.value = this.value;
this._input.setAriaLabel(this.ariaLabel);
this._input.setPlaceHolder(this.placeHolder);
this._input.setEnabled(this.enabled);
if (this.width) {
this._input.width = this.width;
}
this._input.inputElement.required = this.required;
this.setInputProperties(this.inputElement);
this.validate();
}
private setInputProperties(input: InputBox): void {
if (!this.multiline) {
input.inputElement.type = this.inputType;
if (this.inputType === 'number') {
input.inputElement.step = 'any';
if (this.min) {
input.inputElement.min = this.min.toString();
}
if (this.max) {
input.inputElement.max = this.max.toString();
}
}
}
input.value = this.value;
input.setAriaLabel(this.ariaLabel);
input.setPlaceHolder(this.placeHolder);
input.setEnabled(this.enabled);
this.layoutInputBox();
if (this.multiline) {
if (this.rows) {
this.inputElement.rows = this.rows;
}
if (this.columns) {
this.inputElement.columns = this.columns;
}
}
input.inputElement.required = this.required;
}
// CSS-bound properties
public get value(): string {
@@ -146,20 +204,36 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
this.setPropertyFromUI<sqlops.InputBoxProperties, string>((props, value) => props.placeHolder = value, newValue);
}
public get height(): number {
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.height, undefined);
public set columns(newValue: number) {
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.columns = value, newValue);
}
public set height(newValue: number) {
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.height = value, newValue);
public get rows(): number {
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.rows, undefined);
}
public get width(): number {
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.width, undefined);
public get columns(): number {
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.columns, undefined);
}
public set width(newValue: number) {
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.width = value, newValue);
public set rows(newValue: number) {
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.rows = value, newValue);
}
public get min(): number {
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.min, undefined);
}
public set min(newValue: number) {
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.min = value, newValue);
}
public get max(): number {
return this.getPropertyOrDefault<sqlops.InputBoxProperties, number>((props) => props.max, undefined);
}
public set max(newValue: number) {
this.setPropertyFromUI<sqlops.InputBoxProperties, number>((props, value) => props.max = value, newValue);
}
public get inputType(): string {
@@ -170,6 +244,14 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
this.setPropertyFromUI<sqlops.InputBoxProperties, string>((props, value) => props.inputType = value, newValue);
}
public get multiline(): boolean {
return this.getPropertyOrDefault<sqlops.InputBoxProperties, boolean>((props) => props.multiline, false);
}
public set multiline(newValue: boolean) {
this.setPropertyFromUI<sqlops.InputBoxProperties, boolean>((props, value) => props.multiline = value, newValue);
}
public get required(): boolean {
return this.getPropertyOrDefault<sqlops.InputBoxProperties, boolean>((props) => props.required, false);
}

View File

@@ -0,0 +1,112 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import {
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
} from '@angular/core';
import * as sqlops from 'sqlops';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
import { ListBox } from 'sql/base/browser/ui/listBox/listBox';
import { attachListBoxStyler } from 'sql/common/theme/styler';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { Emitter } from 'vs/base/common/event';
import * as nls from 'vs/nls';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
@Component({
selector: 'modelview-listBox',
template: `
<div #input style="width: 100%"></div>
`
})
export default class ListBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
private _input: ListBox;
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@Inject(IContextViewService) private contextViewService: IContextViewService,
@Inject(IClipboardService) private clipboardService: IClipboardService
) {
super(changeRef);
}
ngOnInit(): void {
this.baseInit();
}
ngAfterViewInit(): void {
if (this._inputContainer) {
this._input = new ListBox([], undefined, this.contextViewService, this.clipboardService);
this._input.render(this._inputContainer.nativeElement);
this._register(this._input);
this._register(attachListBoxStyler(this._input, this.themeService));
this._register(this._input.onDidSelect(e => {
this.selectedRow = e.index;
this._onEventEmitter.fire({
eventType: ComponentEventType.onSelectedRowChanged,
args: e
});
}));
}
}
public validate(): Thenable<boolean> {
return super.validate().then(valid => {
return valid;
});
}
ngOnDestroy(): void {
this.baseDestroy();
}
/// IComponent implementation
public layout(): void {
this._changeRef.detectChanges();
}
public setLayout(layout: any): void {
// TODO allow configuring the look and feel
this.layout();
}
public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
this._input.setOptions(this.values, this.selectedRow);
this.validate();
}
// CSS-bound properties
private get values(): string[] {
return this.getPropertyOrDefault<sqlops.ListBoxProperties, string[]>((props) => props.values, undefined);
}
private set values(newValue: string[]) {
this.setPropertyFromUI<sqlops.ListBoxProperties, string[]>((props, value) => props.values = value, newValue);
}
private get selectedRow(): number {
return this.getPropertyOrDefault<sqlops.ListBoxProperties, number>((props) => props.selectedRow, undefined);
}
private set selectedRow(newValue: number) {
this.setPropertyFromUI<sqlops.ListBoxProperties, number>((props, value) => props.selectedRow = value, newValue);
}
}

View File

@@ -25,10 +25,18 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { memoize } from 'vs/base/common/decorators';
import { generateUuid } from 'vs/base/common/uuid';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { Event, Emitter } from 'vs/base/common/event';
import * as nls from 'vs/nls';
const componentRegistry = <IComponentRegistry>Registry.as(Extensions.ComponentContribution);
export interface ModelComponentParams extends IBootstrapParams {
onLayoutRequested: Event<string>;
modelViewId: string;
}
@Component({
selector: 'model-component-wrapper',
template: `
@@ -46,6 +54,7 @@ export class ModelComponentWrapper extends AngularDisposable implements OnInit {
}
private _componentInstance: IComponent;
private _modelViewId: string;
@ViewChild(ComponentHostDirective) componentHost: ComponentHostDirective;
@@ -54,9 +63,18 @@ export class ModelComponentWrapper extends AngularDisposable implements OnInit {
@Inject(forwardRef(() => ElementRef)) private _ref: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeref: ChangeDetectorRef,
@Inject(forwardRef(() => Injector)) private _injector: Injector,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@Inject(IBootstrapParams) private _params: ModelComponentParams
) {
super();
if (_params && _params.onLayoutRequested) {
this._modelViewId = _params.modelViewId;
_params.onLayoutRequested(modelViewId => {
if (modelViewId === this._modelViewId) {
this.layout();
}
});
}
}
ngOnInit() {

View File

@@ -62,6 +62,7 @@ export class ModelViewContent extends ViewBase implements OnInit, IModelView {
}
public layout(): void {
this.changeRef.detectChanges();
}
public get id(): string {

View File

@@ -23,7 +23,7 @@ import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectio
@Component({
selector: 'modelview-table',
template: `
<div #table style="width: 100%"></div>
<div #table style="width: 100%;height:100%"></div>
`
})
export default class TableComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
@@ -92,9 +92,8 @@ export default class TableComponent extends ComponentBase implements IComponent,
let options = <Slick.GridOptions<any>>{
syncColumnCellResize: true,
enableColumnReorder: false,
rowHeight: 45,
enableCellNavigation: true,
forceFitColumns: true
forceFitColumns: true,
};
this._table = new Table<Slick.SlickData>(this._inputContainer.nativeElement, this._tableData, this._tableColumns, options);
@@ -127,13 +126,19 @@ export default class TableComponent extends ComponentBase implements IComponent,
/// IComponent implementation
public layout(): void {
this._table.layout(new Dimension(
this.width ? this.width : getContentWidth(this._inputContainer.nativeElement),
this.height ? this.height : getContentHeight(this._inputContainer.nativeElement)));
this.layoutTable();
this._changeRef.detectChanges();
}
private layoutTable(): void {
let width: number = this.convertSizeToNumber(this.width);
let height: number = this.convertSizeToNumber(this.height);
this._table.layout(new Dimension(
width && width > 0 ? width : getContentWidth(this._inputContainer.nativeElement),
height && height > 0 ? height : getContentHeight(this._inputContainer.nativeElement)));
}
public setLayout(): void {
// TODO allow configuring the look and feel
this.layout();
@@ -149,10 +154,8 @@ export default class TableComponent extends ComponentBase implements IComponent,
if (this.selectedRows) {
this._table.setSelectedRows(this.selectedRows);
}
this._table.layout(new Dimension(
this.width ? this.width : getContentWidth(this._inputContainer.nativeElement),
this.height ? this.height : getContentHeight(this._inputContainer.nativeElement)));
this.layoutTable();
this.validate();
}
@@ -181,20 +184,4 @@ export default class TableComponent extends ComponentBase implements IComponent,
public set selectedRows(newValue: number[]) {
this.setPropertyFromUI<sqlops.TableComponentProperties, number[]>((props, value) => props.selectedRows = value, newValue);
}
public get height(): number {
return this.getPropertyOrDefault<sqlops.TableComponentProperties, number>((props) => props.height, undefined);
}
public set height(newValue: number) {
this.setPropertyFromUI<sqlops.TableComponentProperties, number>((props, value) => props.height = value, newValue);
}
public get width(): number {
return this.getPropertyOrDefault<sqlops.TableComponentProperties, number>((props) => props.width, undefined);
}
public set width(newValue: number) {
this.setPropertyFromUI<sqlops.TableComponentProperties, number>((props, value) => props.width = value, newValue);
}
}

View File

@@ -40,7 +40,6 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
abstract serverInfo: sqlops.ServerInfo;
private _onEventEmitter = new Emitter<any>();
initializeModel(rootComponent: IComponentShape, validationCallback: (componentId: string) => Thenable<boolean>): void {
let descriptor = this.defineComponent(rootComponent);
this.rootDescriptor = descriptor;
@@ -50,6 +49,10 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
}
private defineComponent(component: IComponentShape): IComponentDescriptor {
let existingDescriptor = this.modelStore.getComponentDescriptor(component.id);
if (existingDescriptor) {
return existingDescriptor;
}
let typeId = componentRegistry.getIdForTypeMapping(component.type);
if (!typeId) {
// failure case

View File

@@ -92,33 +92,6 @@ const profilerSessionTemplateSchema: IJSONSchema = {
}
]
}
},
{
name: 'TSQL'
},
{
name: 'Blank'
},
{
name: 'SP_Counts'
},
{
name: 'TQL_Duration'
},
{
name: 'TSQL_Grouped'
},
{
name: 'TSQL_Locks'
},
{
name: 'TSQL_Replay'
},
{
name: 'TSQL_SPs'
},
{
name: 'Tuning'
}
]
};

View File

@@ -19,8 +19,10 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IObjectExplorerService } from '../../objectExplorer/common/objectExplorerService';
import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
import { TPromise } from 'vs/base/common/winjs.base';
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
// Contribute Global Actions
const category = nls.localize('profilerCategory', "Profiler");
@@ -37,20 +39,9 @@ CommandsRegistry.registerCommand({
let editorService: IWorkbenchEditorService = accessor.get(IWorkbenchEditorService);
let instantiationService: IInstantiationService = accessor.get(IInstantiationService);
let connectionService: IConnectionManagementService = accessor.get(IConnectionManagementService);
let objectExplorerService: IObjectExplorerService = accessor.get(IObjectExplorerService);
// TODO: for test-only, grab the first MSSQL active connection for the profiler session
// TODO: when finishing the feature the connection should come from the launch context
let connectionProfile: IConnectionProfile;
let activeConnections = connectionService.getActiveConnections();
if (activeConnections) {
for (let i = 0; i < activeConnections.length; ++i) {
if (activeConnections[i].providerName === 'MSSQL') {
connectionProfile = activeConnections[i];
break;
}
}
}
let connectionProfile = TaskUtilities.getCurrentGlobalConnection(objectExplorerService, connectionService, editorService);
let profilerInput = instantiationService.createInstance(ProfilerInput, connectionProfile);
return editorService.openEditor(profilerInput, { pinned: true }, false).then(() => TPromise.as(true));
}

View File

@@ -57,7 +57,7 @@ export class ProfilerConnect extends Action {
public set connected(value: boolean) {
this._connected = value;
this._setClass(value ? 'disconnect' : 'connect');
this._setLabel(value ? nls.localize('profilerAction.disconnect', 'Disconnected') : nls.localize('profilerAction.connect', "Connect"));
this._setLabel(value ? nls.localize('profilerAction.disconnect', 'Disconnect') : nls.localize('profilerAction.connect', "Connect"));
}
public get connected(): boolean {
@@ -78,17 +78,19 @@ export class ProfilerStart extends Action {
public run(input: ProfilerInput): TPromise<boolean> {
this.enabled = false;
input.data.clear();
return TPromise.wrap(this._profilerService.startSession(input.id).then(() => {
input.state.change({ isRunning: true, isStopped: false, isPaused: false });
return true;
}));
}
}
export class ProfilerPause extends Action {
public static ID = 'profiler.pause';
public static LABEL = nls.localize('pause', "Pause");
public static LABEL = nls.localize('profiler.capture', "Pause Capture");
private _paused: boolean = false;
constructor(
id: string, label: string,
@@ -98,12 +100,22 @@ export class ProfilerPause extends Action {
}
public run(input: ProfilerInput): TPromise<boolean> {
this.enabled = false;
return TPromise.wrap(this._profilerService.pauseSession(input.id).then(() => {
input.state.change({ isPaused: true, isStopped: false, isRunning: false });
this.paused = !this._paused;
input.state.change({ isPaused: this.paused, isStopped: false, isRunning: !this.paused });
return true;
}));
}
public set paused(value: boolean) {
this._paused = value;
this._setClass(value ? 'start' : 'stop');
this._setLabel(value ? nls.localize('profilerAction.resumeCapture', "Resume Capture") : nls.localize('profilerAction.pauseCapture', "Pause Capture"));
}
public get paused(): boolean {
return this._paused;
}
}
export class ProfilerStop extends Action {

View File

@@ -203,14 +203,12 @@ export class ProfilerEditor extends BaseEditor {
this._actionBar.setContent([
{ action: this._startAction },
{ action: this._pauseAction },
{ action: this._stopAction },
{ action: this._connectAction },
{ element: Taskbar.createTaskbarSeparator() },
{ action: this._autoscrollAction },
{ action: this._instantiationService.createInstance(Actions.ProfilerClear, Actions.ProfilerClear.ID, Actions.ProfilerClear.LABEL) },
{ element: dropdownContainer },
{ action: this._instantiationService.createInstance(Actions.ProfilerEditColumns, Actions.ProfilerEditColumns.ID, Actions.ProfilerEditColumns.LABEL) }
{ element: Taskbar.createTaskbarSeparator() },
{ action: this._pauseAction },
{ action: this._autoscrollAction },
{ action: this._instantiationService.createInstance(Actions.ProfilerClear, Actions.ProfilerClear.ID, Actions.ProfilerClear.LABEL) }
]);
}
@@ -228,7 +226,9 @@ export class ProfilerEditor extends BaseEditor {
if (data) {
this._modelService.updateModel(this._editorModel, data['TextData']);
this._detailTableData.clear();
this._detailTableData.push(Object.keys(data).map(key => {
this._detailTableData.push(Object.keys(data).filter(key => {
return data[key] !== ' ';
}).map(key => {
return {
label: key,
value: data[key]
@@ -384,29 +384,26 @@ export class ProfilerEditor extends BaseEditor {
if (e.isConnected) {
this._connectAction.connected = this.input.state.isConnected;
this._startAction.enabled = this.input.state.isConnected;
this._stopAction.enabled = false;
this._pauseAction.enabled = false;
if (this.input.state.isConnected) {
this._sessionTemplateSelector.disable();
} else {
this._sessionTemplateSelector.enable();
this._startAction.enabled = this.input.state.isConnected;
this._stopAction.enabled = false;
this._pauseAction.enabled = false;
return;
}
return;
}
if (e.isRunning) {
this._startAction.enabled = !this.input.state.isRunning;
if (e.isPaused){
this._pauseAction.paused = this.input.state.isPaused;
this._pauseAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused);
}
if (e.isStopped || e.isRunning) {
this._stopAction.enabled = !this.input.state.isStopped && this.input.state.isRunning;
}
if (e.isPaused || e.isRunning) {
this._pauseAction.enabled = !this.input.state.isPaused && this.input.state.isRunning;
this._startAction.enabled = !this.input.state.isRunning && !this.input.state.isPaused;
this._stopAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused);
this._pauseAction.enabled = !this.input.state.isStopped && (this.input.state.isRunning || this.input.state.isPaused);
}
}

View File

@@ -14,6 +14,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput } from 'vs/workbench/common/editor';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Event, Emitter } from 'vs/base/common/event';
import { generateUuid } from 'vs/base/common/uuid';
@@ -35,14 +36,15 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
constructor(
private _connection: IConnectionProfile,
@IInstantiationService private _instantiationService: IInstantiationService,
@IProfilerService private _profilerService: IProfilerService
@IProfilerService private _profilerService: IProfilerService,
@INotificationService private _notificationService: INotificationService
) {
super();
this._state = new ProfilerState();
// set inital state
this.state.change({
isConnected: true,
isStopped: false,
isStopped: true,
isPaused: false,
isRunning: false,
autoscroll: true
@@ -123,13 +125,26 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
return this._state;
}
public onSessionStopped(notification: sqlops.ProfilerSessionStoppedParams) {
this._notificationService.error(nls.localize("profiler.sessionStopped", "XEvent Profiler Session stopped unexpectedly on the server {0}.", this._connection.serverName));
this.state.change({
isStopped: true,
isPaused: false,
isRunning: false
});
}
public onMoreRows(eventMessage: sqlops.ProfilerSessionEvents) {
if (eventMessage.eventsLost){
this._notificationService.warn(nls.localize("profiler.eventsLost", "The XEvent Profiler session for {0} has lost events.", this._connection.serverName));
}
for (let i: number = 0; i < eventMessage.events.length && i < 500; ++i) {
let e: sqlops.ProfilerEvent = eventMessage.events[i];
let data = {};
data['EventClass'] = e.name;
data['StartTime'] = e.timestamp;
data['EndTime'] = e.timestamp;
const columns = [
'TextData',
'ApplicationName',
@@ -156,12 +171,14 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
columnNameMap['cpu_time'] = 'CPU';
columnNameMap['duration'] = 'Duration';
columnNameMap['logical_reads'] = 'Reads';
columnNameMap['event_sequence'] = 'EventSequence';
columnNameMap['client_pid'] = 'ClientProcessID';
columnNameMap['writes'] = 'Writes';
for (let idx = 0; idx < columns.length; ++idx) {
let columnName = columns[idx];
data[columnName] = '';
}
// Using ' ' instead of '' fixed the error where clicking through events
// with empty text fields causes future text panes to be highlighted.
// This is a temporary fix, and should be changed before the July release
data['TextData'] = ' ';
for (let key in e.values) {
let columnName = columnNameMap[key];
if (columnName) {

View File

@@ -25,6 +25,10 @@ export interface IProfilerSession {
* Called by the service when more rows are available to render
*/
onMoreRows(events: sqlops.ProfilerSessionEvents);
/**
* Called by the service when the session is closed unexpectedly
*/
onSessionStopped(events: sqlops.ProfilerSessionStoppedParams);
}
/**
@@ -65,6 +69,10 @@ export interface IProfilerService {
* The method called by the service provider for when more rows are available to render
*/
onMoreRows(params: sqlops.ProfilerSessionEvents): void;
/**
* The method called by the service provider for when more rows are available to render
*/
onSessionStopped(params: sqlops.ProfilerSessionStoppedParams): void;
/**
* Gets a list of the session templates that are specified in the settings
* @param provider An optional string to limit the session template to a specific

View File

@@ -82,6 +82,11 @@ export class ProfilerService implements IProfilerService {
this._sessionMap.get(this._idMap.reverseGet(params.sessionId)).onMoreRows(params);
}
public onSessionStopped(params: sqlops.ProfilerSessionStoppedParams): void {
this._sessionMap.get(this._idMap.reverseGet(params.ownerUri)).onSessionStopped(params);
}
public connectSession(id: ProfilerSessionID): Thenable<boolean> {
return this._runAction(id, provider => provider.connectSession(this._idMap.get(id)));
}

View File

@@ -48,6 +48,10 @@ export class ProfilerTestBackend implements sqlops.ProfilerProvider {
return;
}
registerOnSessionStopped(handler: (response: sqlops.ProfilerSessionStoppedParams) => any) {
return;
}
private intervalFn(guid: string): number {
return setTimeout(() => {
let data = this.testData[this.index++];

View File

@@ -19,6 +19,9 @@ import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { Registry } from 'vs/platform/registry/common/platform';
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';
/* Model-backed components */
let extensionComponents = Registry.as<IComponentRegistry>(Extensions.ComponentContribution).getAllCtors();
@@ -26,6 +29,9 @@ let extensionComponents = Registry.as<IComponentRegistry>(Extensions.ComponentCo
export const DialogModule = (params, selector: string): any => {
@NgModule({
declarations: [
Checkbox,
SelectBox,
InputBox,
DialogContainer,
ModelViewContent,
ModelComponentWrapper,

View File

@@ -15,6 +15,7 @@ import { ComponentEventType } from '../../parts/modelComponents/interfaces';
export interface DialogComponentParams extends IBootstrapParams {
modelViewId: string;
validityChangedCallback: (valid: boolean) => void;
onLayoutRequested: Event<string>;
}
@Component({
@@ -35,6 +36,11 @@ export class DialogContainer implements AfterContentInit {
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IBootstrapParams) private _params: DialogComponentParams) {
this.modelViewId = this._params.modelViewId;
this._params.onLayoutRequested(e => {
if (this.modelViewId === e) {
this.layout();
}
});
}
ngAfterContentInit(): void {

View File

@@ -21,6 +21,7 @@ import { Builder } from 'vs/base/browser/builder';
import { IThemable } from 'vs/platform/theme/common/styler';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Emitter } from 'vs/base/common/event';
export class DialogPane extends Disposable implements IThemable {
private _tabbedPanel: TabbedPanel;
@@ -34,6 +35,9 @@ export class DialogPane extends Disposable implements IThemable {
private _tabBar: HTMLElement;
private _tabs: HTMLElement[];
private _tabContent: HTMLElement[];
private _selectedTabIndex: number = 0; //TODO: can be an option
private _onTabChange = new Emitter<string>();
private _selectedTabContent: string;
constructor(
private _title: string,
@@ -55,10 +59,17 @@ export class DialogPane extends Disposable implements IThemable {
} else {
this._tabbedPanel = new TabbedPanel(this._body);
this._content.forEach((tab, tabIndex) => {
if (this._selectedTabIndex === tabIndex) {
this._selectedTabContent = tab.content;
}
let tabContainer = document.createElement('div');
tabContainer.style.display = 'none';
this._body.appendChild(tabContainer);
this.initializeModelViewContainer(tabContainer, tab.content, tab);
this._tabbedPanel.onTabChange(e => {
tabContainer.style.height = (this.getTabDimension().height - this._tabbedPanel.headersize) + 'px';
this._onTabChange.fire(tab.content);
});
this._tabbedPanel.pushTab({
title: tab.title,
identifier: 'dialogPane.' + this._title + '.' + tabIndex,
@@ -70,7 +81,7 @@ export class DialogPane extends Disposable implements IThemable {
container.appendChild(tabContainer);
tabContainer.style.display = 'block';
},
layout: (dimension) => { }
layout: (dimension) => { this.getTabDimension(); }
} as IPanelView
} as IPanelTab);
});
@@ -80,9 +91,14 @@ export class DialogPane extends Disposable implements IThemable {
return this._body;
}
private getTabDimension(): DOM.Dimension {
return new DOM.Dimension(DOM.getContentWidth(this._body), DOM.getContentHeight(this._body));
}
public layout(): void {
if (this._tabbedPanel) {
this._tabbedPanel.layout(new DOM.Dimension(DOM.getContentWidth(this._body), DOM.getContentHeight(this._body)));
this._onTabChange.fire(this._selectedTabContent);
}
}
@@ -101,10 +117,13 @@ export class DialogPane extends Disposable implements IThemable {
if (tab) {
tab.notifyValidityChanged(valid);
}
}
},
onLayoutRequested: this._onTabChange.event
} as DialogComponentParams,
undefined,
(moduleRef) => this._moduleRefs.push(moduleRef));
(moduleRef) => {
return this._moduleRefs.push(moduleRef);
});
}
public show(): void {

View File

@@ -139,6 +139,7 @@ export class Wizard {
public readonly onPageAdded = this._pageAddedEmitter.event;
private _pageRemovedEmitter = new Emitter<WizardPage>();
public readonly onPageRemoved = this._pageRemovedEmitter.event;
private _navigationValidator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>;
constructor(public title: string) { }
@@ -191,4 +192,19 @@ export class Wizard {
this.pages.splice(index, 1);
this._pageRemovedEmitter.fire(removedPage);
}
public registerNavigationValidator(validator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>): void {
this._navigationValidator = validator;
}
public validateNavigation(newPage: number): Thenable<boolean> {
if (this._navigationValidator) {
return Promise.resolve(this._navigationValidator({
lastPage: this._currentPage,
newPage: newPage
}));
} else {
return Promise.resolve(true);
}
}
}

View File

@@ -29,4 +29,4 @@
.footer-button.dialogModal-hidden {
margin: 0;
}
}

View File

@@ -106,12 +106,12 @@ export class WizardModal extends Modal {
});
this._wizard.onPageAdded(page => {
this.registerPage(page);
this.showPage(this.getCurrentPage());
this.showPage(this.getCurrentPage(), false);
});
this._wizard.onPageRemoved(page => {
let dialogPane = this._dialogPanes.get(page);
this._dialogPanes.delete(page);
this.showPage(this.getCurrentPage());
this.showPage(this.getCurrentPage(), false);
dialogPane.dispose();
});
}
@@ -123,10 +123,13 @@ export class WizardModal extends Modal {
page.onUpdate(() => this.setButtonsForPage(this._wizard.currentPage));
}
private showPage(index: number): void {
private async showPage(index: number, validate: boolean = true): Promise<void> {
let pageToShow = this._wizard.pages[index];
if (!pageToShow) {
this.done();
this.done(validate);
return;
}
if (validate && !await this._wizard.validateNavigation(index)) {
return;
}
this._dialogPanes.forEach((dialogPane, page) => {
@@ -163,12 +166,15 @@ export class WizardModal extends Modal {
}
public open(): void {
this.showPage(0);
this.showPage(0, false);
this.show();
}
public done(): void {
public async done(validate: boolean = true): Promise<void> {
if (this._wizard.doneButton.enabled) {
if (validate && !await this._wizard.validateNavigation(undefined)) {
return;
}
this._onDone.fire();
this.dispose();
this.hide();

211
src/sql/sqlops.d.ts vendored
View File

@@ -1025,22 +1025,33 @@ declare module 'sqlops' {
getDatabaseInfo(connectionUri: string): Thenable<DatabaseInfo>;
}
// Agent Services interfaces
export interface AgentJobsResult {
succeeded: boolean;
errorMessage: string;
jobs: AgentJobInfo[];
// Agent Services types
export enum WeekDays {
sunday = 1,
monday = 2,
tuesday = 4,
wednesday = 8,
thursday = 16,
friday = 32,
weekDays = 62,
saturday = 64,
weekEnds = 65,
everyDay = 127
}
export interface AgentJobHistoryResult {
succeeded: boolean;
errorMessage: string;
jobs: AgentJobHistoryInfo[];
export enum NotifyMethods {
none = 0,
notifyEmail = 1,
pager = 2,
netSend = 4,
notifyAll = 7
}
export interface AgentJobActionResult {
succeeded: boolean;
errorMessage: string;
export enum AlertType {
sqlServerEvent = 1,
sqlServerPerformanceCondition = 2,
nonSqlServerEvent = 3,
wmiEvent = 4
}
export interface AgentJobInfo {
@@ -1061,7 +1072,8 @@ declare module 'sqlops' {
jobId: string;
}
export interface AgentJobStep {
export interface AgentJobStepInfo {
jobId: string;
stepId: string;
stepName: string;
message: string;
@@ -1086,13 +1098,168 @@ declare module 'sqlops' {
operatorPaged: string;
retriesAttempted: string;
server: string;
steps: AgentJobStep[];
steps: AgentJobStepInfo[];
}
export interface AgentProxyInfo {
id: number;
accountName: string;
description: string;
credentialName: string;
credentialIdentity: string;
credentialId: number;
isEnabled: boolean;
}
export interface AgentAlertInfo {
id: number;
delayBetweenResponses: number;
eventDescriptionKeyword: string;
eventSource: string;
hasNotification: number;
includeEventDescription: NotifyMethods;
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: AlertType;
wmiEventNamespace: string;
wmiEventQuery: string;
}
export interface AgentOperatorInfo {
name: string;
id: number;
emailAddress: string;
enabled: boolean;
lastEmailDate: string;
lastNetSendDate: string;
lastPagerDate: string;
pagerAddress: string;
categoryName: string;
pagerDays: WeekDays;
saturdayPagerEndTime: string;
saturdayPagerStartTime: string;
sundayPagerEndTime: string;
sundayPagerStartTime: string;
netSendAddress: string;
weekdayPagerStartTime: string;
weekdayPagerEndTime: string;
}
export interface ResultStatus {
success: boolean;
errorMessage: string;
}
export interface AgentJobsResult extends ResultStatus {
jobs: AgentJobInfo[];
}
export interface AgentJobHistoryResult extends ResultStatus {
jobs: AgentJobHistoryInfo[];
}
export interface CreateAgentJobResult extends ResultStatus {
job: AgentJobInfo;
}
export interface UpdateAgentJobResult extends ResultStatus {
job: AgentJobInfo;
}
export interface CreateAgentJobStepResult extends ResultStatus {
step: AgentJobStepInfo;
}
export interface UpdateAgentJobStepResult extends ResultStatus {
step: AgentJobStepInfo;
}
export interface CreateAgentProxyResult extends ResultStatus {
step: AgentJobStepInfo;
}
export interface UpdateAgentProxyResult extends ResultStatus {
step: AgentJobStepInfo;
}
export interface AgentAlertsResult extends ResultStatus {
alerts: AgentAlertInfo[];
}
export interface CreateAgentAlertResult extends ResultStatus {
alert: AgentJobStepInfo;
}
export interface UpdateAgentAlertResult extends ResultStatus {
alert: AgentJobStepInfo;
}
export interface AgentOperatorsResult extends ResultStatus {
operators: AgentOperatorInfo[];
}
export interface CreateAgentOperatorResult extends ResultStatus {
operator: AgentOperatorInfo;
}
export interface UpdateAgentOperatorResult extends ResultStatus {
operator: AgentOperatorInfo;
}
export interface AgentProxiesResult extends ResultStatus {
operators: AgentOperatorInfo[];
}
export interface CreateAgentProxyResult extends ResultStatus {
operator: AgentOperatorInfo;
}
export interface UpdateAgentProxyResult extends ResultStatus {
operator: AgentOperatorInfo;
}
export interface AgentServicesProvider extends DataProvider {
getJobs(connectionUri: string): Thenable<AgentJobsResult>;
getJobHistory(connectionUri: string, jobId: string): Thenable<AgentJobHistoryResult>;
jobAction(connectionUri: string, jobName: string, action: string): Thenable<AgentJobActionResult>;
// Job management methods
getJobs(ownerUri: string): Thenable<AgentJobsResult>;
getJobHistory(ownerUri: string, jobId: string): Thenable<AgentJobHistoryResult>;
jobAction(ownerUri: string, jobName: string, action: string): Thenable<ResultStatus>;
createJob(ownerUri: string, jobInfo: AgentJobInfo): Thenable<CreateAgentJobResult>;
updateJob(ownerUri: string, originalJobName: string, jobInfo: AgentJobInfo): Thenable<UpdateAgentJobResult>;
deleteJob(ownerUri: string, jobInfo: AgentJobInfo): Thenable<ResultStatus>;
// Job Step management methods
createJobStep(ownerUri: string, jobInfo: AgentJobStepInfo): Thenable<CreateAgentJobStepResult>;
updateJobStep(ownerUri: string, originalJobStepName: string, jobInfo: AgentJobStepInfo): Thenable<UpdateAgentJobStepResult>;
deleteJobStep(ownerUri: string, jobInfo: AgentJobStepInfo): Thenable<ResultStatus>;
// Alert management methods
getAlerts(ownerUri: string): Thenable<AgentAlertsResult>;
createAlert(ownerUri: string, alertInfo: AgentAlertInfo): Thenable<CreateAgentAlertResult>;
updateAlert(ownerUri: string, originalAlertName: string, alertInfo: AgentAlertInfo): Thenable<UpdateAgentAlertResult>;
deleteAlert(ownerUri: string, alertInfo: AgentAlertInfo): Thenable<ResultStatus>;
// Operator management methods
getOperators(ownerUri: string): Thenable<AgentOperatorsResult>;
createOperator(ownerUri: string, operatorInfo: AgentOperatorInfo): Thenable<CreateAgentOperatorResult>;
updateOperator(ownerUri: string, originalOperatorName: string, operatorInfo: AgentOperatorInfo): Thenable<UpdateAgentOperatorResult>;
deleteOperator(ownerUri: string, operatorInfo: AgentOperatorInfo): Thenable<ResultStatus>;
// Proxy management methods
getProxies(ownerUri: string): Thenable<AgentProxiesResult>;
createProxy(ownerUri: string, proxyInfo: AgentProxyInfo): Thenable<CreateAgentOperatorResult>;
updateProxy(ownerUri: string, originalProxyName: string, proxyInfo: AgentProxyInfo): Thenable<UpdateAgentOperatorResult>;
deleteProxy(ownerUri: string, proxyInfo: AgentProxyInfo): Thenable<ResultStatus>;
}
// Task service interfaces ----------------------------------------------------------------------------
@@ -1242,6 +1409,7 @@ declare module 'sqlops' {
disconnectSession(sessionId: string): Thenable<boolean>;
registerOnSessionEventsAvailable(handler: (response: ProfilerSessionEvents) => any): void;
registerOnSessionStopped(handler: (response: ProfilerSessionStoppedParams) => any): void;
}
export interface IProfilerTableRow {
@@ -1282,6 +1450,15 @@ declare module 'sqlops' {
sessionId: string;
events: ProfilerEvent[];
eventsLost: boolean;
}
export interface ProfilerSessionStoppedParams {
ownerUri: string;
sessionId: number;
}
// File browser interfaces -----------------------------------------------------------------------

View File

@@ -26,7 +26,9 @@ declare module 'sqlops' {
text(): ComponentBuilder<TextComponent>;
button(): ComponentBuilder<ButtonComponent>;
dropDown(): ComponentBuilder<DropDownComponent>;
listBox(): ComponentBuilder<ListBoxComponent>;
table(): ComponentBuilder<TableComponent>;
declarativeTable(): ComponentBuilder<DeclarativeTableComponent>;
dashboardWidget(widgetId: string): ComponentBuilder<DashboardWidgetComponent>;
dashboardWebview(webviewId: string): ComponentBuilder<DashboardWebviewComponent>;
formContainer(): FormBuilder;
@@ -104,11 +106,11 @@ declare module 'sqlops' {
/**
* Sends any updated properties of the component to the UI
*
* @returns {Thenable<boolean>} Thenable that completes once the update
* @returns {Thenable<void>} Thenable that completes once the update
* has been applied in the UI
* @memberof Component
*/
updateProperties(properties: { [key: string]: any }): Thenable<boolean>;
updateProperties(properties: { [key: string]: any }): Thenable<void>;
enabled: boolean;
/**
@@ -205,7 +207,20 @@ declare module 'sqlops' {
*/
alignContent?: string;
/**
* Container Height
*/
height?: number | string;
/**
* Container Width
*/
width?: number | string;
/**
*
*/
textAlign?: string
}
export interface FlexItemLayout {
@@ -222,11 +237,13 @@ declare module 'sqlops' {
export interface FormItemLayout {
horizontal?: boolean;
componentWidth?: number;
componentWidth?: number | string;
componentHeight?: number | string;
}
export interface FormLayout {
width?: number;
width?: number | string;
height?: number | string;
}
export interface GroupLayout {
@@ -292,21 +309,29 @@ declare module 'sqlops' {
export type InputBoxInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'text' | 'time' | 'url' | 'week';
export interface InputBoxProperties {
export interface ComponentProperties {
height?: number | string;
width?: number | string;
}
export interface InputBoxProperties extends ComponentProperties {
value?: string;
ariaLabel?: string;
placeHolder?: string;
height: number;
width: number;
inputType?: InputBoxInputType;
required?: boolean;
multiline?: boolean;
rows?: number;
columns?: number;
min?: number;
max?: number;
}
export interface TableColumn {
value: string
}
export interface TableComponentProperties {
export interface TableComponentProperties extends ComponentProperties {
data: any[][];
columns: string[] | TableColumn[];
selectedRows?: number[];
@@ -317,6 +342,12 @@ declare module 'sqlops' {
label?: string;
}
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
}
export interface RadioButtonProperties {
name?: string;
label?: string;
@@ -328,18 +359,37 @@ declare module 'sqlops' {
value?: string;
}
export interface DropDownProperties {
value?: string;
values?: string[];
export interface DropDownProperties extends ComponentProperties {
value?: string | CategoryValue;
values?: string[] | CategoryValue[];
editable?: boolean;
}
export interface DeclarativeTableColumn {
displayName: string;
categoryValues: CategoryValue[];
valueType: DeclarativeDataType;
isReadOnly: boolean;
width: number | string;
}
export interface DeclarativeTableProperties {
data: any[][];
columns: DeclarativeTableColumn[];
}
export interface ListBoxProperties {
selectedRow?: number;
values?: string[];
}
export interface WebViewProperties {
message?: any;
html?: string;
}
export interface ButtonProperties {
export interface ButtonProperties extends ComponentProperties {
label?: string;
iconPath?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
}
@@ -374,11 +424,27 @@ declare module 'sqlops' {
}
export interface DropDownComponent extends Component, DropDownProperties {
value: string;
values: string[];
value: string | CategoryValue;
values: string[] | CategoryValue[];
onValueChanged: vscode.Event<any>;
}
export interface TableCell {
row: number;
column: number;
value: any;
}
export interface DeclarativeTableComponent extends Component, DeclarativeTableProperties {
onDataChanged: vscode.Event<any>;
}
export interface ListBoxComponent extends Component, ListBoxProperties {
selectedRow?: number;
values: string[];
onRowSelected: vscode.Event<any>;
}
export interface TableComponent extends Component, TableComponentProperties {
onRowSelected: vscode.Event<any>;
}
@@ -609,7 +675,7 @@ declare module 'sqlops' {
lastPage: number,
/**
* The new page number
* The new page number or undefined if the user is closing the wizard
*/
newPage: number
}
@@ -722,6 +788,16 @@ declare module 'sqlops' {
* Close the wizard. Does nothing if the wizard is not open.
*/
close(): Thenable<void>;
/**
* Register a callback that will be called when the user tries to navigate by
* changing pages or clicking done. Only one callback can be registered at once, so
* each registration call will clear the previous registration.
* @param validator The callback that gets executed when the user tries to
* navigate. Return true to allow the navigation to proceed, or false to
* cancel it.
*/
registerNavigationValidator(validator: (pageChangeInfo: WizardPageChangeInfo) => boolean | Thenable<boolean>): void;
}
}
}

View File

@@ -63,12 +63,45 @@ export enum ScriptOperation {
Alter = 6
}
export enum WeekDays
{
sunday = 1,
monday = 2,
tuesday = 4,
wednesday = 8,
thursday = 16,
friday = 32,
weekDays = 62,
saturday = 64,
weekEnds = 65,
everyDay = 127
}
export enum NotifyMethods
{
none = 0,
notifyEmail = 1,
pager = 2,
netSend = 4,
notifyAll = 7
}
export enum AlertType
{
sqlServerEvent = 1,
sqlServerPerformanceCondition = 2,
nonSqlServerEvent = 3,
wmiEvent = 4
}
export enum ModelComponentTypes {
NavContainer,
FlexContainer,
Card,
InputBox,
DropDown,
DeclarativeTable,
ListBox,
Button,
CheckBox,
RadioButton,
@@ -185,3 +218,9 @@ export enum DataProviderType {
AgentServicesProvider = 'AgentServicesProvider',
CapabilitiesProvider = 'CapabilitiesProvider'
}
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
}

View File

@@ -503,6 +503,14 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
return this._resolveProvider<sqlops.ProfilerProvider>(handle).stopSession(sessionId);
}
/**
* Pause a profiler session
*/
public $pauseSession(handle: number, sessionId: string): Thenable<boolean> {
return this._resolveProvider<sqlops.ProfilerProvider>(handle).pauseSession(sessionId);
}
/**
* Profiler session events available notification
*/
@@ -510,6 +518,13 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
this._proxy.$onSessionEventsAvailable(handle, response);
}
/**
* Profiler session stopped unexpectedly notification
*/
public $onSessionStopped(handle: number, response: sqlops.ProfilerSessionStoppedParams): void {
this._proxy.$onSessionStopped(handle, response);
}
/**
* Agent Job Provider methods
@@ -532,7 +547,7 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
/**
* Run an action on a job
*/
public $jobAction(handle: number, ownerUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> {
public $jobAction(handle: number, ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
return this._resolveProvider<sqlops.AgentServicesProvider>(handle).jobAction(ownerUri, jobName, action);
}
}

View File

@@ -116,6 +116,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return builder;
}
listBox(): sqlops.ComponentBuilder<sqlops.ListBoxComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.ListBoxComponent> = this.getComponentBuilder(new ListBoxWrapper(this._proxy, this._handle, id), id);
this._componentBuilders.set(id, builder);
return builder;
}
table(): sqlops.ComponentBuilder<sqlops.TableComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.TableComponent> = this.getComponentBuilder(new TableComponentWrapper(this._proxy, this._handle, id), id);
@@ -123,6 +130,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
return builder;
}
declarativeTable(): sqlops.ComponentBuilder<sqlops.DeclarativeTableComponent> {
let id = this.getNextComponentId();
let builder: ComponentBuilderImpl<sqlops.DeclarativeTableComponent> = this.getComponentBuilder(new DeclarativeTableWrapper(this._proxy, this._handle, id), id);
this._componentBuilders.set(id, builder);
return builder;
}
dashboardWidget(widgetId: string): sqlops.ComponentBuilder<sqlops.DashboardWidgetComponent> {
let id = this.getNextComponentId();
let builder = this.getComponentBuilder<sqlops.DashboardWidgetComponent>(new ComponentWrapper(this._proxy, this._handle, ModelComponentTypes.DashboardWidget, id), id);
@@ -328,7 +342,6 @@ class InternalItemConfig {
}
}
class ComponentWrapper implements sqlops.Component {
public properties: { [key: string]: any } = {};
public layout: any;
@@ -371,6 +384,22 @@ class ComponentWrapper implements sqlops.Component {
this.setProperty('enabled', value);
}
public get height(): number | string {
return this.properties['height'];
}
public set height(v: number | string) {
this.setProperty('height', v);
}
public get width(): number | string {
return this.properties['width'];
}
public set width(v: number | string) {
this.setProperty('width', v);
}
public toComponentShape(): IComponentShape {
return <IComponentShape>{
id: this.id,
@@ -406,12 +435,13 @@ class ComponentWrapper implements sqlops.Component {
return this._proxy.$setLayout(this._handle, this.id, layout);
}
public updateProperties(): Thenable<boolean> {
public updateProperties(properties: { [key: string]: any }): Thenable<void> {
this.properties = Object.assign(this.properties, properties);
return this.notifyPropertyChanged();
}
protected notifyPropertyChanged(): Thenable<boolean> {
return this._proxy.$setProperties(this._handle, this._id, this.properties).then(() => true);
protected notifyPropertyChanged(): Thenable<void> {
return this._proxy.$setProperties(this._handle, this._id, this.properties);
}
public registerEvent(): Thenable<boolean> {
@@ -421,9 +451,9 @@ class ComponentWrapper implements sqlops.Component {
public onEvent(eventArgs: IComponentEventArgs) {
if (eventArgs && eventArgs.eventType === ComponentEventType.PropertiesChanged) {
this.properties = eventArgs.args;
}
else if (eventArgs && eventArgs.eventType === ComponentEventType.validityChanged) {
} else if (eventArgs && eventArgs.eventType === ComponentEventType.validityChanged) {
this._valid = eventArgs.args;
this._onValidityChangedEmitter.fire(this._valid);
} else if (eventArgs) {
let emitter = this._emitterMap.get(eventArgs.eventType);
if (emitter) {
@@ -432,13 +462,13 @@ class ComponentWrapper implements sqlops.Component {
}
}
protected async setProperty(key: string, value: any): Promise<boolean> {
protected async setProperty(key: string, value: any): Promise<void> {
if (!this.properties[key] || this.properties[key] !== value) {
// Only notify the front end if a value has been updated
this.properties[key] = value;
return this.notifyPropertyChanged();
}
return Promise.resolve(true);
return Promise.resolve();
}
private handleError(err: Error): void {
@@ -538,18 +568,39 @@ class InputBoxWrapper extends ComponentWrapper implements sqlops.InputBoxCompone
this.setProperty('placeHolder', v);
}
public get height(): number {
return this.properties['height'];
public get rows(): number {
return this.properties['rows'];
}
public set height(v: number) {
this.setProperty('height', v);
public set rows(v: number) {
this.setProperty('rows', v);
}
public get width(): number {
return this.properties['width'];
public get min(): number {
return this.properties['min'];
}
public set width(v: number) {
this.setProperty('width', v);
public set min(v: number) {
this.setProperty('min', v);
}
public get max(): number {
return this.properties['max'];
}
public set max(v: number) {
this.setProperty('max', v);
}
public get columns(): number {
return this.properties['columns'];
}
public set columns(v: number) {
this.setProperty('columns', v);
}
public get multiline(): boolean {
return this.properties['multiline'];
}
public set multiline(v: boolean) {
this.setProperty('multiline', v);
}
public get inputType(): sqlops.InputBoxInputType {
@@ -720,17 +771,17 @@ class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownCompone
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
}
public get value(): string {
public get value(): string | sqlops.CategoryValue {
return this.properties['value'];
}
public set value(v: string) {
public set value(v: string | sqlops.CategoryValue) {
this.setProperty('value', v);
}
public get values(): string[] {
public get values(): string[] | sqlops.CategoryValue[] {
return this.properties['values'];
}
public set values(v: string[]) {
public set values(v: string[] | sqlops.CategoryValue[]) {
this.setProperty('values', v);
}
@@ -747,6 +798,63 @@ class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownCompone
}
}
class DeclarativeTableWrapper extends ComponentWrapper implements sqlops.DeclarativeTableComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
super(proxy, handle, ModelComponentTypes.DeclarativeTable, id);
this.properties = {};
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<any>());
}
public get data(): any[][] {
return this.properties['data'];
}
public set data(v: any[][]) {
this.setProperty('data', v);
}
public get columns(): sqlops.DeclarativeTableColumn[] {
return this.properties['columns'];
}
public set columns(v: sqlops.DeclarativeTableColumn[]) {
this.setProperty('columns', v);
}
public get onDataChanged(): vscode.Event<any> {
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
return emitter && emitter.event;
}
}
class ListBoxWrapper extends ComponentWrapper implements sqlops.ListBoxComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {
super(proxy, handle, ModelComponentTypes.ListBox, id);
this.properties = {};
this._emitterMap.set(ComponentEventType.onSelectedRowChanged, new Emitter<any>());
}
public get selectedRow(): number {
return this.properties['selectedRow'];
}
public set selectedRow(v: number) {
this.setProperty('selectedRow', v);
}
public get values(): string[] {
return this.properties['values'];
}
public set values(v: string[]) {
this.setProperty('values', v);
}
public get onRowSelected(): vscode.Event<any> {
let emitter = this._emitterMap.get(ComponentEventType.onSelectedRowChanged);
return emitter && emitter.event;
}
}
class ButtonWrapper extends ComponentWrapper implements sqlops.ButtonComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string) {

View File

@@ -217,6 +217,7 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard {
public customButtons: sqlops.window.modelviewdialog.Button[];
private _pageChangedEmitter = new Emitter<sqlops.window.modelviewdialog.WizardPageChangeInfo>();
public readonly onPageChanged = this._pageChangedEmitter.event;
private _navigationValidator: (info: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>;
constructor(public title: string, private _extHostModelViewDialog: ExtHostModelViewDialog) {
this.doneButton = this._extHostModelViewDialog.createButton(DONE_LABEL);
@@ -225,6 +226,7 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard {
this.nextButton = this._extHostModelViewDialog.createButton(NEXT_LABEL);
this.backButton = this._extHostModelViewDialog.createButton(PREVIOUS_LABEL);
this._extHostModelViewDialog.registerWizardPageInfoChangedCallback(this, info => this.handlePageInfoChanged(info));
this._currentPage = 0;
this.onPageChanged(info => this._currentPage = info.newPage);
}
@@ -254,6 +256,18 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard {
return this._extHostModelViewDialog.closeWizard(this);
}
public registerNavigationValidator(validator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>): void {
this._navigationValidator = validator;
}
public validateNavigation(info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean> {
if (this._navigationValidator) {
return Promise.resolve(this._navigationValidator(info));
} else {
return Promise.resolve(true);
}
}
private handlePageInfoChanged(info: WizardPageEventInfo): void {
this._currentPage = info.pageChangeInfo.newPage;
if (info.eventType === WizardPageInfoEventType.PageAddedOrRemoved) {
@@ -335,6 +349,11 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
}
}
public $validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean> {
let wizard = this._objectsByHandle.get(handle) as WizardImpl;
return wizard.validateNavigation(info);
}
public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void {
let handle = this.getHandle(dialog);
this.updateDialogContent(dialog);

View File

@@ -296,7 +296,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
return self._proxy.$stopSession(handle, sessionId);
},
pauseSession(sessionId: string): Thenable<boolean> {
return TPromise.as(true);
return self._proxy.$pauseSession(handle, sessionId);
},
connectSession(sessionId: string): Thenable<boolean> {
return TPromise.as(true);
@@ -339,7 +339,7 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
return self._proxy.$getJobHistory(handle, connectionUri, jobID);
},
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> {
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
return self._proxy.$jobAction(handle, connectionUri, jobName, action);
}
});
@@ -432,6 +432,10 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
this._profilerService.onMoreRows(response);
}
public $onSessionStopped(handle: number, response: sqlops.ProfilerSessionStoppedParams): void {
this._profilerService.onSessionStopped(response);
}
public $unregisterProvider(handle: number): TPromise<any> {
let capabilitiesRegistration = this._capabilitiesRegistrations[handle];
if (capabilitiesRegistration) {

View File

@@ -157,6 +157,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
wizard.onPageChanged(info => this._proxy.$onWizardPageChanged(handle, info));
wizard.onPageAdded(() => this.handleWizardPageAddedOrRemoved(handle));
wizard.onPageRemoved(() => this.handleWizardPageAddedOrRemoved(handle));
wizard.registerNavigationValidator(info => this.validateNavigation(handle, info));
this._wizards.set(handle, wizard);
}
@@ -254,4 +255,8 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
let wizard = this._wizards.get(handle);
this._proxy.$updateWizardPageInfo(handle, wizard.pages.map(page => this._wizardPageHandles.get(page)), wizard.currentPage);
}
private validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean> {
return this._proxy.$validateNavigation(handle, info);
}
}

View File

@@ -237,6 +237,10 @@ export function createApiFactory(
extHostDataProvider.$onSessionEventsAvailable(provider.handle, response);
});
provider.registerOnSessionStopped((response: sqlops.ProfilerSessionStoppedParams) => {
extHostDataProvider.$onSessionStopped(provider.handle, response);
});
return extHostDataProvider.$registerProfilerProvider(provider);
};
@@ -369,6 +373,7 @@ export function createApiFactory(
serialization,
dataprotocol,
DataProviderType: sqlExtHostTypes.DataProviderType,
DeclarativeDataType: sqlExtHostTypes.DeclarativeDataType,
ServiceOptionType: sqlExtHostTypes.ServiceOptionType,
ConnectionOptionSpecialType: sqlExtHostTypes.ConnectionOptionSpecialType,
EditRowState: sqlExtHostTypes.EditRowState,
@@ -376,6 +381,9 @@ export function createApiFactory(
TaskStatus: sqlExtHostTypes.TaskStatus,
TaskExecutionMode: sqlExtHostTypes.TaskExecutionMode,
ScriptOperation: sqlExtHostTypes.ScriptOperation,
WeekDays: sqlExtHostTypes.WeekDays,
NotifyMethods: sqlExtHostTypes.NotifyMethods,
AlertType: sqlExtHostTypes.AlertType,
window,
tasks,
dashboard,

View File

@@ -313,6 +313,11 @@ export abstract class ExtHostDataProtocolShape {
*/
$stopSession(handle: number, sessionId: string): Thenable<boolean> { throw ni(); }
/**
* Pause a profiler session
*/
$pauseSession(handle: number, sessionId: string): Thenable<boolean> { throw ni(); }
/**
* Get Agent Job list
@@ -327,7 +332,7 @@ export abstract class ExtHostDataProtocolShape {
/**
* Run an action on a Job
*/
$jobAction(handle: number, ownerUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> { throw ni(); }
$jobAction(handle: number, ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> { throw ni(); }
}
/**
@@ -411,6 +416,7 @@ export interface MainThreadDataProtocolShape extends IDisposable {
$onFilePathsValidated(handle: number, response: sqlops.FileBrowserValidatedParams): void;
$onScriptingComplete(handle: number, message: sqlops.ScriptingCompleteResult): void;
$onSessionEventsAvailable(handle: number, response: sqlops.ProfilerSessionEvents): void;
$onSessionStopped(handle: number, response: sqlops.ProfilerSessionStoppedParams): void;
/**
* Callback when a session has completed initialization
@@ -556,6 +562,7 @@ export interface ExtHostModelViewDialogShape {
$onPanelValidityChanged(handle: number, valid: boolean): void;
$onWizardPageChanged(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): void;
$updateWizardPageInfo(handle: number, pageHandles: number[], currentPageIndex: number): void;
$validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean>;
}
export interface MainThreadModelViewDialogShape extends IDisposable {

View File

@@ -29,14 +29,14 @@ export class OpenGettingStartedInBrowserAction extends Action {
}
run(): TPromise<any> {
const uri = URI.parse(product.releaseNotesUrl);
const uri = URI.parse(product.gettingStartedUrl);
return this.openerService.open(uri);
}
}
export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction {
static ID = 'update.showCurrentCarbonReleaseNotes';
static ID = 'update.showGettingStarted';
static LABEL = nls.localize('showReleaseNotes', "Show Getting Started");
constructor(

View File

@@ -118,4 +118,14 @@ suite('ExtHostModelView Validation Tests', () => {
});
assert.equal(inputBox.valid, true, 'Input box did not update validity to true based on the validityChanged event');
});
test('Main thread validityChanged events cause component to fire validity changed events', () => {
let validityFromEvent: boolean = undefined;
inputBox.onValidityChanged(valid => validityFromEvent = valid);
extHostModelView.$handleEvent(handle, inputBox.id, {
eventType: ComponentEventType.validityChanged,
args: false
});
assert.equal(validityFromEvent, false, 'Main thread validityChanged event did not cause component to fire its own event');
});
});

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as sqlops from 'sqlops';
import * as assert from 'assert';
import { Mock, It, Times } from 'typemoq';
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
@@ -263,4 +264,30 @@ suite('ExtHostModelViewDialog Tests', () => {
extHostModelViewDialog.$onPanelValidityChanged(pageHandle, false);
assert.equal(page.valid, false);
});
test('Main thread can execute wizard navigation validation', () => {
// Set up the main thread mock to record the wizard handle
let wizardHandle: number;
mockProxy.setup(x => x.$setWizardDetails(It.isAny(), It.isAny())).callback((handle, details) => wizardHandle = handle);
// Create the wizard and add a validation that records that it has been called
let wizard = extHostModelViewDialog.createWizard('wizard_1');
extHostModelViewDialog.updateWizard(wizard);
let validationInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo;
wizard.registerNavigationValidator(info => {
validationInfo = info;
return true;
});
// If I call the validation from the main thread then it should run and record the correct page change info
let lastPage = 0;
let newPage = 1;
extHostModelViewDialog.$validateNavigation(wizardHandle, {
lastPage: lastPage,
newPage: newPage
});
assert.notEqual(validationInfo, undefined);
assert.equal(validationInfo.lastPage, lastPage);
assert.equal(validationInfo.newPage, newPage);
});
});

View File

@@ -59,7 +59,8 @@ suite('MainThreadModelViewDialog Tests', () => {
$onButtonClick: handle => undefined,
$onPanelValidityChanged: (handle, valid) => undefined,
$onWizardPageChanged: (handle, info) => undefined,
$updateWizardPageInfo: (wizardHandle, pageHandles, currentPageIndex) => undefined
$updateWizardPageInfo: (wizardHandle, pageHandles, currentPageIndex) => undefined,
$validateNavigation: (handle, info) => undefined
});
let extHostContext = <IExtHostContext>{
getProxy: proxyType => mockExtHostModelViewDialog.object
@@ -316,4 +317,15 @@ suite('MainThreadModelViewDialog Tests', () => {
It.is(pageHandles => pageHandles.length === 1 && pageHandles[0] === page2Handle),
It.is(currentPage => currentPage === 0)), Times.once());
});
test('Creating a wizard adds a navigation validation that calls the extension host', () => {
mockExtHostModelViewDialog.setup(x => x.$validateNavigation(It.isAny(), It.isAny()));
// If I call validateNavigation on the wizard that gets created
let wizard: Wizard = (mainThreadModelViewDialog as any).getWizard(wizardHandle);
wizard.validateNavigation(1);
// Then the call gets forwarded to the extension host
mockExtHostModelViewDialog.verify(x => x.$validateNavigation(It.is(handle => handle === wizardHandle), It.is(info => info.newPage === 1)), Times.once());
});
});

View File

@@ -248,7 +248,8 @@ export class ContextView {
this.$view.hide();
}
private isVisible(): boolean {
// {{SQL CARBON EDIT}}
public isVisible(): boolean {
return !!this.delegate;
}

View File

@@ -364,7 +364,11 @@ export class InputBox extends Widget {
};
}
if (!errorMsg) {
if (errorMsg) {
this.inputElement.setAttribute('aria-invalid', 'true');
this.showMessage(errorMsg);
}
else if (this.inputElement.hasAttribute('aria-invalid')) {
this.inputElement.removeAttribute('aria-invalid');
this.hideMessage();
}

View File

@@ -397,8 +397,16 @@ export class SelectBoxList implements ISelectBoxDelegate, IDelegate<ISelectOptio
container.appendChild(this.selectDropDownContainer);
this.layoutSelectDropDown();
// {{SQL CARBON EDIT}}
return {
dispose: () => container.removeChild(this.selectDropDownContainer) // remove to take out the CSS rules we add
dispose: () => {
try {
container.removeChild(this.selectDropDownContainer); // remove to take out the CSS rules we add
} catch(e) {
// if this fails it means it is already removed
}
}
};
}
@@ -541,21 +549,22 @@ export class SelectBoxList implements ISelectBoxDelegate, IDelegate<ISelectOptio
this.selectList.setFocus([this.selected]);
this.selectList.reveal(this.selectList.getFocus()[0]);
// {{SQL CARBON EDIT}} - Update the selection before firing the handler instead of after
// Reset Selection Handler
this._currentSelection = -1;
this.hideSelectDropDown(true);
this._onDidSelect.fire({
index: this.selectElement.selectedIndex,
selected: this.selectElement.title
});
// Reset Selection Handler
this._currentSelection = -1;
this.hideSelectDropDown(true);
}
dom.EventHelper.stop(e);
}
// List Exit - passive - hide drop-down, fire onDidSelect
private onListBlur(): void {
if (this._currentSelection >= 0) {
this.select(this._currentSelection);
}

View File

@@ -771,7 +771,8 @@ export class IssueReporter extends Disposable {
}
}
const queryStringPrefix = product.reportIssueUrl.indexOf('?') === -1 ? '?' : '&';
// {{SQL CARBON EDIT}}
const queryStringPrefix = repositoryUrl.indexOf('?') === -1 ? '?' : '&';
return `${repositoryUrl}${queryStringPrefix}title=${encodeURIComponent(issueTitle)}`;
}

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