Compare commits

..

33 Commits

Author SHA1 Message Date
Karl Burtram
088cac030f Merge branch 'master' into release/0.30 2018-06-18 13:07:21 -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
Karl Burtram
bd3c293f94 Merge branch 'master' into release/0.30 2018-06-18 10:28: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
Karl Burtram
17db0b7d09 Revert SlickGrid to 2.3.16 to workaround Edit Data TAB issue 2018-06-12 16:37:42 -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
92 changed files with 3519 additions and 917 deletions

View File

@@ -658,7 +658,7 @@
}
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.7",
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.8.2",
"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.45",
"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

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.30.4",
"version": "0.30.5",
"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.16",
"spdlog": "0.6.0",
"sudo-prompt": "^8.0.0",
"svg.js": "^2.2.5",

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

@@ -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.width.toString());
}
if (this.height) {
this._button.setWidth(this.height.toString());
}
this.updateIcon();
}

View File

@@ -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,51 @@ 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): string {
if (types.isUndefinedOrNull(size)) {
return '100%';
}
let convertedSize: string = size ? size.toString() : '100%';
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,6 +129,39 @@ 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.valuesHaveDisplayName()) {
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.name === this.value);
return valueCategory && valueCategory.displayName;
} else {
return 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 && valueCategory.name;
} else {
this.value = newValue;
}
}
// CSS-bound properties
private get value(): string {
@@ -138,11 +172,11 @@ export default class DropDownComponent extends ComponentBase implements ICompone
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';
}
@@ -150,12 +184,12 @@ export default class DropDownComponent extends ComponentBase implements ICompone
this.setPropertyFromUI<sqlops.DropDownProperties, string>(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,13 +19,13 @@ 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">
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height" [style.width]="width">
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.order]="getItemOrder(item)" >
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
</model-component-wrapper>
@@ -40,6 +41,7 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
private _alignItems: string;
private _alignContent: string;
private _height: string;
private _width: string;
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef) {
super(changeRef);
@@ -58,18 +60,14 @@ 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._height = this.convertSize(layout.height);
this._width = this.convertSize(layout.width);
this.layout();
}
@@ -90,6 +88,10 @@ 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;
}

View File

@@ -35,10 +35,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)">
<ng-container *ngIf="isHorizontal(item)">
<div class="form-cell">{{getItemTitle(item)}}</div>
<div class="form-cell">
@@ -69,7 +69,6 @@ class FormItem {
</div>
</div>
</div>
</ng-container>
</div>
</ng-container>
</div>
@@ -113,7 +112,11 @@ 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 {

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

@@ -127,13 +127,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 +155,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 +185,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

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

@@ -202,15 +202,15 @@ export class ProfilerEditor extends BaseEditor {
this._register(attachSelectBoxStyler(this._sessionTemplateSelector, this.themeService));
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) },
{ action: this._startAction },
{ action: this._stopAction },
{ 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 +228,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]
@@ -397,16 +399,14 @@ export class ProfilerEditor extends BaseEditor {
return;
}
if (e.isRunning) {
this._startAction.enabled = !this.input.state.isRunning;
if (e.isPaused){
this._pauseAction.paused = 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

@@ -129,7 +129,6 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
let data = {};
data['EventClass'] = e.name;
data['StartTime'] = e.timestamp;
data['EndTime'] = e.timestamp;
const columns = [
'TextData',
'ApplicationName',
@@ -156,12 +155,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

@@ -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,16 @@ 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 => {
this._onTabChange.fire(tab.content);
});
this._tabbedPanel.pushTab({
title: tab.title,
identifier: 'dialogPane.' + this._title + '.' + tabIndex,
@@ -70,7 +80,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 +90,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 +116,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();

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

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,15 @@ declare module 'sqlops' {
*/
alignContent?: string;
/**
* Container Height
*/
height?: number | string;
/**
* Container Width
*/
width?: number | string;
}
export interface FlexItemLayout {
@@ -226,7 +236,8 @@ declare module 'sqlops' {
}
export interface FormLayout {
width?: number;
width?: number | string;
height?: number | string;
}
export interface GroupLayout {
@@ -292,21 +303,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 +336,12 @@ declare module 'sqlops' {
label?: string;
}
export enum DeclarativeDataType {
string = 'string',
category = 'category',
boolean = 'boolean'
}
export interface RadioButtonProperties {
name?: string;
label?: string;
@@ -328,18 +353,37 @@ declare module 'sqlops' {
value?: string;
}
export interface DropDownProperties {
export interface DropDownProperties extends ComponentProperties {
value?: string;
values?: string[];
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 };
}
@@ -375,10 +419,26 @@ declare module 'sqlops' {
export interface DropDownComponent extends Component, DropDownProperties {
value: string;
values: string[];
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 +669,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 +782,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
*/
@@ -532,7 +540,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 {
@@ -727,10 +778,10 @@ class DropDownWrapper extends ComponentWrapper implements sqlops.DropDownCompone
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);
}
});

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

@@ -369,6 +369,7 @@ export function createApiFactory(
serialization,
dataprotocol,
DataProviderType: sqlExtHostTypes.DataProviderType,
DeclarativeDataType: sqlExtHostTypes.DeclarativeDataType,
ServiceOptionType: sqlExtHostTypes.ServiceOptionType,
ConnectionOptionSpecialType: sqlExtHostTypes.ConnectionOptionSpecialType,
EditRowState: sqlExtHostTypes.EditRowState,
@@ -376,6 +377,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(); }
}
/**
@@ -556,6 +561,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

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

@@ -541,21 +541,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)}`;
}

View File

@@ -28,8 +28,9 @@ export interface ITemplateData {
element: HTMLElement;
icon: HTMLImageElement;
name: HTMLElement;
installCount: HTMLElement;
ratings: HTMLElement;
// {{SQL CARBON EDIT}}
//installCount: HTMLElement;
//ratings: HTMLElement;
author: HTMLElement;
description: HTMLElement;
extension: IExtension;
@@ -103,16 +104,19 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
const reloadAction = this.instantiationService.createInstance(ReloadAction);
const manageAction = this.instantiationService.createInstance(ManageExtensionAction);
// {{SQL CARBON EDIT}}
actionbar.push([updateAction, reloadAction, installAction, disabledStatusAction, maliciousStatusAction, manageAction], actionOptions);
const disposables = [versionWidget, installCountWidget, ratingsWidget, maliciousStatusAction, disabledStatusAction, updateAction, reloadAction, manageAction, actionbar, bookmarkStyler];
const disposables = [versionWidget, /*installCountWidget, ratingsWidget*/, maliciousStatusAction, disabledStatusAction, updateAction, reloadAction, manageAction, actionbar, bookmarkStyler];
return {
root, element, icon, name, installCount, ratings, author, description, disposables,
// {{SQL CARBON EDIT}}
root, element, icon, name, /*installCount, ratings,*/ author, description, disposables,
extensionDisposables: [],
set extension(extension: IExtension) {
versionWidget.extension = extension;
installCountWidget.extension = extension;
ratingsWidget.extension = extension;
// {{SQL CARBON EDIT}}
//installCountWidget.extension = extension;
//ratingsWidget.extension = extension;
maliciousStatusAction.extension = extension;
disabledStatusAction.extension = extension;
installAction.extension = extension;
@@ -132,8 +136,8 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
data.name.textContent = '';
data.author.textContent = '';
data.description.textContent = '';
data.installCount.style.display = 'none';
data.ratings.style.display = 'none';
//data.installCount.style.display = 'none';
//data.ratings.style.display = 'none';
data.extension = null;
}
@@ -173,8 +177,9 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
data.name.textContent = extension.displayName;
data.author.textContent = extension.publisherDisplayName;
data.description.textContent = extension.description;
data.installCount.style.display = '';
data.ratings.style.display = '';
// {{SQL CARBON EDIT}}
//data.installCount.style.display = '';
//data.ratings.style.display = '';
data.extension = extension;
}

View File

@@ -327,19 +327,22 @@ export class ExtensionEditor extends BaseEditor {
.done(viewlet => viewlet.search(`publisher:"${extension.publisherDisplayName}"`));
});
if (extension.licenseUrl) {
this.license.onclick = finalHandler(() => window.open(extension.licenseUrl));
this.license.style.display = 'initial';
} else {
this.license.onclick = null;
this.license.style.display = 'none';
}
} else {
this.name.onclick = null;
this.rating.onclick = null;
this.publisher.onclick = null;
}
// {{SQL CARBON EDIT}}
if (extension.licenseUrl) {
this.license.onclick = finalHandler(() => window.open(extension.licenseUrl));
this.license.style.display = 'initial';
} else {
this.license.onclick = null;
this.license.style.display = 'none';
}
if (extension.repository) {
this.repository.onclick = finalHandler(() => window.open(extension.repository));
this.repository.style.display = 'initial';

View File

@@ -5889,9 +5889,9 @@ slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
"slickgrid@github:anthonydresser/SlickGrid#2.3.17":
version "2.3.17"
resolved "https://codeload.github.com/anthonydresser/SlickGrid/tar.gz/8def7636409c51a10890209f90083bb2c3092b3b"
"slickgrid@github:anthonydresser/SlickGrid#2.3.16":
version "2.3.16"
resolved "https://codeload.github.com/anthonydresser/SlickGrid/tar.gz/0cdbb91ca4f24e296e156b704dab51ea9e1cf3ac"
dependencies:
jquery ">=1.8.0"
jquery-ui ">=1.8.0"