Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
088cac030f | ||
|
|
bd3c293f94 | ||
|
|
17db0b7d09 |
3
.vscode/settings.json
vendored
@@ -38,6 +38,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
"git.ignoreLimitWarning": true
|
|
||||||
}
|
}
|
||||||
|
|||||||
13
CHANGELOG.md
@@ -1,18 +1,5 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
## Version 0.30.6
|
|
||||||
* Release date: June 20, 2018
|
|
||||||
* Release status: Public Preview
|
|
||||||
|
|
||||||
## What's new in this version
|
|
||||||
* **SQL Server Profiler for SQL Operations Studio *Preview*** extension initial release
|
|
||||||
* The new **SQL Data Warehouse** extension includes rich customizable dashboard widgets surfacing insights to your data warehouse. This unlocks key scenarios around managing and tuning your data warehouse to ensure it is optimized for consistent performance.
|
|
||||||
* **Edit Data "Filtering and Sorting"** support
|
|
||||||
* **SQL Server Agent for SQL Operations Studio *Preview*** extension enhancements for Jobs and Job History views
|
|
||||||
* Improved **Wizard & Dialog UI Builder Framework** extensibility APIs
|
|
||||||
* Update VS Code Platform source code integrating [March 2018 (1.22)](https://code.visualstudio.com/updates/v1_22) and [April 2018 (1.23)](https://code.visualstudio.com/updates/v1_23) releases
|
|
||||||
* Fix GitHub Issues
|
|
||||||
|
|
||||||
## Version 0.29.3
|
## Version 0.29.3
|
||||||
* Release date: May 7, 2018
|
* Release date: May 7, 2018
|
||||||
* Release status: Public Preview
|
* Release status: Public Preview
|
||||||
|
|||||||
15
README.md
@@ -4,16 +4,16 @@
|
|||||||
|
|
||||||
SQL Operations Studio is a data management tool that enables you to work with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
SQL Operations Studio is a data management tool that enables you to work with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
||||||
|
|
||||||
**Download SQL Operations Studio June Public Preview**
|
**Download SQL Operations Studio May Public Preview**
|
||||||
|
|
||||||
Platform | Link
|
Platform | Link
|
||||||
-- | --
|
-- | --
|
||||||
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=875602
|
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=873386
|
||||||
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=875603
|
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=873387
|
||||||
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=875604
|
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=873388
|
||||||
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=875605
|
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=873389
|
||||||
Linux RPM | https://go.microsoft.com/fwlink/?linkid=875606
|
Linux RPM | https://go.microsoft.com/fwlink/?linkid=873390
|
||||||
Linux DEB | https://go.microsoft.com/fwlink/?linkid=875607
|
Linux DEB | https://go.microsoft.com/fwlink/?linkid=873391
|
||||||
|
|
||||||
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
|
Go to our [download page](https://aka.ms/sqlopsstudio) for more specific instructions.
|
||||||
|
|
||||||
@@ -61,7 +61,6 @@ The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.micro
|
|||||||
## Contributions and "thank you"
|
## Contributions and "thank you"
|
||||||
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
|
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
|
||||||
|
|
||||||
* lanceklinger `Fix for double clicking column handle in results table #1504`
|
|
||||||
* westerncj for `Removed duplicate contribution from README.md (#753)`
|
* westerncj for `Removed duplicate contribution from README.md (#753)`
|
||||||
* ntovas for `Fix for duplicate extensions shown in "Save File" dialog. (#779)`
|
* ntovas for `Fix for duplicate extensions shown in "Save File" dialog. (#779)`
|
||||||
* SebastianPfliegel for `Add cursor snippet (#475)`
|
* SebastianPfliegel for `Add cursor snippet (#475)`
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
|
|
||||||
export class AgentUtils {
|
|
||||||
|
|
||||||
private static _agentService: sqlops.AgentServicesProvider;
|
|
||||||
private static _connectionService: sqlops.ConnectionProvider;
|
|
||||||
private static _queryProvider: sqlops.QueryProvider;
|
|
||||||
|
|
||||||
public static async getAgentService(): Promise<sqlops.AgentServicesProvider> {
|
|
||||||
if (!AgentUtils._agentService) {
|
|
||||||
let currentConnection = await sqlops.connection.getCurrentConnection();
|
|
||||||
this._agentService = sqlops.dataprotocol.getProvider<sqlops.AgentServicesProvider>(currentConnection.providerName, sqlops.DataProviderType.AgentServicesProvider);
|
|
||||||
}
|
|
||||||
return AgentUtils._agentService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async getDatabases(ownerUri: string): Promise<string[]> {
|
|
||||||
if (!AgentUtils._connectionService) {
|
|
||||||
let currentConnection = await sqlops.connection.getCurrentConnection();
|
|
||||||
this._connectionService = sqlops.dataprotocol.getProvider<sqlops.ConnectionProvider>(currentConnection.providerName, sqlops.DataProviderType.ConnectionProvider);
|
|
||||||
}
|
|
||||||
return AgentUtils._connectionService.listDatabases(ownerUri).then(result => {
|
|
||||||
if (result && result.databaseNames && result.databaseNames.length > 0) {
|
|
||||||
return result.databaseNames;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async getQueryProvider(): Promise<sqlops.QueryProvider> {
|
|
||||||
if (!AgentUtils._queryProvider) {
|
|
||||||
let currentConnection = await sqlops.connection.getCurrentConnection();
|
|
||||||
this._queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(currentConnection.providerName, sqlops.DataProviderType.QueryProvider);
|
|
||||||
}
|
|
||||||
return this._queryProvider;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import { AgentUtils } from '../agentUtils';
|
|
||||||
|
|
||||||
export class CreateAlertData {
|
|
||||||
public ownerUri: string;
|
|
||||||
private _alert: sqlops.AgentAlertInfo;
|
|
||||||
|
|
||||||
constructor(ownerUri:string) {
|
|
||||||
this.ownerUri = ownerUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async initialize() {
|
|
||||||
let agentService = await AgentUtils.getAgentService();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async save() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import { AgentUtils } from '../agentUtils';
|
|
||||||
|
|
||||||
export class CreateJobData {
|
|
||||||
|
|
||||||
private readonly JobCompletionActionCondition_Always: string = 'When the job completes';
|
|
||||||
private readonly JobCompletionActionCondition_OnFailure: string = 'When the job fails';
|
|
||||||
private readonly JobCompletionActionCondition_OnSuccess: string = 'When the job succeeds';
|
|
||||||
|
|
||||||
// Error Messages
|
|
||||||
private readonly CreateJobErrorMessage_NameIsEmpty = 'Job name must be provided';
|
|
||||||
|
|
||||||
private _ownerUri: string;
|
|
||||||
private _jobCategories: string[];
|
|
||||||
private _operators: string[];
|
|
||||||
private _agentService: sqlops.AgentServicesProvider;
|
|
||||||
private _defaultOwner: string;
|
|
||||||
private _jobCompletionActionConditions: sqlops.CategoryValue[];
|
|
||||||
|
|
||||||
public name: string;
|
|
||||||
public enabled: boolean = true;
|
|
||||||
public description: string;
|
|
||||||
public category: string;
|
|
||||||
public categoryId: number;
|
|
||||||
public owner: string;
|
|
||||||
public emailLevel: sqlops.JobCompletionActionCondition = sqlops.JobCompletionActionCondition.OnFailure;
|
|
||||||
public pageLevel: sqlops.JobCompletionActionCondition = sqlops.JobCompletionActionCondition.OnFailure;
|
|
||||||
public eventLogLevel: sqlops.JobCompletionActionCondition = sqlops.JobCompletionActionCondition.OnFailure;
|
|
||||||
public deleteLevel: sqlops.JobCompletionActionCondition = sqlops.JobCompletionActionCondition.OnSuccess;
|
|
||||||
public operatorToEmail: string;
|
|
||||||
public operatorToPage: string;
|
|
||||||
public jobSteps: sqlops.AgentJobStepInfo[];
|
|
||||||
public jobSchedules: sqlops.AgentJobScheduleInfo[];
|
|
||||||
public alerts: sqlops.AgentAlertInfo[];
|
|
||||||
|
|
||||||
constructor(ownerUri: string) {
|
|
||||||
this._ownerUri = ownerUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get jobCategories(): string[] {
|
|
||||||
return this._jobCategories;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get operators(): string[] {
|
|
||||||
return this._operators;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get ownerUri(): string {
|
|
||||||
return this._ownerUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get defaultOwner(): string {
|
|
||||||
return this._defaultOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get JobCompletionActionConditions(): sqlops.CategoryValue[] {
|
|
||||||
return this._jobCompletionActionConditions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async initialize() {
|
|
||||||
this._agentService = await AgentUtils.getAgentService();
|
|
||||||
let jobDefaults = await this._agentService.getJobDefaults(this.ownerUri);
|
|
||||||
if (jobDefaults && jobDefaults.success) {
|
|
||||||
this._jobCategories = jobDefaults.categories.map((cat) => {
|
|
||||||
return cat.name;
|
|
||||||
});
|
|
||||||
|
|
||||||
this._defaultOwner = jobDefaults.owner;
|
|
||||||
|
|
||||||
this._operators = ['', this._defaultOwner];
|
|
||||||
}
|
|
||||||
|
|
||||||
this._jobCompletionActionConditions = [{
|
|
||||||
displayName: this.JobCompletionActionCondition_OnSuccess,
|
|
||||||
name: sqlops.JobCompletionActionCondition.OnSuccess.toString()
|
|
||||||
}, {
|
|
||||||
displayName: this.JobCompletionActionCondition_OnFailure,
|
|
||||||
name: sqlops.JobCompletionActionCondition.OnFailure.toString()
|
|
||||||
}, {
|
|
||||||
displayName: this.JobCompletionActionCondition_Always,
|
|
||||||
name: sqlops.JobCompletionActionCondition.Always.toString()
|
|
||||||
}];
|
|
||||||
|
|
||||||
this.jobSchedules = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public async save() {
|
|
||||||
await this._agentService.createJob(this.ownerUri, {
|
|
||||||
name: this.name,
|
|
||||||
owner: this.owner,
|
|
||||||
description: this.description,
|
|
||||||
EmailLevel: this.emailLevel,
|
|
||||||
PageLevel: this.pageLevel,
|
|
||||||
EventLogLevel: this.eventLogLevel,
|
|
||||||
DeleteLevel: this.deleteLevel,
|
|
||||||
OperatorToEmail: this.operatorToEmail,
|
|
||||||
OperatorToPage: this.operatorToPage,
|
|
||||||
enabled: this.enabled,
|
|
||||||
category: this.category,
|
|
||||||
Alerts: this.alerts,
|
|
||||||
JobSchedules: this.jobSchedules,
|
|
||||||
JobSteps: this.jobSteps,
|
|
||||||
// The properties below are not collected from UI
|
|
||||||
// We could consider using a seperate class for create job request
|
|
||||||
//
|
|
||||||
currentExecutionStatus: 0,
|
|
||||||
lastRunOutcome: 0,
|
|
||||||
currentExecutionStep: '',
|
|
||||||
hasTarget: true,
|
|
||||||
hasSchedule: false,
|
|
||||||
hasStep: false,
|
|
||||||
runnable: true,
|
|
||||||
categoryId: 0,
|
|
||||||
categoryType: 1, // LocalJob, hard-coding the value, corresponds to the target tab in SSMS
|
|
||||||
lastRun: '',
|
|
||||||
nextRun: '',
|
|
||||||
jobId: ''
|
|
||||||
}).then(result => {
|
|
||||||
if (!result.success) {
|
|
||||||
console.info(result.errorMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public validate(): { valid: boolean, errorMessages: string[] } {
|
|
||||||
let validationErrors: string[] = [];
|
|
||||||
|
|
||||||
if (!(this.name && this.name.trim())) {
|
|
||||||
validationErrors.push(this.CreateJobErrorMessage_NameIsEmpty);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
valid: validationErrors.length === 0,
|
|
||||||
errorMessages: validationErrors
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public addJobSchedule(schedule: sqlops.AgentJobScheduleInfo) {
|
|
||||||
let existingSchedule = this.jobSchedules.find(item => item.name === schedule.name);
|
|
||||||
if (!existingSchedule) {
|
|
||||||
this.jobSchedules.push(schedule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import { AgentUtils } from '../agentUtils';
|
|
||||||
|
|
||||||
export class CreateScheduleData {
|
|
||||||
public ownerUri: string;
|
|
||||||
public schedules: sqlops.AgentJobScheduleInfo[];
|
|
||||||
public selectedSchedule: sqlops.AgentJobScheduleInfo;
|
|
||||||
|
|
||||||
constructor(ownerUri:string) {
|
|
||||||
this.ownerUri = ownerUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async initialize() {
|
|
||||||
let agentService = await AgentUtils.getAgentService();
|
|
||||||
let result = await agentService.getJobSchedules(this.ownerUri);
|
|
||||||
if (result && result.success) {
|
|
||||||
this.schedules = result.schedules;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async save() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import { AgentUtils } from '../agentUtils';
|
|
||||||
|
|
||||||
export class CreateStepData {
|
|
||||||
public ownerUri: string;
|
|
||||||
public jobId: string; //
|
|
||||||
public jobName: string;
|
|
||||||
public script: string; //
|
|
||||||
public scriptName: string;
|
|
||||||
public stepName: string; //
|
|
||||||
public subSystem: string; //
|
|
||||||
public id: number;
|
|
||||||
public failureAction: string; //
|
|
||||||
public successAction: string; //
|
|
||||||
public failStepId: number;
|
|
||||||
public successStepId: number;
|
|
||||||
public command: string;
|
|
||||||
public commandExecutionSuccessCode: number;
|
|
||||||
public databaseName: string; //
|
|
||||||
public databaseUserName: string;
|
|
||||||
public server: string;
|
|
||||||
public outputFileName: string; //
|
|
||||||
public appendToLogFile: boolean;
|
|
||||||
public appendToStepHist: boolean;
|
|
||||||
public writeLogToTable: boolean;
|
|
||||||
public appendLogToTable: boolean;
|
|
||||||
public retryAttempts: number; //
|
|
||||||
public retryInterval: number; //
|
|
||||||
public proxyName: string;
|
|
||||||
|
|
||||||
constructor(ownerUri:string) {
|
|
||||||
this.ownerUri = ownerUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async save() {
|
|
||||||
let agentService = await AgentUtils.getAgentService();
|
|
||||||
agentService.createJobStep(this.ownerUri, {
|
|
||||||
jobId: this.jobId,
|
|
||||||
jobName: this.jobName,
|
|
||||||
script: this.script,
|
|
||||||
scriptName: this.scriptName,
|
|
||||||
stepName: this.stepName,
|
|
||||||
subSystem: this.subSystem,
|
|
||||||
id: 1,
|
|
||||||
failureAction: this.failureAction,
|
|
||||||
successAction: this.successAction,
|
|
||||||
failStepId: this.failStepId,
|
|
||||||
successStepId: this.successStepId,
|
|
||||||
command: this.command,
|
|
||||||
commandExecutionSuccessCode: this.commandExecutionSuccessCode,
|
|
||||||
databaseName: this.databaseName,
|
|
||||||
databaseUserName: this.databaseUserName,
|
|
||||||
server: this.server,
|
|
||||||
outputFileName: this.outputFileName,
|
|
||||||
appendToLogFile: this.appendToLogFile,
|
|
||||||
appendToStepHist: this.appendToStepHist,
|
|
||||||
writeLogToTable: this.writeLogToTable,
|
|
||||||
appendLogToTable: this.appendLogToTable,
|
|
||||||
retryAttempts: this.retryAttempts,
|
|
||||||
retryInterval: this.retryInterval,
|
|
||||||
proxyName: this.proxyName
|
|
||||||
}).then(result => {
|
|
||||||
console.info(result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import { AgentUtils } from '../agentUtils';
|
|
||||||
|
|
||||||
export class PickScheduleData {
|
|
||||||
public ownerUri: string;
|
|
||||||
public schedules: sqlops.AgentJobScheduleInfo[];
|
|
||||||
public selectedSchedule: sqlops.AgentJobScheduleInfo;
|
|
||||||
|
|
||||||
constructor(ownerUri:string) {
|
|
||||||
this.ownerUri = ownerUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async initialize() {
|
|
||||||
let agentService = await AgentUtils.getAgentService();
|
|
||||||
let result = await agentService.getJobSchedules(this.ownerUri);
|
|
||||||
if (result && result.success) {
|
|
||||||
this.schedules = result.schedules;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async save() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import { CreateAlertData } from '../data/createAlertData';
|
|
||||||
|
|
||||||
export class CreateAlertDialog {
|
|
||||||
|
|
||||||
// Top level
|
|
||||||
private readonly DialogTitle: string = 'Create Alert';
|
|
||||||
private readonly OkButtonText: string = 'OK';
|
|
||||||
private readonly CancelButtonText: string = 'Cancel';
|
|
||||||
private readonly GeneralTabText: string = 'Response';
|
|
||||||
private readonly ResponseTabText: string = 'Steps';
|
|
||||||
private readonly OptionsTabText: string = 'Options';
|
|
||||||
private readonly HistoryTabText: string = 'History';
|
|
||||||
|
|
||||||
// General tab strings
|
|
||||||
private readonly NameTextBoxLabel: string = 'Name';
|
|
||||||
|
|
||||||
// Response tab strings
|
|
||||||
private readonly ExecuteJobTextBoxLabel: string = 'Execute Job';
|
|
||||||
|
|
||||||
// Options tab strings
|
|
||||||
private readonly AdditionalMessageTextBoxLabel: string = 'Additional notification message to send';
|
|
||||||
|
|
||||||
// History tab strings
|
|
||||||
private readonly ResetCountTextBoxLabel: string = 'Reset Count';
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
private dialog: sqlops.window.modelviewdialog.Dialog;
|
|
||||||
private generalTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
private responseTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
private optionsTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
private historyTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
private schedulesTable: sqlops.TableComponent;
|
|
||||||
|
|
||||||
// General tab controls
|
|
||||||
private nameTextBox: sqlops.InputBoxComponent;
|
|
||||||
|
|
||||||
// Response tab controls
|
|
||||||
private executeJobTextBox: sqlops.InputBoxComponent;
|
|
||||||
|
|
||||||
// Options tab controls
|
|
||||||
private additionalMessageTextBox: sqlops.InputBoxComponent;
|
|
||||||
|
|
||||||
// History tab controls
|
|
||||||
private resetCountTextBox: sqlops.InputBoxComponent;
|
|
||||||
|
|
||||||
private model: CreateAlertData;
|
|
||||||
|
|
||||||
private _onSuccess: vscode.EventEmitter<CreateAlertData> = new vscode.EventEmitter<CreateAlertData>();
|
|
||||||
public readonly onSuccess: vscode.Event<CreateAlertData> = this._onSuccess.event;
|
|
||||||
|
|
||||||
constructor(ownerUri: string) {
|
|
||||||
this.model = new CreateAlertData(ownerUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async showDialog() {
|
|
||||||
await this.model.initialize();
|
|
||||||
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
|
|
||||||
this.generalTab = sqlops.window.modelviewdialog.createTab(this.GeneralTabText);
|
|
||||||
this.responseTab = sqlops.window.modelviewdialog.createTab(this.ResponseTabText);
|
|
||||||
this.optionsTab = sqlops.window.modelviewdialog.createTab(this.OptionsTabText);
|
|
||||||
this.historyTab = sqlops.window.modelviewdialog.createTab(this.HistoryTabText);
|
|
||||||
|
|
||||||
this.initializeGeneralTab();
|
|
||||||
this.initializeResponseTab();
|
|
||||||
this.initializeOptionsTab();
|
|
||||||
this.initializeHistoryTab();
|
|
||||||
|
|
||||||
this.dialog.content = [this.generalTab, this.responseTab, this.optionsTab, this.historyTab];
|
|
||||||
this.dialog.okButton.onClick(async () => await this.execute());
|
|
||||||
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
|
||||||
this.dialog.okButton.label = this.OkButtonText;
|
|
||||||
this.dialog.cancelButton.label = this.CancelButtonText;
|
|
||||||
|
|
||||||
sqlops.window.modelviewdialog.openDialog(this.dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeGeneralTab() {
|
|
||||||
this.generalTab.registerContent(async view => {
|
|
||||||
this.nameTextBox = view.modelBuilder.inputBox().component();
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.nameTextBox,
|
|
||||||
title: this.NameTextBoxLabel
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeResponseTab() {
|
|
||||||
this.responseTab.registerContent(async view => {
|
|
||||||
this.executeJobTextBox = view.modelBuilder.inputBox().component();
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.executeJobTextBox,
|
|
||||||
title: this.ExecuteJobTextBoxLabel
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeOptionsTab() {
|
|
||||||
this.optionsTab.registerContent(async view => {
|
|
||||||
this.additionalMessageTextBox = view.modelBuilder.inputBox().component();
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.additionalMessageTextBox,
|
|
||||||
title: this.AdditionalMessageTextBoxLabel
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeHistoryTab() {
|
|
||||||
this.historyTab.registerContent(async view => {
|
|
||||||
this.resetCountTextBox = view.modelBuilder.inputBox().component();
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.resetCountTextBox,
|
|
||||||
title: this.ResetCountTextBoxLabel
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async execute() {
|
|
||||||
this.updateModel();
|
|
||||||
await this.model.save();
|
|
||||||
this._onSuccess.fire(this.model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async cancel() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateModel() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,464 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import { CreateJobData } from '../data/createJobData';
|
|
||||||
import { CreateStepDialog } from './createStepDialog';
|
|
||||||
import { PickScheduleDialog } from './pickScheduleDialog';
|
|
||||||
import { CreateAlertDialog } from './createAlertDialog';
|
|
||||||
|
|
||||||
export class CreateJobDialog {
|
|
||||||
|
|
||||||
// TODO: localize
|
|
||||||
// Top level
|
|
||||||
private readonly DialogTitle: string = 'New Job';
|
|
||||||
private readonly OkButtonText: string = 'OK';
|
|
||||||
private readonly CancelButtonText: string = 'Cancel';
|
|
||||||
private readonly GeneralTabText: string = 'General';
|
|
||||||
private readonly StepsTabText: string = 'Steps';
|
|
||||||
private readonly SchedulesTabText: string = 'Schedules';
|
|
||||||
private readonly AlertsTabText: string = 'Alerts';
|
|
||||||
private readonly NotificationsTabText: string = 'Notifications';
|
|
||||||
|
|
||||||
// General tab strings
|
|
||||||
private readonly NameTextBoxLabel: string = 'Name';
|
|
||||||
private readonly OwnerTextBoxLabel: string = 'Owner';
|
|
||||||
private readonly CategoryDropdownLabel: string = 'Category';
|
|
||||||
private readonly DescriptionTextBoxLabel: string = 'Description';
|
|
||||||
private readonly EnabledCheckboxLabel: string = 'Enabled';
|
|
||||||
|
|
||||||
// Steps tab strings
|
|
||||||
private readonly JobStepsTopLabelString: string = 'Job step list';
|
|
||||||
private readonly StepsTable_StepColumnString: string = 'Step';
|
|
||||||
private readonly StepsTable_NameColumnString: string = 'Name';
|
|
||||||
private readonly StepsTable_TypeColumnString: string = 'Type';
|
|
||||||
private readonly StepsTable_SuccessColumnString: string = 'On Success';
|
|
||||||
private readonly StepsTable_FailureColumnString: string = 'On Failure';
|
|
||||||
private readonly NewStepButtonString: string = 'New...';
|
|
||||||
private readonly InsertStepButtonString: string = 'Insert...';
|
|
||||||
private readonly EditStepButtonString: string = 'Edit';
|
|
||||||
private readonly DeleteStepButtonString: string = 'Delete';
|
|
||||||
|
|
||||||
// Notifications tab strings
|
|
||||||
private readonly NotificationsTabTopLabelString: string = 'Actions to perform when the job completes';
|
|
||||||
private readonly EmailCheckBoxString: string = 'Email';
|
|
||||||
private readonly PagerCheckBoxString: string = 'Page';
|
|
||||||
private readonly EventLogCheckBoxString: string = 'Write to the Windows Application event log';
|
|
||||||
private readonly DeleteJobCheckBoxString: string = 'Automatically delete job';
|
|
||||||
|
|
||||||
// Schedules tab strings
|
|
||||||
private readonly SchedulesTopLabelString: string = 'Schedules list';
|
|
||||||
private readonly PickScheduleButtonString: string = 'Pick Schedule';
|
|
||||||
|
|
||||||
// Alerts tab strings
|
|
||||||
private readonly AlertsTopLabelString: string = 'Alerts list';
|
|
||||||
private readonly NewAlertButtonString: string = 'New Alert';
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
private dialog: sqlops.window.modelviewdialog.Dialog;
|
|
||||||
private generalTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
private stepsTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
private alertsTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
private schedulesTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
private notificationsTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
|
|
||||||
// General tab controls
|
|
||||||
private nameTextBox: sqlops.InputBoxComponent;
|
|
||||||
private ownerTextBox: sqlops.InputBoxComponent;
|
|
||||||
private categoryDropdown: sqlops.DropDownComponent;
|
|
||||||
private descriptionTextBox: sqlops.InputBoxComponent;
|
|
||||||
private enabledCheckBox: sqlops.CheckBoxComponent;
|
|
||||||
|
|
||||||
// Steps tab controls
|
|
||||||
private stepsTable: sqlops.TableComponent;
|
|
||||||
private newStepButton: sqlops.ButtonComponent;
|
|
||||||
private insertStepButton: sqlops.ButtonComponent;
|
|
||||||
private editStepButton: sqlops.ButtonComponent;
|
|
||||||
private deleteStepButton: sqlops.ButtonComponent;
|
|
||||||
|
|
||||||
// Notifications tab controls
|
|
||||||
private notificationsTabTopLabel: sqlops.TextComponent;
|
|
||||||
private emailCheckBox: sqlops.CheckBoxComponent;
|
|
||||||
private emailOperatorDropdown: sqlops.DropDownComponent;
|
|
||||||
private emailConditionDropdown: sqlops.DropDownComponent;
|
|
||||||
private pagerCheckBox: sqlops.CheckBoxComponent;
|
|
||||||
private pagerOperatorDropdown: sqlops.DropDownComponent;
|
|
||||||
private pagerConditionDropdown: sqlops.DropDownComponent;
|
|
||||||
private eventLogCheckBox: sqlops.CheckBoxComponent;
|
|
||||||
private eventLogConditionDropdown: sqlops.DropDownComponent;
|
|
||||||
private deleteJobCheckBox: sqlops.CheckBoxComponent;
|
|
||||||
private deleteJobConditionDropdown: sqlops.DropDownComponent;
|
|
||||||
|
|
||||||
// Schedule tab controls
|
|
||||||
private schedulesTable: sqlops.TableComponent;
|
|
||||||
private pickScheduleButton: sqlops.ButtonComponent;
|
|
||||||
|
|
||||||
// Alert tab controls
|
|
||||||
private alertsTable: sqlops.TableComponent;
|
|
||||||
private newAlertButton: sqlops.ButtonComponent;
|
|
||||||
|
|
||||||
private model: CreateJobData;
|
|
||||||
|
|
||||||
constructor(ownerUri: string) {
|
|
||||||
this.model = new CreateJobData(ownerUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async showDialog() {
|
|
||||||
await this.model.initialize();
|
|
||||||
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
|
|
||||||
this.generalTab = sqlops.window.modelviewdialog.createTab(this.GeneralTabText);
|
|
||||||
this.stepsTab = sqlops.window.modelviewdialog.createTab(this.StepsTabText);
|
|
||||||
this.alertsTab = sqlops.window.modelviewdialog.createTab(this.AlertsTabText);
|
|
||||||
this.schedulesTab = sqlops.window.modelviewdialog.createTab(this.SchedulesTabText);
|
|
||||||
this.notificationsTab = sqlops.window.modelviewdialog.createTab(this.NotificationsTabText);
|
|
||||||
this.initializeGeneralTab();
|
|
||||||
this.initializeStepsTab();
|
|
||||||
this.initializeAlertsTab();
|
|
||||||
this.initializeSchedulesTab();
|
|
||||||
this.initializeNotificationsTab();
|
|
||||||
this.dialog.content = [this.generalTab, this.stepsTab, this.schedulesTab, this.alertsTab, this.notificationsTab];
|
|
||||||
this.dialog.okButton.onClick(async () => await this.execute());
|
|
||||||
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
|
||||||
this.dialog.okButton.label = this.OkButtonText;
|
|
||||||
this.dialog.cancelButton.label = this.CancelButtonText;
|
|
||||||
|
|
||||||
this.dialog.registerCloseValidator(() => {
|
|
||||||
this.updateModel();
|
|
||||||
let validationResult = this.model.validate();
|
|
||||||
if (!validationResult.valid) {
|
|
||||||
// TODO: Show Error Messages
|
|
||||||
console.error(validationResult.errorMessages.join(','));
|
|
||||||
}
|
|
||||||
|
|
||||||
return validationResult.valid;
|
|
||||||
});
|
|
||||||
|
|
||||||
sqlops.window.modelviewdialog.openDialog(this.dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeGeneralTab() {
|
|
||||||
this.generalTab.registerContent(async view => {
|
|
||||||
this.nameTextBox = view.modelBuilder.inputBox().component();
|
|
||||||
this.ownerTextBox = view.modelBuilder.inputBox().component();
|
|
||||||
this.categoryDropdown = view.modelBuilder.dropDown().component();
|
|
||||||
this.descriptionTextBox = view.modelBuilder.inputBox().withProperties({
|
|
||||||
multiline: true,
|
|
||||||
height: 200
|
|
||||||
}).component();
|
|
||||||
this.enabledCheckBox = view.modelBuilder.checkBox()
|
|
||||||
.withProperties({
|
|
||||||
label: this.EnabledCheckboxLabel
|
|
||||||
}).component();
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.nameTextBox,
|
|
||||||
title: this.NameTextBoxLabel
|
|
||||||
}, {
|
|
||||||
component: this.ownerTextBox,
|
|
||||||
title: this.OwnerTextBoxLabel
|
|
||||||
}, {
|
|
||||||
component: this.categoryDropdown,
|
|
||||||
title: this.CategoryDropdownLabel
|
|
||||||
}, {
|
|
||||||
component: this.descriptionTextBox,
|
|
||||||
title: this.DescriptionTextBoxLabel
|
|
||||||
}, {
|
|
||||||
component: this.enabledCheckBox,
|
|
||||||
title: ''
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
|
|
||||||
this.ownerTextBox.value = this.model.defaultOwner;
|
|
||||||
this.categoryDropdown.values = this.model.jobCategories;
|
|
||||||
this.categoryDropdown.value = this.model.jobCategories[0];
|
|
||||||
this.enabledCheckBox.checked = this.model.enabled;
|
|
||||||
this.descriptionTextBox.value = '';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeStepsTab() {
|
|
||||||
this.stepsTab.registerContent(async view => {
|
|
||||||
this.stepsTable = view.modelBuilder.table()
|
|
||||||
.withProperties({
|
|
||||||
columns: [
|
|
||||||
this.StepsTable_StepColumnString,
|
|
||||||
this.StepsTable_NameColumnString,
|
|
||||||
this.StepsTable_TypeColumnString,
|
|
||||||
this.StepsTable_SuccessColumnString,
|
|
||||||
this.StepsTable_FailureColumnString
|
|
||||||
],
|
|
||||||
data: [],
|
|
||||||
height: 800
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.newStepButton = view.modelBuilder.button().withProperties({
|
|
||||||
label: this.NewStepButtonString,
|
|
||||||
width: 80
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.newStepButton.onDidClick((e)=>{
|
|
||||||
let stepDialog = new CreateStepDialog(this.model.ownerUri, '', '', 1, this.model);
|
|
||||||
stepDialog.openNewStepDialog();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.insertStepButton = view.modelBuilder.button().withProperties({
|
|
||||||
label: this.InsertStepButtonString,
|
|
||||||
width: 80
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.editStepButton = view.modelBuilder.button().withProperties({
|
|
||||||
label: this.EditStepButtonString,
|
|
||||||
width: 80
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.deleteStepButton = view.modelBuilder.button().withProperties({
|
|
||||||
label: this.DeleteStepButtonString,
|
|
||||||
width: 80
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.stepsTable,
|
|
||||||
title: this.JobStepsTopLabelString,
|
|
||||||
actions: [this.newStepButton, this.insertStepButton, this.editStepButton, this.deleteStepButton]
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeAlertsTab() {
|
|
||||||
this.alertsTab.registerContent(async view => {
|
|
||||||
this.alertsTable = view.modelBuilder.table()
|
|
||||||
.withProperties({
|
|
||||||
columns: [
|
|
||||||
'Alert Name'
|
|
||||||
],
|
|
||||||
data: [],
|
|
||||||
height: 600,
|
|
||||||
width: 400
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.newAlertButton = view.modelBuilder.button().withProperties({
|
|
||||||
label: this.NewAlertButtonString,
|
|
||||||
width: 80
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.newAlertButton.onDidClick((e)=>{
|
|
||||||
let alertDialog = new CreateAlertDialog(this.model.ownerUri);
|
|
||||||
alertDialog.onSuccess((dialogModel) => {
|
|
||||||
});
|
|
||||||
alertDialog.showDialog();
|
|
||||||
});
|
|
||||||
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.alertsTable,
|
|
||||||
title: this.AlertsTopLabelString,
|
|
||||||
actions: [this.newAlertButton]
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeSchedulesTab() {
|
|
||||||
this.schedulesTab.registerContent(async view => {
|
|
||||||
this.schedulesTable = view.modelBuilder.table()
|
|
||||||
.withProperties({
|
|
||||||
columns: [
|
|
||||||
'Schedule Name'
|
|
||||||
],
|
|
||||||
data: [],
|
|
||||||
height: 600,
|
|
||||||
width: 400
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.pickScheduleButton = view.modelBuilder.button().withProperties({
|
|
||||||
label: this.PickScheduleButtonString,
|
|
||||||
width: 80
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.pickScheduleButton.onDidClick((e)=>{
|
|
||||||
let pickScheduleDialog = new PickScheduleDialog(this.model.ownerUri);
|
|
||||||
pickScheduleDialog.onSuccess((dialogModel) => {
|
|
||||||
let selectedSchedule = dialogModel.selectedSchedule;
|
|
||||||
if (selectedSchedule) {
|
|
||||||
this.model.addJobSchedule(selectedSchedule);
|
|
||||||
this.populateScheduleTable();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
pickScheduleDialog.showDialog();
|
|
||||||
});
|
|
||||||
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.schedulesTable,
|
|
||||||
title: this.SchedulesTopLabelString,
|
|
||||||
actions: [this.pickScheduleButton]
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
|
|
||||||
this.populateScheduleTable();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private populateScheduleTable() {
|
|
||||||
if (this.model.jobSchedules) {
|
|
||||||
let data: any[][] = [];
|
|
||||||
for (let i = 0; i < this.model.jobSchedules.length; ++i) {
|
|
||||||
let schedule = this.model.jobSchedules[i];
|
|
||||||
data[i] = [ schedule.name ];
|
|
||||||
}
|
|
||||||
this.schedulesTable.data = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeNotificationsTab() {
|
|
||||||
this.notificationsTab.registerContent(async view => {
|
|
||||||
|
|
||||||
this.notificationsTabTopLabel = view.modelBuilder.text().withProperties({ value: this.NotificationsTabTopLabelString }).component();
|
|
||||||
this.emailCheckBox = view.modelBuilder.checkBox().withProperties({
|
|
||||||
label: this.EmailCheckBoxString,
|
|
||||||
width: 80
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.pagerCheckBox = view.modelBuilder.checkBox().withProperties({
|
|
||||||
label: this.PagerCheckBoxString,
|
|
||||||
width: 80
|
|
||||||
}).component();
|
|
||||||
this.eventLogCheckBox = view.modelBuilder.checkBox().withProperties({
|
|
||||||
label: this.EventLogCheckBoxString,
|
|
||||||
width: 250
|
|
||||||
}).component();
|
|
||||||
this.deleteJobCheckBox = view.modelBuilder.checkBox().withProperties({
|
|
||||||
label: this.DeleteJobCheckBoxString,
|
|
||||||
width: 250
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this.emailCheckBox.onChanged(() => {
|
|
||||||
this.emailConditionDropdown.enabled = this.emailCheckBox.checked;
|
|
||||||
this.emailOperatorDropdown.enabled = this.emailCheckBox.checked;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.pagerCheckBox.onChanged(() => {
|
|
||||||
this.pagerConditionDropdown.enabled = this.pagerCheckBox.checked;
|
|
||||||
this.pagerOperatorDropdown.enabled = this.pagerCheckBox.checked;
|
|
||||||
});
|
|
||||||
this.eventLogCheckBox.onChanged(() => {
|
|
||||||
this.eventLogConditionDropdown.enabled = this.eventLogCheckBox.checked;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.deleteJobCheckBox.onChanged(() => {
|
|
||||||
this.deleteJobConditionDropdown.enabled = this.deleteJobCheckBox.checked;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.emailOperatorDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
|
|
||||||
this.pagerOperatorDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
|
|
||||||
this.emailConditionDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
|
|
||||||
this.pagerConditionDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
|
|
||||||
this.eventLogConditionDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
|
|
||||||
this.deleteJobConditionDropdown = view.modelBuilder.dropDown().withProperties({ width: 150 }).component();
|
|
||||||
|
|
||||||
let emailContainer = this.createRowContainer(view).withItems([this.emailCheckBox, this.emailOperatorDropdown, this.emailConditionDropdown]).component();
|
|
||||||
|
|
||||||
let pagerContainer = this.createRowContainer(view).withItems([this.pagerCheckBox, this.pagerOperatorDropdown, this.pagerConditionDropdown]).component();
|
|
||||||
|
|
||||||
let eventLogContainer = this.createRowContainer(view).withItems([this.eventLogCheckBox, this.eventLogConditionDropdown]).component();
|
|
||||||
|
|
||||||
let deleteJobContainer = this.createRowContainer(view).withItems([this.deleteJobCheckBox, this.deleteJobConditionDropdown]).component();
|
|
||||||
|
|
||||||
let formModel = view.modelBuilder.formContainer().withFormItems([
|
|
||||||
{
|
|
||||||
component: this.notificationsTabTopLabel,
|
|
||||||
title: ''
|
|
||||||
}, {
|
|
||||||
component: emailContainer,
|
|
||||||
title: ''
|
|
||||||
}, {
|
|
||||||
component: pagerContainer,
|
|
||||||
title: ''
|
|
||||||
}, {
|
|
||||||
component: eventLogContainer,
|
|
||||||
title: ''
|
|
||||||
}, {
|
|
||||||
component: deleteJobContainer,
|
|
||||||
title: ''
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
this.emailConditionDropdown.values = this.model.JobCompletionActionConditions;
|
|
||||||
this.pagerConditionDropdown.values = this.model.JobCompletionActionConditions;
|
|
||||||
this.eventLogConditionDropdown.values = this.model.JobCompletionActionConditions;
|
|
||||||
this.deleteJobConditionDropdown.values = this.model.JobCompletionActionConditions;
|
|
||||||
this.setConditionDropdownSelectedValue(this.emailConditionDropdown, this.model.emailLevel);
|
|
||||||
this.setConditionDropdownSelectedValue(this.pagerConditionDropdown, this.model.pageLevel);
|
|
||||||
this.setConditionDropdownSelectedValue(this.eventLogConditionDropdown, this.model.eventLogLevel);
|
|
||||||
this.setConditionDropdownSelectedValue(this.deleteJobConditionDropdown, this.model.deleteLevel);
|
|
||||||
this.emailOperatorDropdown.values = this.model.operators;
|
|
||||||
this.pagerOperatorDropdown.values = this.model.operators;
|
|
||||||
this.emailCheckBox.checked = false;
|
|
||||||
this.pagerCheckBox.checked = false;
|
|
||||||
this.eventLogCheckBox.checked = false;
|
|
||||||
this.deleteJobCheckBox.checked = false;
|
|
||||||
this.emailOperatorDropdown.enabled = false;
|
|
||||||
this.pagerOperatorDropdown.enabled = false;
|
|
||||||
this.emailConditionDropdown.enabled = false;
|
|
||||||
this.pagerConditionDropdown.enabled = false;
|
|
||||||
this.eventLogConditionDropdown.enabled = false;
|
|
||||||
this.deleteJobConditionDropdown.enabled = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private createRowContainer(view: sqlops.ModelView): sqlops.FlexBuilder {
|
|
||||||
return view.modelBuilder.flexContainer().withLayout({
|
|
||||||
flexFlow: 'row',
|
|
||||||
alignItems: 'left',
|
|
||||||
justifyContent: 'space-between'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async execute() {
|
|
||||||
this.updateModel();
|
|
||||||
await this.model.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async cancel() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private getActualConditionValue(checkbox: sqlops.CheckBoxComponent, dropdown: sqlops.DropDownComponent): sqlops.JobCompletionActionCondition {
|
|
||||||
return checkbox.checked ? Number(this.getDropdownValue(dropdown)) : sqlops.JobCompletionActionCondition.Never;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getDropdownValue(dropdown: sqlops.DropDownComponent): string {
|
|
||||||
return (typeof dropdown.value === 'string') ? dropdown.value : dropdown.value.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private setConditionDropdownSelectedValue(dropdown: sqlops.DropDownComponent, selectedValue: number) {
|
|
||||||
let idx: number = 0;
|
|
||||||
for (idx = 0; idx < dropdown.values.length; idx++) {
|
|
||||||
if (Number((<sqlops.CategoryValue>dropdown.values[idx]).name) === selectedValue) {
|
|
||||||
dropdown.value = dropdown.values[idx];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateModel() {
|
|
||||||
this.model.name = this.nameTextBox.value;
|
|
||||||
this.model.owner = this.ownerTextBox.value;
|
|
||||||
this.model.enabled = this.enabledCheckBox.checked;
|
|
||||||
this.model.description = this.descriptionTextBox.value;
|
|
||||||
this.model.category = this.getDropdownValue(this.categoryDropdown);
|
|
||||||
this.model.emailLevel = this.getActualConditionValue(this.emailCheckBox, this.emailConditionDropdown);
|
|
||||||
this.model.operatorToEmail = this.getDropdownValue(this.emailOperatorDropdown);
|
|
||||||
this.model.operatorToPage = this.getDropdownValue(this.pagerOperatorDropdown);
|
|
||||||
this.model.pageLevel = this.getActualConditionValue(this.pagerCheckBox, this.pagerConditionDropdown);
|
|
||||||
this.model.eventLogLevel = this.getActualConditionValue(this.eventLogCheckBox, this.eventLogConditionDropdown);
|
|
||||||
this.model.deleteLevel = this.getActualConditionValue(this.deleteJobCheckBox, this.deleteJobConditionDropdown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import { CreateScheduleData } from '../data/createScheduleData';
|
|
||||||
|
|
||||||
export class CreateScheduleDialog {
|
|
||||||
|
|
||||||
// Top level
|
|
||||||
private readonly DialogTitle: string = 'New Schedule';
|
|
||||||
private readonly OkButtonText: string = 'OK';
|
|
||||||
private readonly CancelButtonText: string = 'Cancel';
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
private dialog: sqlops.window.modelviewdialog.Dialog;
|
|
||||||
private schedulesTable: sqlops.TableComponent;
|
|
||||||
|
|
||||||
private model: CreateScheduleData;
|
|
||||||
|
|
||||||
private _onSuccess: vscode.EventEmitter<CreateScheduleData> = new vscode.EventEmitter<CreateScheduleData>();
|
|
||||||
public readonly onSuccess: vscode.Event<CreateScheduleData> = this._onSuccess.event;
|
|
||||||
|
|
||||||
constructor(ownerUri: string) {
|
|
||||||
this.model = new CreateScheduleData(ownerUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async showDialog() {
|
|
||||||
await this.model.initialize();
|
|
||||||
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
|
|
||||||
this.initializeContent();
|
|
||||||
this.dialog.okButton.onClick(async () => await this.execute());
|
|
||||||
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
|
||||||
this.dialog.okButton.label = this.OkButtonText;
|
|
||||||
this.dialog.cancelButton.label = this.CancelButtonText;
|
|
||||||
|
|
||||||
sqlops.window.modelviewdialog.openDialog(this.dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeContent() {
|
|
||||||
this.dialog.registerContent(async view => {
|
|
||||||
this.schedulesTable = view.modelBuilder.table()
|
|
||||||
.withProperties({
|
|
||||||
columns: [
|
|
||||||
'Schedule Name'
|
|
||||||
],
|
|
||||||
data: [],
|
|
||||||
height: 600,
|
|
||||||
width: 400
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.schedulesTable,
|
|
||||||
title: 'Schedules'
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
|
|
||||||
if (this.model.schedules) {
|
|
||||||
let data: any[][] = [];
|
|
||||||
for (let i = 0; i < this.model.schedules.length; ++i) {
|
|
||||||
let schedule = this.model.schedules[i];
|
|
||||||
data[i] = [ schedule.name ];
|
|
||||||
}
|
|
||||||
this.schedulesTable.data = data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async execute() {
|
|
||||||
this.updateModel();
|
|
||||||
await this.model.save();
|
|
||||||
this._onSuccess.fire(this.model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async cancel() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateModel() {
|
|
||||||
let selectedRows = this.schedulesTable.selectedRows;
|
|
||||||
if (selectedRows && selectedRows.length > 0) {
|
|
||||||
let selectedRow = selectedRows[0];
|
|
||||||
this.model.selectedSchedule = this.model.schedules[selectedRow];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,421 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
'use strict';
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import { CreateStepData } from '../data/createStepData';
|
|
||||||
import { AgentUtils } from '../agentUtils';
|
|
||||||
import { CreateJobData } from '../data/createJobData';
|
|
||||||
|
|
||||||
export class CreateStepDialog {
|
|
||||||
|
|
||||||
// TODO: localize
|
|
||||||
// Top level
|
|
||||||
//
|
|
||||||
private static readonly DialogTitle: string = 'New Job Step';
|
|
||||||
private static readonly OkButtonText: string = 'OK';
|
|
||||||
private static readonly CancelButtonText: string = 'Cancel';
|
|
||||||
private static readonly GeneralTabText: string = 'General';
|
|
||||||
private static readonly AdvancedTabText: string = 'Advanced';
|
|
||||||
private static readonly OpenCommandText: string = 'Open...';
|
|
||||||
private static readonly ParseCommandText: string = 'Parse';
|
|
||||||
private static readonly NextButtonText: string = 'Next';
|
|
||||||
private static readonly PreviousButtonText: string = 'Previous';
|
|
||||||
private static readonly SuccessAction: string = 'On success action';
|
|
||||||
private static readonly FailureAction: string = 'On failure action';
|
|
||||||
|
|
||||||
|
|
||||||
// Dropdown options
|
|
||||||
private static readonly TSQLScript: string = 'Transact-SQL script (T-SQL)';
|
|
||||||
private static readonly AgentServiceAccount: string = 'SQL Server Agent Service Account';
|
|
||||||
private static readonly NextStep: string = 'Go to the next step';
|
|
||||||
private static readonly QuitJobReportingSuccess: string = 'Quit the job reporting success';
|
|
||||||
private static readonly QuitJobReportingFailure: string = 'Quit the job reporting failure';
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
//
|
|
||||||
private dialog: sqlops.window.modelviewdialog.Dialog;
|
|
||||||
private generalTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
private advancedTab: sqlops.window.modelviewdialog.DialogTab;
|
|
||||||
private nameTextBox: sqlops.InputBoxComponent;
|
|
||||||
private typeDropdown: sqlops.DropDownComponent;
|
|
||||||
private runAsDropdown: sqlops.DropDownComponent;
|
|
||||||
private databaseDropdown: sqlops.DropDownComponent;
|
|
||||||
private successActionDropdown: sqlops.DropDownComponent;
|
|
||||||
private failureActionDropdown: sqlops.DropDownComponent;
|
|
||||||
private commandTextBox: sqlops.InputBoxComponent;
|
|
||||||
private openButton: sqlops.ButtonComponent;
|
|
||||||
private parseButton: sqlops.ButtonComponent;
|
|
||||||
private nextButton: sqlops.ButtonComponent;
|
|
||||||
private previousButton: sqlops.ButtonComponent;
|
|
||||||
private retryAttemptsBox: sqlops.InputBoxComponent;
|
|
||||||
private retryIntervalBox: sqlops.InputBoxComponent;
|
|
||||||
private appendToExistingFileCheckbox: sqlops.CheckBoxComponent;
|
|
||||||
private logToTableCheckbox: sqlops.CheckBoxComponent;
|
|
||||||
private outputFileNameBox: sqlops.InputBoxComponent;
|
|
||||||
private outputFileBrowserButton: sqlops.ButtonComponent;
|
|
||||||
|
|
||||||
private model: CreateStepData;
|
|
||||||
private ownerUri: string;
|
|
||||||
private jobName: string;
|
|
||||||
private server: string;
|
|
||||||
private stepId: number;
|
|
||||||
|
|
||||||
private jobModel: CreateJobData;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
ownerUri: string,
|
|
||||||
jobName: string,
|
|
||||||
server: string,
|
|
||||||
stepId: number,
|
|
||||||
jobModel?: CreateJobData
|
|
||||||
) {
|
|
||||||
this.model = new CreateStepData(ownerUri);
|
|
||||||
this.stepId = stepId;
|
|
||||||
this.ownerUri = ownerUri;
|
|
||||||
this.jobName = jobName;
|
|
||||||
this.server = server;
|
|
||||||
this.jobModel = jobModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeUIComponents() {
|
|
||||||
this.dialog = sqlops.window.modelviewdialog.createDialog(CreateStepDialog.DialogTitle);
|
|
||||||
this.generalTab = sqlops.window.modelviewdialog.createTab(CreateStepDialog.GeneralTabText);
|
|
||||||
this.advancedTab = sqlops.window.modelviewdialog.createTab(CreateStepDialog.AdvancedTabText);
|
|
||||||
this.dialog.content = [this.generalTab, this.advancedTab];
|
|
||||||
this.dialog.okButton.onClick(async () => await this.execute());
|
|
||||||
this.dialog.okButton.label = CreateStepDialog.OkButtonText;
|
|
||||||
this.dialog.cancelButton.label = CreateStepDialog.CancelButtonText;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createCommands(view, queryProvider: sqlops.QueryProvider) {
|
|
||||||
this.openButton = view.modelBuilder.button()
|
|
||||||
.withProperties({
|
|
||||||
label: CreateStepDialog.OpenCommandText,
|
|
||||||
width: '80px'
|
|
||||||
}).component();
|
|
||||||
this.parseButton = view.modelBuilder.button()
|
|
||||||
.withProperties({
|
|
||||||
label: CreateStepDialog.ParseCommandText,
|
|
||||||
width: '80px'
|
|
||||||
}).component();
|
|
||||||
this.parseButton.onDidClick(e => {
|
|
||||||
if (this.commandTextBox.value) {
|
|
||||||
queryProvider.parseSyntax(this.ownerUri, this.commandTextBox.value).then(result => {
|
|
||||||
if (result && result.parseable) {
|
|
||||||
this.dialog.message = { text: 'The command was successfully parsed.', level: 2};
|
|
||||||
} else if (result && !result.parseable) {
|
|
||||||
this.dialog.message = { text: 'The command failed' };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.commandTextBox = view.modelBuilder.inputBox()
|
|
||||||
.withProperties({
|
|
||||||
height: 300,
|
|
||||||
width: 400,
|
|
||||||
multiline: true,
|
|
||||||
inputType: 'text'
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
this.nextButton = view.modelBuilder.button()
|
|
||||||
.withProperties({
|
|
||||||
label: CreateStepDialog.NextButtonText,
|
|
||||||
enabled: false,
|
|
||||||
width: '80px'
|
|
||||||
}).component();
|
|
||||||
this.previousButton = view.modelBuilder.button()
|
|
||||||
.withProperties({
|
|
||||||
label: CreateStepDialog.PreviousButtonText,
|
|
||||||
enabled: false,
|
|
||||||
width: '80px'
|
|
||||||
}).component();
|
|
||||||
}
|
|
||||||
|
|
||||||
private createGeneralTab(databases: string[], queryProvider: sqlops.QueryProvider) {
|
|
||||||
this.generalTab.registerContent(async (view) => {
|
|
||||||
this.nameTextBox = view.modelBuilder.inputBox()
|
|
||||||
.withProperties({
|
|
||||||
}).component();
|
|
||||||
this.nameTextBox.required = true;
|
|
||||||
this.typeDropdown = view.modelBuilder.dropDown()
|
|
||||||
.withProperties({
|
|
||||||
value: CreateStepDialog.TSQLScript,
|
|
||||||
values: [CreateStepDialog.TSQLScript]
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
this.runAsDropdown = view.modelBuilder.dropDown()
|
|
||||||
.withProperties({
|
|
||||||
value: '',
|
|
||||||
values: ['']
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
this.runAsDropdown.enabled = false;
|
|
||||||
this.typeDropdown.onValueChanged((type) => {
|
|
||||||
if (type.selected !== CreateStepDialog.TSQLScript) {
|
|
||||||
this.runAsDropdown.value = CreateStepDialog.AgentServiceAccount;
|
|
||||||
this.runAsDropdown.values = [this.runAsDropdown.value];
|
|
||||||
} else {
|
|
||||||
this.runAsDropdown.value = '';
|
|
||||||
this.runAsDropdown.values = [''];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.databaseDropdown = view.modelBuilder.dropDown()
|
|
||||||
.withProperties({
|
|
||||||
value: databases[0],
|
|
||||||
values: databases
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
// create the commands section
|
|
||||||
this.createCommands(view, queryProvider);
|
|
||||||
|
|
||||||
let buttonContainer = view.modelBuilder.flexContainer()
|
|
||||||
.withLayout({
|
|
||||||
flexFlow: 'row',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
width: 420
|
|
||||||
}).withItems([this.openButton, this.parseButton, this.previousButton, this.nextButton], {
|
|
||||||
flex: '1 1 50%'
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.nameTextBox,
|
|
||||||
title: 'Step name'
|
|
||||||
}, {
|
|
||||||
component: this.typeDropdown,
|
|
||||||
title: 'Type'
|
|
||||||
}, {
|
|
||||||
component: this.runAsDropdown,
|
|
||||||
title: 'Run as'
|
|
||||||
}, {
|
|
||||||
component: this.databaseDropdown,
|
|
||||||
title: 'Database'
|
|
||||||
}, {
|
|
||||||
component: this.commandTextBox,
|
|
||||||
title: 'Command',
|
|
||||||
actions: [buttonContainer]
|
|
||||||
}], {
|
|
||||||
horizontal: false,
|
|
||||||
componentWidth: 420
|
|
||||||
}).component();
|
|
||||||
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
|
||||||
formWrapper.loading = false;
|
|
||||||
await view.initializeModel(formWrapper);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private createRunAsUserOptions(view) {
|
|
||||||
let userInputBox = view.modelBuilder.inputBox()
|
|
||||||
.withProperties({ inputType: 'text', width: '100px' }).component();
|
|
||||||
let viewButton = view.modelBuilder.button()
|
|
||||||
.withProperties({ label: '...', width: '20px' }).component();
|
|
||||||
let viewButtonContainer = view.modelBuilder.flexContainer()
|
|
||||||
.withLayout({ width: 100, textAlign: 'right' })
|
|
||||||
.withItems([viewButton], { flex: '1 1 50%' }).component();
|
|
||||||
let userInputBoxContainer = view.modelBuilder.flexContainer()
|
|
||||||
.withLayout({ width: 200, textAlign: 'left' })
|
|
||||||
.withItems([userInputBox], { flex: '1 1 50%' }).component();
|
|
||||||
let runAsUserContainer = view.modelBuilder.flexContainer()
|
|
||||||
.withLayout({ width: 200 })
|
|
||||||
.withItems([userInputBoxContainer, viewButtonContainer], { flex: '1 1 50%' })
|
|
||||||
.component();
|
|
||||||
let runAsUserForm = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: runAsUserContainer,
|
|
||||||
title: 'Run as user'
|
|
||||||
}], { horizontal: true, componentWidth: 200 }).component();
|
|
||||||
return runAsUserForm;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createAdvancedTab() {
|
|
||||||
this.advancedTab.registerContent(async (view) => {
|
|
||||||
this.successActionDropdown = view.modelBuilder.dropDown()
|
|
||||||
.withProperties({
|
|
||||||
value: CreateStepDialog.NextStep,
|
|
||||||
values: [CreateStepDialog.NextStep, CreateStepDialog.QuitJobReportingSuccess, CreateStepDialog.QuitJobReportingFailure]
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
let retryFlexContainer = this.createRetryCounters(view);
|
|
||||||
this.failureActionDropdown = view.modelBuilder.dropDown()
|
|
||||||
.withProperties({
|
|
||||||
value: CreateStepDialog.QuitJobReportingFailure,
|
|
||||||
values: [CreateStepDialog.QuitJobReportingFailure, CreateStepDialog.NextStep, CreateStepDialog.QuitJobReportingSuccess]
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
let optionsGroup = this.createTSQLOptions(view);
|
|
||||||
let viewButton = view.modelBuilder.button()
|
|
||||||
.withProperties({ label: 'View', width: '50px' }).component();
|
|
||||||
viewButton.enabled = false;
|
|
||||||
this.logToTableCheckbox = view.modelBuilder.checkBox()
|
|
||||||
.withProperties({
|
|
||||||
label: 'Log to table'
|
|
||||||
}).component();
|
|
||||||
let appendToExistingEntryInTableCheckbox = view.modelBuilder.checkBox()
|
|
||||||
.withProperties({ label: 'Append output to existing entry in table' }).component();
|
|
||||||
appendToExistingEntryInTableCheckbox.enabled = false;
|
|
||||||
this.logToTableCheckbox.onChanged(e => {
|
|
||||||
viewButton.enabled = e;
|
|
||||||
appendToExistingEntryInTableCheckbox.enabled = e;
|
|
||||||
});
|
|
||||||
let appendCheckboxContainer = view.modelBuilder.groupContainer()
|
|
||||||
.withItems([appendToExistingEntryInTableCheckbox]).component();
|
|
||||||
let logToTableContainer = view.modelBuilder.flexContainer()
|
|
||||||
.withLayout({ flexFlow: 'row', justifyContent: 'space-between', width: 300 })
|
|
||||||
.withItems([this.logToTableCheckbox, viewButton]).component();
|
|
||||||
let logStepOutputHistoryCheckbox = view.modelBuilder.checkBox()
|
|
||||||
.withProperties({ label: 'Include step output in history' }).component();
|
|
||||||
let runAsUserOptions = this.createRunAsUserOptions(view);
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems(
|
|
||||||
[{
|
|
||||||
component: this.successActionDropdown,
|
|
||||||
title: CreateStepDialog.SuccessAction
|
|
||||||
}, {
|
|
||||||
component: retryFlexContainer,
|
|
||||||
title: ''
|
|
||||||
}, {
|
|
||||||
component: this.failureActionDropdown,
|
|
||||||
title: CreateStepDialog.FailureAction
|
|
||||||
}, {
|
|
||||||
component: optionsGroup,
|
|
||||||
title: 'Transact-SQL script (T-SQL)'
|
|
||||||
}, {
|
|
||||||
component: logToTableContainer,
|
|
||||||
title: ''
|
|
||||||
}, {
|
|
||||||
component: appendCheckboxContainer,
|
|
||||||
title: ' '
|
|
||||||
}, {
|
|
||||||
component: logStepOutputHistoryCheckbox,
|
|
||||||
title: ''
|
|
||||||
}, {
|
|
||||||
component: runAsUserOptions,
|
|
||||||
title: ''
|
|
||||||
}], {
|
|
||||||
componentWidth: 400
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
|
||||||
formWrapper.loading = false;
|
|
||||||
view.initializeModel(formWrapper);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private createRetryCounters(view) {
|
|
||||||
this.retryAttemptsBox = view.modelBuilder.inputBox()
|
|
||||||
.withValidation(component => component.value >= 0)
|
|
||||||
.withProperties({
|
|
||||||
inputType: 'number'
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
this.retryIntervalBox = view.modelBuilder.inputBox()
|
|
||||||
.withValidation(component => component.value >= 0)
|
|
||||||
.withProperties({
|
|
||||||
inputType: 'number'
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
let retryAttemptsContainer = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems(
|
|
||||||
[{
|
|
||||||
component: this.retryAttemptsBox,
|
|
||||||
title: 'Retry Attempts'
|
|
||||||
}], {
|
|
||||||
horizontal: false
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
|
|
||||||
let retryIntervalContainer = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems(
|
|
||||||
[{
|
|
||||||
component: this.retryIntervalBox,
|
|
||||||
title: 'Retry Interval (minutes)'
|
|
||||||
}], {
|
|
||||||
horizontal: false
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
|
|
||||||
let retryFlexContainer = view.modelBuilder.flexContainer()
|
|
||||||
.withLayout({
|
|
||||||
flexFlow: 'row',
|
|
||||||
}).withItems([retryAttemptsContainer, retryIntervalContainer]).component();
|
|
||||||
return retryFlexContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createTSQLOptions(view) {
|
|
||||||
this.outputFileBrowserButton = view.modelBuilder.button()
|
|
||||||
.withProperties({ width: '20px', label: '...' }).component();
|
|
||||||
this.outputFileNameBox = view.modelBuilder.inputBox()
|
|
||||||
.withProperties({
|
|
||||||
width: '100px',
|
|
||||||
inputType: 'text'
|
|
||||||
}).component();
|
|
||||||
let outputViewButton = view.modelBuilder.button()
|
|
||||||
.withProperties({
|
|
||||||
width: '50px',
|
|
||||||
label: 'View'
|
|
||||||
}).component();
|
|
||||||
outputViewButton.enabled = false;
|
|
||||||
let outputButtonContainer = view.modelBuilder.flexContainer()
|
|
||||||
.withLayout({
|
|
||||||
flexFlow: 'row',
|
|
||||||
textAlign: 'right',
|
|
||||||
width: 120
|
|
||||||
}).withItems([this.outputFileBrowserButton, outputViewButton], { flex: '1 1 50%' }).component();
|
|
||||||
let outputFlexBox = view.modelBuilder.flexContainer()
|
|
||||||
.withLayout({
|
|
||||||
flexFlow: 'row',
|
|
||||||
width: 350
|
|
||||||
}).withItems([this.outputFileNameBox, outputButtonContainer], {
|
|
||||||
flex: '1 1 50%'
|
|
||||||
}).component();
|
|
||||||
this.appendToExistingFileCheckbox = view.modelBuilder.checkBox()
|
|
||||||
.withProperties({
|
|
||||||
label: 'Append output to existing file'
|
|
||||||
}).component();
|
|
||||||
this.appendToExistingFileCheckbox.enabled = false;
|
|
||||||
this.outputFileNameBox.onTextChanged((input) => {
|
|
||||||
if (input !== '') {
|
|
||||||
this.appendToExistingFileCheckbox.enabled = true;
|
|
||||||
} else {
|
|
||||||
this.appendToExistingFileCheckbox.enabled = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let outputFileForm = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: outputFlexBox,
|
|
||||||
title: 'Output file'
|
|
||||||
}, {
|
|
||||||
component: this.appendToExistingFileCheckbox,
|
|
||||||
title: ''
|
|
||||||
}], { horizontal: true, componentWidth: 200 }).component();
|
|
||||||
return outputFileForm;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async execute() {
|
|
||||||
this.model.jobName = this.jobName;
|
|
||||||
this.model.id = this.stepId;
|
|
||||||
this.model.server = this.server;
|
|
||||||
this.model.stepName = this.nameTextBox.value;
|
|
||||||
this.model.subSystem = this.typeDropdown.value as string;
|
|
||||||
this.model.databaseName = this.databaseDropdown.value as string;
|
|
||||||
this.model.script = this.commandTextBox.value;
|
|
||||||
this.model.successAction = this.successActionDropdown.value as string;
|
|
||||||
this.model.retryAttempts = +this.retryAttemptsBox.value;
|
|
||||||
this.model.retryInterval = +this.retryIntervalBox.value;
|
|
||||||
this.model.failureAction = this.failureActionDropdown.value as string;
|
|
||||||
this.model.outputFileName = this.outputFileNameBox.value;
|
|
||||||
await this.model.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async openNewStepDialog() {
|
|
||||||
let databases = await AgentUtils.getDatabases(this.ownerUri);
|
|
||||||
let queryProvider = await AgentUtils.getQueryProvider();
|
|
||||||
this.initializeUIComponents();
|
|
||||||
this.createGeneralTab(databases, queryProvider);
|
|
||||||
this.createAdvancedTab();
|
|
||||||
sqlops.window.modelviewdialog.openDialog(this.dialog);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import { PickScheduleData } from '../data/pickScheduleData';
|
|
||||||
|
|
||||||
export class PickScheduleDialog {
|
|
||||||
|
|
||||||
// TODO: localize
|
|
||||||
// Top level
|
|
||||||
private readonly DialogTitle: string = 'Job Schedules';
|
|
||||||
private readonly OkButtonText: string = 'OK';
|
|
||||||
private readonly CancelButtonText: string = 'Cancel';
|
|
||||||
private readonly SchedulesTabText: string = 'Schedules';
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
private dialog: sqlops.window.modelviewdialog.Dialog;
|
|
||||||
private schedulesTable: sqlops.TableComponent;
|
|
||||||
|
|
||||||
private model: PickScheduleData;
|
|
||||||
|
|
||||||
private _onSuccess: vscode.EventEmitter<PickScheduleData> = new vscode.EventEmitter<PickScheduleData>();
|
|
||||||
public readonly onSuccess: vscode.Event<PickScheduleData> = this._onSuccess.event;
|
|
||||||
|
|
||||||
constructor(ownerUri: string) {
|
|
||||||
this.model = new PickScheduleData(ownerUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async showDialog() {
|
|
||||||
await this.model.initialize();
|
|
||||||
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
|
|
||||||
this.initializeContent();
|
|
||||||
this.dialog.okButton.onClick(async () => await this.execute());
|
|
||||||
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
|
||||||
this.dialog.okButton.label = this.OkButtonText;
|
|
||||||
this.dialog.cancelButton.label = this.CancelButtonText;
|
|
||||||
|
|
||||||
sqlops.window.modelviewdialog.openDialog(this.dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeContent() {
|
|
||||||
this.dialog.registerContent(async view => {
|
|
||||||
this.schedulesTable = view.modelBuilder.table()
|
|
||||||
.withProperties({
|
|
||||||
columns: [
|
|
||||||
'Schedule Name'
|
|
||||||
],
|
|
||||||
data: [],
|
|
||||||
height: 600,
|
|
||||||
width: 400
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: this.schedulesTable,
|
|
||||||
title: 'Schedules'
|
|
||||||
}]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
|
|
||||||
if (this.model.schedules) {
|
|
||||||
let data: any[][] = [];
|
|
||||||
for (let i = 0; i < this.model.schedules.length; ++i) {
|
|
||||||
let schedule = this.model.schedules[i];
|
|
||||||
data[i] = [ schedule.name ];
|
|
||||||
}
|
|
||||||
this.schedulesTable.data = data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async execute() {
|
|
||||||
this.updateModel();
|
|
||||||
await this.model.save();
|
|
||||||
this._onSuccess.fire(this.model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async cancel() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateModel() {
|
|
||||||
let selectedRows = this.schedulesTable.selectedRows;
|
|
||||||
if (selectedRows && selectedRows.length > 0) {
|
|
||||||
let selectedRow = selectedRows[0];
|
|
||||||
this.model.selectedSchedule = this.model.schedules[selectedRow];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,10 +6,12 @@
|
|||||||
|
|
||||||
import vscode = require('vscode');
|
import vscode = require('vscode');
|
||||||
import { MainController } from './mainController';
|
import { MainController } from './mainController';
|
||||||
|
import { ApiWrapper } from './apiWrapper';
|
||||||
export let controller: MainController;
|
export let controller: MainController;
|
||||||
|
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
controller = new MainController(context);
|
let apiWrapper = new ApiWrapper();
|
||||||
|
controller = new MainController(context, apiWrapper);
|
||||||
controller.activate();
|
controller.activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,47 +3,24 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { CreateAlertDialog } from './dialogs/createAlertDialog';
|
import * as data from 'sqlops';
|
||||||
import { CreateJobDialog } from './dialogs/createJobDialog';
|
import { ApiWrapper } from './apiWrapper';
|
||||||
import { CreateStepDialog } from './dialogs/createStepDialog';
|
|
||||||
import { PickScheduleDialog } from './dialogs/pickScheduleDialog';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main controller class that initializes the extension
|
* The main controller class that initializes the extension
|
||||||
*/
|
*/
|
||||||
export class MainController {
|
export class MainController {
|
||||||
|
protected _apiWrapper: ApiWrapper;
|
||||||
protected _context: vscode.ExtensionContext;
|
protected _context: vscode.ExtensionContext;
|
||||||
|
|
||||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||||
public constructor(context: vscode.ExtensionContext) {
|
public constructor(context: vscode.ExtensionContext, apiWrapper?: ApiWrapper) {
|
||||||
|
this._apiWrapper = apiWrapper || new ApiWrapper();
|
||||||
this._context = context;
|
this._context = context;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
console.log('Got: ' + apiWrapper);
|
||||||
* Activates the extension
|
|
||||||
*/
|
|
||||||
public activate(): void {
|
|
||||||
vscode.commands.registerCommand('agent.openCreateJobDialog', (ownerUri: string) => {
|
|
||||||
let dialog = new CreateJobDialog(ownerUri);
|
|
||||||
dialog.showDialog();
|
|
||||||
});
|
|
||||||
vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, jobId: string, server: string, stepId: number) => {
|
|
||||||
let dialog = new CreateStepDialog(ownerUri, jobId, server, stepId);
|
|
||||||
dialog.openNewStepDialog();
|
|
||||||
});
|
|
||||||
vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string) => {
|
|
||||||
let dialog = new PickScheduleDialog(ownerUri);
|
|
||||||
dialog.showDialog();
|
|
||||||
});
|
|
||||||
vscode.commands.registerCommand('agent.openCreateAlertDialog', (ownerUri: string) => {
|
|
||||||
let dialog = new CreateAlertDialog(ownerUri);
|
|
||||||
dialog.showDialog();
|
|
||||||
});
|
|
||||||
vscode.commands.registerCommand('agent.openCreateOperatorDialog', (ownerUri: string) => {
|
|
||||||
});
|
|
||||||
vscode.commands.registerCommand('agent.openCreateProxyDialog', (ownerUri: string) => {
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,4 +28,11 @@ export class MainController {
|
|||||||
*/
|
*/
|
||||||
public deactivate(): void {
|
public deactivate(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public activate(): void {
|
||||||
|
|
||||||
|
this._apiWrapper.registerWebviewProvider('data-management-agent', webview => {
|
||||||
|
webview.html = '<div><h1>SQL Agent</h1></div>';
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
extensions/agent/client/src/typings/ref.d.ts
vendored
@@ -5,5 +5,4 @@
|
|||||||
|
|
||||||
/// <reference path='../../../../../src/vs/vscode.d.ts'/>
|
/// <reference path='../../../../../src/vs/vscode.d.ts'/>
|
||||||
/// <reference path='../../../../../src/sql/sqlops.d.ts'/>
|
/// <reference path='../../../../../src/sql/sqlops.d.ts'/>
|
||||||
/// <reference path='../../../../../src/sql/sqlops.proposed.d.ts'/>
|
|
||||||
/// <reference types='@types/node'/>
|
/// <reference types='@types/node'/>
|
||||||
3234
extensions/agent/package-lock.json
generated
@@ -9,12 +9,15 @@
|
|||||||
"icon": "images/sqlserver.png",
|
"icon": "images/sqlserver.png",
|
||||||
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
|
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "0.10.x"
|
"vscode": "0.10.x"
|
||||||
},
|
},
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"*"
|
"*"
|
||||||
],
|
],
|
||||||
"main": "./client/out/main",
|
"main": "./client/out/main",
|
||||||
|
"scripts": {
|
||||||
|
"compile": "gulp compile-extension:agent-client"
|
||||||
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Microsoft/sqlopsstudio.git"
|
"url": "https://github.com/Microsoft/sqlopsstudio.git"
|
||||||
@@ -26,25 +29,22 @@
|
|||||||
"outputChannels": [
|
"outputChannels": [
|
||||||
"sqlagent"
|
"sqlagent"
|
||||||
],
|
],
|
||||||
"commands": [
|
|
||||||
{
|
|
||||||
"command": "agent.openNewStepDialog",
|
|
||||||
"title": "agent.openNewStepDialog"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dashboard.tabs": [
|
"dashboard.tabs": [
|
||||||
{
|
{
|
||||||
"id": "data-management-agent",
|
"id": "data-management-agent",
|
||||||
"description": "Manage and troubleshoot SQL Agent jobs",
|
"description": "Manage and troubleshoot SQL Agent jobs",
|
||||||
"provider": "MSSQL",
|
"provider": "MSSQL",
|
||||||
"title": "SQL Agent",
|
"title": "SQL Agent",
|
||||||
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
|
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
|
||||||
"container": {
|
"container": {
|
||||||
"controlhost-container": {
|
"controlhost-container": {
|
||||||
"type": "agent"
|
"type": "agent"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vscode": "1.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
See [documentation](https://code.visualstudio.com/docs/editor/versioncontrol#_merge-conflicts).
|
See [documentation](https://code.visualstudio.com/docs/editor/versioncontrol#_merge-conflicts).
|
||||||
|
|
||||||
**Notice** This is a an extension that is bundled with SQL Operations Studio.
|
**Notice** This is a an extension that is bundled with Visual Studio Code.
|
||||||
|
|||||||
@@ -658,7 +658,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.9",
|
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.8.2",
|
||||||
"opener": "^1.4.3",
|
"opener": "^1.4.3",
|
||||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
|
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
|
||||||
"vscode-extension-telemetry": "^0.0.15"
|
"vscode-extension-telemetry": "^0.0.15"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||||
"version": "1.5.0-alpha.2",
|
"version": "1.4.0-alpha.45",
|
||||||
"downloadFileNames": {
|
"downloadFileNames": {
|
||||||
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
||||||
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
||||||
@@ -16,4 +16,4 @@
|
|||||||
},
|
},
|
||||||
"installDirectory": "../sqltoolsservice/{#platform#}/{#version#}",
|
"installDirectory": "../sqltoolsservice/{#platform#}/{#version#}",
|
||||||
"executableFiles": ["MicrosoftSqlToolsServiceLayer.exe", "MicrosoftSqlToolsServiceLayer"]
|
"executableFiles": ["MicrosoftSqlToolsServiceLayer.exe", "MicrosoftSqlToolsServiceLayer"]
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ export default class ContextProvider {
|
|||||||
public onDashboardOpen(e: sqlops.DashboardDocument): void {
|
public onDashboardOpen(e: sqlops.DashboardDocument): void {
|
||||||
let iscloud: boolean;
|
let iscloud: boolean;
|
||||||
let edition: number;
|
let edition: number;
|
||||||
if (e.profile.providerName.toLowerCase() === 'mssql' && !types.isUndefinedOrNull(e.serverInfo) && !types.isUndefinedOrNull(e.serverInfo.engineEditionId)) {
|
if (e.profile.providerName.toLowerCase() === 'mssql' && !types.isUndefinedOrNull(e.serverInfo.engineEditionId)) {
|
||||||
if (isCloudEditions.some(i => i === e.serverInfo.engineEditionId)) {
|
if (isCloudEditions.some(i => i === e.serverInfo.engineEditionId)) {
|
||||||
iscloud = true;
|
iscloud = true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -64,10 +64,6 @@ export interface DeleteAgentJobParams {
|
|||||||
job: sqlops.AgentJobInfo;
|
job: sqlops.AgentJobInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobDefaultsParams {
|
|
||||||
ownerUri: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Job Step management parameters
|
// Job Step management parameters
|
||||||
export interface CreateAgentJobStepParams {
|
export interface CreateAgentJobStepParams {
|
||||||
ownerUri: string;
|
ownerUri: string;
|
||||||
@@ -148,27 +144,6 @@ export interface DeleteAgentProxyParams {
|
|||||||
proxy: sqlops.AgentProxyInfo;
|
proxy: sqlops.AgentProxyInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Job Schedule management parameters
|
|
||||||
export interface AgentJobScheduleParams {
|
|
||||||
ownerUri: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateAgentJobScheduleParams {
|
|
||||||
ownerUri: string;
|
|
||||||
schedule: sqlops.AgentJobScheduleInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpdateAgentJobScheduleParams {
|
|
||||||
ownerUri: string;
|
|
||||||
originalScheduleName: string;
|
|
||||||
schedule: sqlops.AgentJobScheduleInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DeleteAgentJobScheduleParams {
|
|
||||||
ownerUri: string;
|
|
||||||
schedule: sqlops.AgentJobScheduleInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Agent Job management requests
|
// Agent Job management requests
|
||||||
export namespace AgentJobsRequest {
|
export namespace AgentJobsRequest {
|
||||||
export const type = new RequestType<AgentJobsParams, sqlops.AgentJobsResult, void, void>('agent/jobs');
|
export const type = new RequestType<AgentJobsParams, sqlops.AgentJobsResult, void, void>('agent/jobs');
|
||||||
@@ -194,10 +169,6 @@ export namespace DeleteAgentJobRequest {
|
|||||||
export const type = new RequestType<DeleteAgentJobParams, sqlops.ResultStatus, void, void>('agent/deletejob');
|
export const type = new RequestType<DeleteAgentJobParams, sqlops.ResultStatus, void, void>('agent/deletejob');
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace AgentJobDefaultsRequest {
|
|
||||||
export const type = new RequestType<AgentJobDefaultsParams, sqlops.AgentJobDefaultsResult, void, void>('agent/jobdefaults');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Job Step requests
|
// Job Step requests
|
||||||
export namespace CreateAgentJobStepRequest {
|
export namespace CreateAgentJobStepRequest {
|
||||||
export const type = new RequestType<CreateAgentJobStepParams, sqlops.CreateAgentJobStepResult, void, void>('agent/createjobstep');
|
export const type = new RequestType<CreateAgentJobStepParams, sqlops.CreateAgentJobStepResult, void, void>('agent/createjobstep');
|
||||||
@@ -262,21 +233,4 @@ export namespace DeleteAgentProxyRequest {
|
|||||||
export const type = new RequestType<DeleteAgentProxyParams, sqlops.ResultStatus, void, void>('agent/deleteproxy');
|
export const type = new RequestType<DeleteAgentProxyParams, sqlops.ResultStatus, void, void>('agent/deleteproxy');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Job Schedules requests
|
|
||||||
export namespace AgentJobSchedulesRequest {
|
|
||||||
export const type = new RequestType<AgentJobScheduleParams, sqlops.AgentJobSchedulesResult, void, void>('agent/schedules');
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace CreateAgentJobScheduleRequest {
|
|
||||||
export const type = new RequestType<CreateAgentJobScheduleParams, sqlops.CreateAgentJobScheduleResult, void, void>('agent/createschedule');
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace UpdateAgentJobScheduleRequest {
|
|
||||||
export const type = new RequestType<UpdateAgentJobScheduleParams, sqlops.UpdateAgentJobScheduleResult, void, void>('agent/updateschedule');
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace DeleteAgentJobScheduleRequest {
|
|
||||||
export const type = new RequestType<DeleteAgentJobScheduleParams, sqlops.ResultStatus, void, void>('agent/deleteschedule');
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------- < Agent Management > ------------------------------------
|
// ------------------------------- < Agent Management > ------------------------------------
|
||||||
|
|||||||
@@ -135,20 +135,6 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let getJobDefaults = (ownerUri: string): Thenable<sqlops.AgentJobDefaultsResult> => {
|
|
||||||
let params: contracts.AgentJobDefaultsParams = {
|
|
||||||
ownerUri: ownerUri
|
|
||||||
};
|
|
||||||
let requestType = contracts.AgentJobDefaultsRequest.type;
|
|
||||||
return client.sendRequest(requestType, params).then(
|
|
||||||
r => r,
|
|
||||||
e => {
|
|
||||||
client.logFailedRequest(requestType, e);
|
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Job Step management methods
|
// Job Step management methods
|
||||||
let createJobStep = (ownerUri: string, stepInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.CreateAgentJobStepResult> => {
|
let createJobStep = (ownerUri: string, stepInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.CreateAgentJobStepResult> => {
|
||||||
let params: contracts.CreateAgentJobStepParams = {
|
let params: contracts.CreateAgentJobStepParams = {
|
||||||
@@ -379,67 +365,6 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Job Schedule management methods
|
|
||||||
let getJobSchedules = (ownerUri: string): Thenable<sqlops.AgentJobSchedulesResult> => {
|
|
||||||
let params: contracts.AgentJobScheduleParams = {
|
|
||||||
ownerUri: ownerUri
|
|
||||||
};
|
|
||||||
let requestType = contracts.AgentJobSchedulesRequest.type;
|
|
||||||
return client.sendRequest(requestType, params).then(
|
|
||||||
r => r,
|
|
||||||
e => {
|
|
||||||
client.logFailedRequest(requestType, e);
|
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
let createJobSchedule = (ownerUri: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.CreateAgentJobScheduleResult> => {
|
|
||||||
let params: contracts.CreateAgentJobScheduleParams = {
|
|
||||||
ownerUri: ownerUri,
|
|
||||||
schedule: scheduleInfo
|
|
||||||
};
|
|
||||||
let requestType = contracts.CreateAgentJobScheduleRequest.type;
|
|
||||||
return client.sendRequest(requestType, params).then(
|
|
||||||
r => r,
|
|
||||||
e => {
|
|
||||||
client.logFailedRequest(requestType, e);
|
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
let updateJobSchedule = (ownerUri: string, originalScheduleName: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.UpdateAgentJobScheduleResult> => {
|
|
||||||
let params: contracts.UpdateAgentJobScheduleParams = {
|
|
||||||
ownerUri: ownerUri,
|
|
||||||
originalScheduleName: originalScheduleName,
|
|
||||||
schedule: scheduleInfo
|
|
||||||
};
|
|
||||||
let requestType = contracts.UpdateAgentJobScheduleRequest.type;
|
|
||||||
return client.sendRequest(requestType, params).then(
|
|
||||||
r => r,
|
|
||||||
e => {
|
|
||||||
client.logFailedRequest(requestType, e);
|
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
let deleteJobSchedule = (ownerUri: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.ResultStatus> => {
|
|
||||||
let params: contracts.DeleteAgentJobScheduleParams = {
|
|
||||||
ownerUri: ownerUri,
|
|
||||||
schedule: scheduleInfo
|
|
||||||
};
|
|
||||||
let requestType = contracts.DeleteAgentJobScheduleRequest.type;
|
|
||||||
return client.sendRequest(requestType, params).then(
|
|
||||||
r => r,
|
|
||||||
e => {
|
|
||||||
client.logFailedRequest(requestType, e);
|
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return sqlops.dataprotocol.registerAgentServicesProvider({
|
return sqlops.dataprotocol.registerAgentServicesProvider({
|
||||||
providerId: client.providerId,
|
providerId: client.providerId,
|
||||||
getJobs,
|
getJobs,
|
||||||
@@ -448,7 +373,6 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
createJob,
|
createJob,
|
||||||
updateJob,
|
updateJob,
|
||||||
deleteJob,
|
deleteJob,
|
||||||
getJobDefaults,
|
|
||||||
createJobStep,
|
createJobStep,
|
||||||
updateJobStep,
|
updateJobStep,
|
||||||
deleteJobStep,
|
deleteJobStep,
|
||||||
@@ -463,11 +387,7 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
getProxies,
|
getProxies,
|
||||||
createProxy,
|
createProxy,
|
||||||
updateProxy,
|
updateProxy,
|
||||||
deleteProxy,
|
deleteProxy
|
||||||
getJobSchedules,
|
|
||||||
createJobSchedule,
|
|
||||||
updateJobSchedule,
|
|
||||||
deleteJobSchedule
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ core-util-is@~1.0.0:
|
|||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||||
|
|
||||||
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.1.9":
|
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.1.7":
|
||||||
version "0.1.9"
|
version "0.1.7"
|
||||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/a1a79895cb79658b75d78aa5cfd745855019148d"
|
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/d50285b03d0d5073c086362c5c96afb279320607"
|
||||||
dependencies:
|
dependencies:
|
||||||
vscode-languageclient "3.5.0"
|
vscode-languageclient "3.5.0"
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,6 @@ Common SQL Profiler use-cases taken from https://docs.microsoft.com/en-us/sql/to
|
|||||||
- Monitoring the performance of SQL Server to tune workloads.
|
- Monitoring the performance of SQL Server to tune workloads.
|
||||||
- Correlating performance counters to diagnose problems.
|
- Correlating performance counters to diagnose problems.
|
||||||
|
|
||||||
## SQL Server Profiler 0.1.1 Release
|
|
||||||
The SQL Server Profiler for SQL Operations Studio *Preview* extension is now available. This is the initial preview release for a new lightweight XEvent-based profiler. The SQL Server Profiler extension tries to make it simple to quickly trace server activity for troubleshooting and monitoring.
|
|
||||||
|
|
||||||
We'll continue to enhance this extension over the next couple releases. Take a look at the below screenshot to see what's currently available.
|
|
||||||
|
|
||||||
<img width="850" src="https://user-images.githubusercontent.com/599935/41578613-fa10e8bc-7347-11e8-8b97-9fb7d186c9f6.png">
|
|
||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "profiler",
|
"name": "profiler",
|
||||||
"displayName": "SQL Server Profiler",
|
"displayName": "SQL Server Profiler",
|
||||||
"description": "SQL Server Profiler for SQL Operations Studio",
|
"description": "SQL Server Profiler for SQL Operations Studio",
|
||||||
"version": "0.1.1",
|
"version": "0.30.0",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
||||||
@@ -32,16 +32,6 @@
|
|||||||
"command": "profiler.newProfiler",
|
"command": "profiler.newProfiler",
|
||||||
"title": "New Profiler",
|
"title": "New Profiler",
|
||||||
"category": "Profiler"
|
"category": "Profiler"
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "profiler.start",
|
|
||||||
"title": "Start",
|
|
||||||
"category": "Profiler"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "profiler.stop",
|
|
||||||
"title": "Stop",
|
|
||||||
"category": "Profiler"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputChannels": [
|
"outputChannels": [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sqlops",
|
"name": "sqlops",
|
||||||
"version": "0.31.1",
|
"version": "0.30.5",
|
||||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Microsoft Corporation"
|
"name": "Microsoft Corporation"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"reflect-metadata": "^0.1.8",
|
"reflect-metadata": "^0.1.8",
|
||||||
"rxjs": "5.4.0",
|
"rxjs": "5.4.0",
|
||||||
"semver": "4.3.6",
|
"semver": "4.3.6",
|
||||||
"slickgrid": "github:anthonydresser/SlickGrid#2.3.22",
|
"slickgrid": "github:anthonydresser/SlickGrid#2.3.16",
|
||||||
"spdlog": "0.6.0",
|
"spdlog": "0.6.0",
|
||||||
"sudo-prompt": "^8.0.0",
|
"sudo-prompt": "^8.0.0",
|
||||||
"svg.js": "^2.2.5",
|
"svg.js": "^2.2.5",
|
||||||
|
|||||||
@@ -27,16 +27,14 @@
|
|||||||
"reportIssueUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=customer%20reported%20issue",
|
"reportIssueUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=customer%20reported%20issue",
|
||||||
"requestFeatureUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=feature-request"
|
"requestFeatureUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=feature-request"
|
||||||
},
|
},
|
||||||
"gettingStartedUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
|
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=862039",
|
||||||
"releaseNotesUrl": "https://go.microsoft.com/fwlink/?linkid=875578",
|
|
||||||
"documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277",
|
"documentationUrl": "https://go.microsoft.com/fwlink/?linkid=862277",
|
||||||
"commit": "9ca6200018fc206d67a47229f991901a8a453781",
|
"commit": "9ca6200018fc206d67a47229f991901a8a453781",
|
||||||
"date": "2017-12-15T12:00:00.000Z",
|
"date": "2017-12-15T12:00:00.000Z",
|
||||||
"recommendedExtensions": [
|
"recommendedExtensions": [
|
||||||
"Microsoft.agent",
|
"Microsoft.agent",
|
||||||
"Microsoft.profiler",
|
|
||||||
"Microsoft.server-report",
|
|
||||||
"Microsoft.whoisactive",
|
"Microsoft.whoisactive",
|
||||||
|
"Microsoft.server-report",
|
||||||
"Redgate.sql-search"
|
"Redgate.sql-search"
|
||||||
],
|
],
|
||||||
"extensionsGallery": {
|
"extensionsGallery": {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ declare @dbsize table
|
|||||||
Free_Space_MB decimal(20,2) default (0))
|
Free_Space_MB decimal(20,2) default (0))
|
||||||
insert into @dbsize
|
insert into @dbsize
|
||||||
(Dbname,file_Size_MB,Space_Used_MB,Free_Space_MB)
|
(Dbname,file_Size_MB,Space_Used_MB,Free_Space_MB)
|
||||||
exec sp_MSforeachdb
|
exec sp_msforeachdb
|
||||||
'use [?];
|
'use [?];
|
||||||
select DB_NAME() AS DbName,
|
select DB_NAME() AS DbName,
|
||||||
sum(size)/128.0 AS File_Size_MB,
|
sum(size)/128.0 AS File_Size_MB,
|
||||||
@@ -24,7 +24,7 @@ declare @logsize table
|
|||||||
log_Free_Space_MB decimal(20,2)default (0))
|
log_Free_Space_MB decimal(20,2)default (0))
|
||||||
insert into @logsize
|
insert into @logsize
|
||||||
(Dbname,Log_File_Size_MB,log_Space_Used_MB,log_Free_Space_MB)
|
(Dbname,Log_File_Size_MB,log_Space_Used_MB,log_Free_Space_MB)
|
||||||
exec sp_MSforeachdb
|
exec sp_msforeachdb
|
||||||
'use [?];
|
'use [?];
|
||||||
select DB_NAME() AS DbName,
|
select DB_NAME() AS DbName,
|
||||||
sum(size)/128.0 AS Log_File_Size_MB,
|
sum(size)/128.0 AS Log_File_Size_MB,
|
||||||
@@ -38,7 +38,7 @@ declare @dbfreesize table
|
|||||||
Freespace varchar(50)default (0.00))
|
Freespace varchar(50)default (0.00))
|
||||||
insert into @dbfreesize
|
insert into @dbfreesize
|
||||||
(name,database_size,Freespace)
|
(name,database_size,Freespace)
|
||||||
exec sp_MSforeachdb
|
exec sp_msforeachdb
|
||||||
'use [?];SELECT database_name = db_name()
|
'use [?];SELECT database_name = db_name()
|
||||||
,database_size = ltrim(str((convert(DECIMAL(15, 2), dbsize) + convert(DECIMAL(15, 2), logsize)) * 8192 / 1048576, 15, 2) + ''MB'')
|
,database_size = ltrim(str((convert(DECIMAL(15, 2), dbsize) + convert(DECIMAL(15, 2), logsize)) * 8192 / 1048576, 15, 2) + ''MB'')
|
||||||
,''unallocated space'' = ltrim(str((
|
,''unallocated space'' = ltrim(str((
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"command": "sqlservices.openDialog",
|
"command": "sqlservices.openDialog",
|
||||||
"title": "sqlservices.openDialog"
|
"title": "openDialog"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "sqlservices.openEditor",
|
"command": "sqlservices.openEditor",
|
||||||
@@ -32,10 +32,6 @@
|
|||||||
{
|
{
|
||||||
"command": "sqlservices.openEditorWithWebView2",
|
"command": "sqlservices.openEditorWithWebView2",
|
||||||
"title": "sqlservices.openEditorWithWebView2"
|
"title": "sqlservices.openEditorWithWebView2"
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "sqlservices.openWizard",
|
|
||||||
"title": "sqlservices.openWizard"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dashboard.tabs": [
|
"dashboard.tabs": [
|
||||||
|
|||||||
@@ -60,212 +60,9 @@ export default class MainController implements vscode.Disposable {
|
|||||||
this.openEditorWithWebview2();
|
this.openEditorWithWebview2();
|
||||||
});
|
});
|
||||||
|
|
||||||
vscode.commands.registerCommand('sqlservices.openWizard', () => {
|
|
||||||
this.openWizard();
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getTabContent(view: sqlops.ModelView, customButton1: sqlops.window.modelviewdialog.Button, customButton2: sqlops.window.modelviewdialog.Button, componentWidth: number | string): Promise<void> {
|
|
||||||
let inputBox = view.modelBuilder.inputBox()
|
|
||||||
.withProperties({
|
|
||||||
multiline: true,
|
|
||||||
height: 100
|
|
||||||
}).component();
|
|
||||||
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
|
|
||||||
inputBoxWrapper.loading = false;
|
|
||||||
customButton1.onClick(() => {
|
|
||||||
inputBoxWrapper.loading = true;
|
|
||||||
setTimeout(() => inputBoxWrapper.loading = false, 5000);
|
|
||||||
});
|
|
||||||
let inputBox2 = view.modelBuilder.inputBox().component();
|
|
||||||
let backupFilesInputBox = view.modelBuilder.inputBox().component();
|
|
||||||
|
|
||||||
let checkbox = view.modelBuilder.checkBox()
|
|
||||||
.withProperties({
|
|
||||||
label: 'Copy-only backup'
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
checkbox.onChanged(e => {
|
|
||||||
console.info("inputBox.enabled " + inputBox.enabled);
|
|
||||||
inputBox.enabled = !inputBox.enabled;
|
|
||||||
});
|
|
||||||
let button = view.modelBuilder.button()
|
|
||||||
.withProperties({
|
|
||||||
label: '+'
|
|
||||||
}).component();
|
|
||||||
let button3 = view.modelBuilder.button()
|
|
||||||
.withProperties({
|
|
||||||
label: '-'
|
|
||||||
|
|
||||||
}).component();
|
|
||||||
let button2 = view.modelBuilder.button()
|
|
||||||
.component();
|
|
||||||
button.onDidClick(e => {
|
|
||||||
backupFilesInputBox.value = 'Button clicked';
|
|
||||||
});
|
|
||||||
let dropdown = view.modelBuilder.dropDown()
|
|
||||||
.withProperties({
|
|
||||||
value: 'Full',
|
|
||||||
values: ['Full', 'Differential', 'Transaction Log']
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
let f = 0;
|
|
||||||
inputBox.onTextChanged((params) => {
|
|
||||||
vscode.window.showInformationMessage(inputBox.value);
|
|
||||||
f = f + 1;
|
|
||||||
inputBox2.value = f.toString();
|
|
||||||
});
|
|
||||||
dropdown.onValueChanged((params) => {
|
|
||||||
vscode.window.showInformationMessage(inputBox2.value);
|
|
||||||
inputBox.value = dropdown.value.toString();
|
|
||||||
});
|
|
||||||
let radioButton = view.modelBuilder.radioButton()
|
|
||||||
.withProperties({
|
|
||||||
value: 'option1',
|
|
||||||
name: 'radioButtonOptions',
|
|
||||||
label: 'Option 1',
|
|
||||||
checked: true
|
|
||||||
//width: 300
|
|
||||||
}).component();
|
|
||||||
let radioButton2 = view.modelBuilder.radioButton()
|
|
||||||
.withProperties({
|
|
||||||
value: 'option2',
|
|
||||||
name: 'radioButtonOptions',
|
|
||||||
label: 'Option 2'
|
|
||||||
}).component();
|
|
||||||
let inputBox3 = view.modelBuilder.inputBox().component();
|
|
||||||
let inputBox4 = view.modelBuilder.inputBox().component();
|
|
||||||
let form2Model = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: inputBox3,
|
|
||||||
title: 'inputBox3'
|
|
||||||
}, {
|
|
||||||
component: inputBox4,
|
|
||||||
title: 'inputBox4'
|
|
||||||
}], {
|
|
||||||
horizontal: true
|
|
||||||
}).component();
|
|
||||||
let groupModel1 = view.modelBuilder.groupContainer()
|
|
||||||
.withLayout({
|
|
||||||
}).withItems([
|
|
||||||
form2Model
|
|
||||||
]).component();
|
|
||||||
radioButton.onDidClick(() => {
|
|
||||||
inputBox.value = radioButton.value;
|
|
||||||
groupModel1.enabled = true;
|
|
||||||
});
|
|
||||||
radioButton2.onDidClick(() => {
|
|
||||||
inputBox.value = radioButton.value;
|
|
||||||
groupModel1.enabled = false;
|
|
||||||
});
|
|
||||||
let table = view.modelBuilder.table().withProperties({
|
|
||||||
data: [
|
|
||||||
['1', '2', '2'],
|
|
||||||
['4', '5', '6'],
|
|
||||||
['7', '8', '9']
|
|
||||||
], columns: ['c1', 'c2', 'c3'],
|
|
||||||
height: 250,
|
|
||||||
selectedRows: [0]
|
|
||||||
}).component();
|
|
||||||
table.onRowSelected(e => {
|
|
||||||
// TODO:
|
|
||||||
});
|
|
||||||
let listBox = view.modelBuilder.listBox().withProperties({
|
|
||||||
values: ['1', '2', '3'],
|
|
||||||
selectedRow: 2
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
let declarativeTable = view.modelBuilder.declarativeTable()
|
|
||||||
.withProperties({
|
|
||||||
columns: [{
|
|
||||||
displayName: 'Column 1',
|
|
||||||
valueType: sqlops.DeclarativeDataType.string,
|
|
||||||
width: '20px',
|
|
||||||
isReadOnly: true
|
|
||||||
}, {
|
|
||||||
displayName: 'Column 2',
|
|
||||||
valueType: sqlops.DeclarativeDataType.string,
|
|
||||||
width: '100px',
|
|
||||||
isReadOnly: false
|
|
||||||
}, {
|
|
||||||
displayName: 'Column 3',
|
|
||||||
valueType: sqlops.DeclarativeDataType.boolean,
|
|
||||||
width: '20px',
|
|
||||||
isReadOnly: false
|
|
||||||
}, {
|
|
||||||
displayName: 'Column 4',
|
|
||||||
valueType: sqlops.DeclarativeDataType.category,
|
|
||||||
isReadOnly: false,
|
|
||||||
width: '120px',
|
|
||||||
categoryValues: [
|
|
||||||
{ name: 'options1', displayName: 'option 1' },
|
|
||||||
{ name: 'options2', displayName: 'option 2' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
data: [
|
|
||||||
['Data00', 'Data01', false, 'options2'],
|
|
||||||
['Data10', 'Data11', true, 'options1']
|
|
||||||
]
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
declarativeTable.onDataChanged(e => {
|
|
||||||
inputBox2.value = e.row.toString() + ' ' + e.column.toString() + ' ' + e.value.toString();
|
|
||||||
inputBox3.value = declarativeTable.data[e.row][e.column];
|
|
||||||
});
|
|
||||||
|
|
||||||
let flexRadioButtonsModel = view.modelBuilder.flexContainer()
|
|
||||||
.withLayout({
|
|
||||||
flexFlow: 'column',
|
|
||||||
alignItems: 'left',
|
|
||||||
height: 150
|
|
||||||
}).withItems([
|
|
||||||
radioButton, groupModel1, radioButton2]
|
|
||||||
, { flex: '1 1 50%' }).component();
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
|
||||||
.withFormItems([{
|
|
||||||
component: inputBoxWrapper,
|
|
||||||
title: 'Backup name'
|
|
||||||
}, {
|
|
||||||
component: inputBox2,
|
|
||||||
title: 'Recovery model'
|
|
||||||
}, {
|
|
||||||
component: dropdown,
|
|
||||||
title: 'Backup type'
|
|
||||||
}, {
|
|
||||||
component: checkbox,
|
|
||||||
title: ''
|
|
||||||
}, {
|
|
||||||
component: backupFilesInputBox,
|
|
||||||
title: 'Backup files',
|
|
||||||
actions: [button, button3]
|
|
||||||
}, {
|
|
||||||
component: flexRadioButtonsModel,
|
|
||||||
title: 'Options'
|
|
||||||
}, {
|
|
||||||
component: declarativeTable,
|
|
||||||
title: 'Declarative Table'
|
|
||||||
}, {
|
|
||||||
component: table,
|
|
||||||
title: 'Table'
|
|
||||||
}, {
|
|
||||||
component: listBox,
|
|
||||||
title: 'List Box'
|
|
||||||
}], {
|
|
||||||
horizontal: false,
|
|
||||||
componentWidth: componentWidth
|
|
||||||
}).component();
|
|
||||||
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
|
||||||
formWrapper.loading = false;
|
|
||||||
customButton2.onClick(() => {
|
|
||||||
formWrapper.loading = true;
|
|
||||||
setTimeout(() => formWrapper.loading = false, 5000);
|
|
||||||
});
|
|
||||||
await view.initializeModel(formWrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
private openDialog(): void {
|
private openDialog(): void {
|
||||||
let dialog = sqlops.window.modelviewdialog.createDialog('Test dialog');
|
let dialog = sqlops.window.modelviewdialog.createDialog('Test dialog');
|
||||||
let tab1 = sqlops.window.modelviewdialog.createTab('Test tab 1');
|
let tab1 = sqlops.window.modelviewdialog.createTab('Test tab 1');
|
||||||
@@ -283,29 +80,207 @@ export default class MainController implements vscode.Disposable {
|
|||||||
customButton2.onClick(() => console.log('button 2 clicked!'));
|
customButton2.onClick(() => console.log('button 2 clicked!'));
|
||||||
dialog.customButtons = [customButton1, customButton2];
|
dialog.customButtons = [customButton1, customButton2];
|
||||||
tab1.registerContent(async (view) => {
|
tab1.registerContent(async (view) => {
|
||||||
await this.getTabContent(view, customButton1, customButton2, 400);
|
let inputBox = view.modelBuilder.inputBox()
|
||||||
|
.withProperties({
|
||||||
|
multiline: true,
|
||||||
|
height: 100
|
||||||
|
}).component();
|
||||||
|
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
|
||||||
|
inputBoxWrapper.loading = false;
|
||||||
|
customButton1.onClick(() => {
|
||||||
|
inputBoxWrapper.loading = true;
|
||||||
|
setTimeout(() => inputBoxWrapper.loading = false, 5000);
|
||||||
|
});
|
||||||
|
let inputBox2 = view.modelBuilder.inputBox().component();
|
||||||
|
let backupFilesInputBox = view.modelBuilder.inputBox().component();
|
||||||
|
|
||||||
|
let checkbox = view.modelBuilder.checkBox()
|
||||||
|
.withProperties({
|
||||||
|
label: 'Copy-only backup'
|
||||||
|
})
|
||||||
|
.component();
|
||||||
|
checkbox.onChanged(e => {
|
||||||
|
console.info("inputBox.enabled " + inputBox.enabled);
|
||||||
|
inputBox.enabled = !inputBox.enabled;
|
||||||
|
});
|
||||||
|
let button = view.modelBuilder.button()
|
||||||
|
.withProperties({
|
||||||
|
label: '+'
|
||||||
|
}).component();
|
||||||
|
let button3 = view.modelBuilder.button()
|
||||||
|
.withProperties({
|
||||||
|
label: '-'
|
||||||
|
|
||||||
|
}).component();
|
||||||
|
let button2 = view.modelBuilder.button()
|
||||||
|
.component();
|
||||||
|
button.onDidClick(e => {
|
||||||
|
backupFilesInputBox.value = 'Button clicked';
|
||||||
|
});
|
||||||
|
let dropdown = view.modelBuilder.dropDown()
|
||||||
|
.withProperties({
|
||||||
|
value: 'Full',
|
||||||
|
values: ['Full', 'Differential', 'Transaction Log']
|
||||||
|
})
|
||||||
|
.component();
|
||||||
|
let f = 0;
|
||||||
|
inputBox.onTextChanged((params) => {
|
||||||
|
vscode.window.showInformationMessage(inputBox.value);
|
||||||
|
f = f + 1;
|
||||||
|
inputBox2.value = f.toString();
|
||||||
|
});
|
||||||
|
dropdown.onValueChanged((params) => {
|
||||||
|
vscode.window.showInformationMessage(inputBox2.value);
|
||||||
|
inputBox.value = dropdown.value;
|
||||||
|
});
|
||||||
|
let radioButton = view.modelBuilder.radioButton()
|
||||||
|
.withProperties({
|
||||||
|
value: 'option1',
|
||||||
|
name: 'radioButtonOptions',
|
||||||
|
label: 'Option 1',
|
||||||
|
checked: true
|
||||||
|
//width: 300
|
||||||
|
}).component();
|
||||||
|
let radioButton2 = view.modelBuilder.radioButton()
|
||||||
|
.withProperties({
|
||||||
|
value: 'option2',
|
||||||
|
name: 'radioButtonOptions',
|
||||||
|
label: 'Option 2'
|
||||||
|
}).component();
|
||||||
|
let inputBox3 = view.modelBuilder.inputBox().component();
|
||||||
|
let inputBox4 = view.modelBuilder.inputBox().component();
|
||||||
|
let form2Model = view.modelBuilder.formContainer()
|
||||||
|
.withFormItems([{
|
||||||
|
component: inputBox3,
|
||||||
|
title: 'inputBox3'
|
||||||
|
}, {
|
||||||
|
component: inputBox4,
|
||||||
|
title: 'inputBox4'
|
||||||
|
}], {
|
||||||
|
horizontal: true
|
||||||
|
}).component();
|
||||||
|
let groupModel1 = view.modelBuilder.groupContainer()
|
||||||
|
.withLayout({
|
||||||
|
}).withItems([
|
||||||
|
form2Model
|
||||||
|
]).component();
|
||||||
|
radioButton.onDidClick(() => {
|
||||||
|
inputBox.value = radioButton.value;
|
||||||
|
groupModel1.enabled = true;
|
||||||
|
});
|
||||||
|
radioButton2.onDidClick(() => {
|
||||||
|
inputBox.value = radioButton.value;
|
||||||
|
groupModel1.enabled = false;
|
||||||
|
});
|
||||||
|
let table = view.modelBuilder.table().withProperties({
|
||||||
|
data: [
|
||||||
|
['1', '2', '2'],
|
||||||
|
['4', '5', '6'],
|
||||||
|
['7', '8', '9']
|
||||||
|
], columns: ['c1', 'c2', 'c3'],
|
||||||
|
height: 250,
|
||||||
|
selectedRows: [0]
|
||||||
|
}).component();
|
||||||
|
table.onRowSelected(e => {
|
||||||
|
// TODO:
|
||||||
|
});
|
||||||
|
let listBox = view.modelBuilder.listBox().withProperties({
|
||||||
|
values: ['1', '2', '3'],
|
||||||
|
selectedRow: 2
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
let declarativeTable = view.modelBuilder.declarativeTable()
|
||||||
|
.withProperties({
|
||||||
|
columns: [{
|
||||||
|
displayName: 'Column 1',
|
||||||
|
valueType: sqlops.DeclarativeDataType.string,
|
||||||
|
width: '20px',
|
||||||
|
isReadOnly: true
|
||||||
|
}, {
|
||||||
|
displayName: 'Column 2',
|
||||||
|
valueType: sqlops.DeclarativeDataType.string,
|
||||||
|
width: '100px',
|
||||||
|
isReadOnly: false
|
||||||
|
}, {
|
||||||
|
displayName: 'Column 3',
|
||||||
|
valueType: sqlops.DeclarativeDataType.boolean,
|
||||||
|
width: '20px',
|
||||||
|
isReadOnly: false
|
||||||
|
}, {
|
||||||
|
displayName: 'Column 4',
|
||||||
|
valueType: sqlops.DeclarativeDataType.category,
|
||||||
|
isReadOnly: false,
|
||||||
|
width: '120px',
|
||||||
|
categoryValues: [
|
||||||
|
{ name: 'options1', displayName: 'option 1' },
|
||||||
|
{ name: 'options2', displayName: 'option 2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
data: [
|
||||||
|
['Data00', 'Data01', false, 'options2'],
|
||||||
|
['Data10', 'Data11', true, 'options1']
|
||||||
|
]
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
declarativeTable.onDataChanged(e => {
|
||||||
|
inputBox2.value = e.row.toString() + ' ' + e.column.toString() + ' ' + e.value.toString();
|
||||||
|
inputBox3.value = declarativeTable.data[e.row][e.column];
|
||||||
|
});
|
||||||
|
|
||||||
|
let flexRadioButtonsModel = view.modelBuilder.flexContainer()
|
||||||
|
.withLayout({
|
||||||
|
flexFlow: 'column',
|
||||||
|
alignItems: 'left',
|
||||||
|
height: 150
|
||||||
|
}).withItems([
|
||||||
|
radioButton, groupModel1, radioButton2]
|
||||||
|
, { flex: '1 1 50%' }).component();
|
||||||
|
let formModel = view.modelBuilder.formContainer()
|
||||||
|
.withFormItems([{
|
||||||
|
component: inputBoxWrapper,
|
||||||
|
title: 'Backup name'
|
||||||
|
}, {
|
||||||
|
component: inputBox2,
|
||||||
|
title: 'Recovery model'
|
||||||
|
}, {
|
||||||
|
component: dropdown,
|
||||||
|
title: 'Backup type'
|
||||||
|
}, {
|
||||||
|
component: checkbox,
|
||||||
|
title: ''
|
||||||
|
}, {
|
||||||
|
component: backupFilesInputBox,
|
||||||
|
title: 'Backup files',
|
||||||
|
actions: [button, button3]
|
||||||
|
}, {
|
||||||
|
component: flexRadioButtonsModel,
|
||||||
|
title: 'Options'
|
||||||
|
}, {
|
||||||
|
component: declarativeTable,
|
||||||
|
title: 'Declarative Table'
|
||||||
|
}, {
|
||||||
|
component: table,
|
||||||
|
title: 'Table'
|
||||||
|
}, {
|
||||||
|
component: listBox,
|
||||||
|
title: 'List Box'
|
||||||
|
}], {
|
||||||
|
horizontal: false,
|
||||||
|
componentWidth: 400
|
||||||
|
}).component();
|
||||||
|
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
||||||
|
formWrapper.loading = false;
|
||||||
|
customButton2.onClick(() => {
|
||||||
|
formWrapper.loading = true;
|
||||||
|
setTimeout(() => formWrapper.loading = false, 5000);
|
||||||
|
});
|
||||||
|
await view.initializeModel(formWrapper);
|
||||||
});
|
});
|
||||||
|
|
||||||
sqlops.window.modelviewdialog.openDialog(dialog);
|
sqlops.window.modelviewdialog.openDialog(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
private openWizard(): void {
|
|
||||||
let wizard = sqlops.window.modelviewdialog.createWizard('Test wizard');
|
|
||||||
let page1 = sqlops.window.modelviewdialog.createWizardPage('First wizard page');
|
|
||||||
let page2 = sqlops.window.modelviewdialog.createWizardPage('Second wizard page');
|
|
||||||
page2.content = 'sqlservices';
|
|
||||||
let customButton1 = sqlops.window.modelviewdialog.createButton('Load name');
|
|
||||||
customButton1.onClick(() => console.log('button 1 clicked!'));
|
|
||||||
let customButton2 = sqlops.window.modelviewdialog.createButton('Load all');
|
|
||||||
customButton2.onClick(() => console.log('button 2 clicked!'));
|
|
||||||
wizard.customButtons = [customButton1, customButton2];
|
|
||||||
page1.registerContent(async (view) => {
|
|
||||||
await this.getTabContent(view, customButton1, customButton2, 800);
|
|
||||||
});
|
|
||||||
wizard.pages = [page1, page2];
|
|
||||||
wizard.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
private openEditor(): void {
|
private openEditor(): void {
|
||||||
let editor = sqlops.workspace.createModelViewEditor('Test Model View');
|
let editor = sqlops.workspace.createModelViewEditor('Test Model View');
|
||||||
editor.registerContent(async view => {
|
editor.registerContent(async view => {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class ToggleDropdownAction extends Action {
|
|||||||
private static readonly ID = 'dropdownAction.toggle';
|
private static readonly ID = 'dropdownAction.toggle';
|
||||||
private static readonly ICON = 'dropdown-arrow';
|
private static readonly ICON = 'dropdown-arrow';
|
||||||
|
|
||||||
constructor(private _fn: () => any, label: string) {
|
constructor(label, private _fn: () => any) {
|
||||||
super(ToggleDropdownAction.ID, label, ToggleDropdownAction.ICON);
|
super(ToggleDropdownAction.ID, label, ToggleDropdownAction.ICON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,10 +53,6 @@ export interface IDropdownOptions extends IDropdownStyles {
|
|||||||
* Value to use as aria-label for the input box
|
* Value to use as aria-label for the input box
|
||||||
*/
|
*/
|
||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
/**
|
|
||||||
* Label for the dropdown action
|
|
||||||
*/
|
|
||||||
actionLabel: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDropdownStyles {
|
export interface IDropdownStyles {
|
||||||
@@ -70,8 +66,7 @@ const defaults: IDropdownOptions = {
|
|||||||
strictSelection: true,
|
strictSelection: true,
|
||||||
maxHeight: 300,
|
maxHeight: 300,
|
||||||
errorMessage: errorMessage,
|
errorMessage: errorMessage,
|
||||||
contextBorder: Color.fromHex('#696969'),
|
contextBorder: Color.fromHex('#696969')
|
||||||
actionLabel: nls.localize('dropdownAction.toggle', "Toggle dropdown")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ListResource {
|
interface ListResource {
|
||||||
@@ -120,11 +115,11 @@ export class Dropdown extends Disposable {
|
|||||||
this.$input = $('.dropdown-input').style('width', '100%').appendTo(this.$el);
|
this.$input = $('.dropdown-input').style('width', '100%').appendTo(this.$el);
|
||||||
this.$treeContainer = $('.dropdown-tree');
|
this.$treeContainer = $('.dropdown-tree');
|
||||||
|
|
||||||
this._toggleAction = new ToggleDropdownAction(() => {
|
this._toggleAction = new ToggleDropdownAction(nls.localize('dropdown.toggle', '{0} Toggle Dropdown', this._options.ariaLabel), () => {
|
||||||
this._showList();
|
this._showList();
|
||||||
this._tree.domFocus();
|
this._tree.domFocus();
|
||||||
this._tree.focusFirst();
|
this._tree.focusFirst();
|
||||||
}, opt.actionLabel);
|
});
|
||||||
|
|
||||||
this._input = new InputBox(this.$input.getHTMLElement(), contextViewService, {
|
this._input = new InputBox(this.$input.getHTMLElement(), contextViewService, {
|
||||||
validationOptions: {
|
validationOptions: {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
import { InputBox as vsInputBox, IInputOptions, IInputBoxStyles as vsIInputBoxStyles, IMessage } from 'vs/base/browser/ui/inputbox/inputBox';
|
import { InputBox as vsInputBox, IInputOptions, IInputBoxStyles as vsIInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||||
import { Color } from 'vs/base/common/color';
|
import { Color } from 'vs/base/common/color';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
@@ -33,7 +33,6 @@ export class InputBox extends vsInputBox {
|
|||||||
public onLoseFocus: Event<OnLoseFocusParams> = this._onLoseFocus.event;
|
public onLoseFocus: Event<OnLoseFocusParams> = this._onLoseFocus.event;
|
||||||
|
|
||||||
private _isTextAreaInput: boolean;
|
private _isTextAreaInput: boolean;
|
||||||
private _hideErrors = false;
|
|
||||||
|
|
||||||
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) {
|
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) {
|
||||||
super(container, contextViewProvider, options);
|
super(container, contextViewProvider, options);
|
||||||
@@ -104,21 +103,4 @@ export class InputBox extends vsInputBox {
|
|||||||
public isEnabled(): boolean {
|
public isEnabled(): boolean {
|
||||||
return !this.inputElement.hasAttribute('disabled');
|
return !this.inputElement.hasAttribute('disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hideErrors(): boolean {
|
|
||||||
return this._hideErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
public set hideErrors(hideErrors: boolean) {
|
|
||||||
this._hideErrors = hideErrors;
|
|
||||||
if (hideErrors) {
|
|
||||||
this.hideMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public showMessage(message: IMessage, force?: boolean): void {
|
|
||||||
if (!this.hideErrors) {
|
|
||||||
super.showMessage(message, force);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -172,22 +172,4 @@
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
|
||||||
|
|
||||||
.modal .modal-footer .dialogErrorMessage {
|
|
||||||
align-items: center;
|
|
||||||
max-height: 30px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .dialogErrorMessage .icon {
|
|
||||||
float: left;
|
|
||||||
margin-right: 10px;
|
|
||||||
width: auto;
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .modal-footer .dialogErrorMessage .errorMessage {
|
|
||||||
max-height: 100%;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
}
|
||||||
@@ -21,13 +21,9 @@ import { Button } from 'sql/base/browser/ui/button/button';
|
|||||||
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
|
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
|
||||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes';
|
|
||||||
|
|
||||||
export const MODAL_SHOWING_KEY = 'modalShowing';
|
export const MODAL_SHOWING_KEY = 'modalShowing';
|
||||||
export const MODAL_SHOWING_CONTEXT = new RawContextKey<Array<string>>(MODAL_SHOWING_KEY, []);
|
export const MODAL_SHOWING_CONTEXT = new RawContextKey<Array<string>>(MODAL_SHOWING_KEY, []);
|
||||||
const INFO_ALT_TEXT = localize('infoAltText', 'Info');
|
|
||||||
const WARNING_ALT_TEXT = localize('warningAltText', 'Warning');
|
|
||||||
const ERROR_ALT_TEXT = localize('errorAltText', 'Error');
|
|
||||||
|
|
||||||
export interface IModalDialogStyles {
|
export interface IModalDialogStyles {
|
||||||
dialogForeground?: Color;
|
dialogForeground?: Color;
|
||||||
@@ -149,7 +145,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
|||||||
/**
|
/**
|
||||||
* Build and render the modal, will call {@link Modal#renderBody}
|
* Build and render the modal, will call {@link Modal#renderBody}
|
||||||
*/
|
*/
|
||||||
public render(errorMessagesInFooter: boolean = false) {
|
public render() {
|
||||||
let modalBodyClass = (this._modalOptions.isAngular === false ? 'modal-body' : 'modal-body-and-footer');
|
let modalBodyClass = (this._modalOptions.isAngular === false ? 'modal-body' : 'modal-body-and-footer');
|
||||||
let parts: Array<HTMLElement> = [];
|
let parts: Array<HTMLElement> = [];
|
||||||
// This modal header section refers to the header of of the dialog
|
// This modal header section refers to the header of of the dialog
|
||||||
@@ -186,6 +182,17 @@ export abstract class Modal extends Disposable implements IThemable {
|
|||||||
|
|
||||||
this.renderBody(body.getHTMLElement());
|
this.renderBody(body.getHTMLElement());
|
||||||
|
|
||||||
|
if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) {
|
||||||
|
body.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => {
|
||||||
|
errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => {
|
||||||
|
this._errorIconElement = iconContainer.getHTMLElement();
|
||||||
|
this._errorIconElement.style.visibility = 'hidden';
|
||||||
|
});
|
||||||
|
errorMessageContainer.div({ class: 'errorMessage' }, (messageContainer) => {
|
||||||
|
this._errorMessage = messageContainer;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
// This modal footer section refers to the footer of of the dialog
|
// This modal footer section refers to the footer of of the dialog
|
||||||
if (this._modalOptions.isAngular === false) {
|
if (this._modalOptions.isAngular === false) {
|
||||||
this._modalFooterSection = $().div({ class: 'modal-footer' }, (modelFooter) => {
|
this._modalFooterSection = $().div({ class: 'modal-footer' }, (modelFooter) => {
|
||||||
@@ -214,19 +221,6 @@ export abstract class Modal extends Disposable implements IThemable {
|
|||||||
builderClass += ' wide';
|
builderClass += ' wide';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) {
|
|
||||||
let builder = errorMessagesInFooter ? this._leftFooter : body;
|
|
||||||
builder.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => {
|
|
||||||
errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => {
|
|
||||||
this._errorIconElement = iconContainer.getHTMLElement();
|
|
||||||
this._errorIconElement.style.visibility = 'hidden';
|
|
||||||
});
|
|
||||||
errorMessageContainer.div({ class: 'errorMessage' }, (messageContainer) => {
|
|
||||||
this._errorMessage = messageContainer;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// The builder builds the dialog. It append header, body and footer sections.
|
// The builder builds the dialog. It append header, body and footer sections.
|
||||||
this._builder = $().div({ class: builderClass, 'role': 'dialog' }, (dialogContainer) => {
|
this._builder = $().div({ class: builderClass, 'role': 'dialog' }, (dialogContainer) => {
|
||||||
this._modalDialog = dialogContainer.div({ class: 'modal-dialog ', role: 'document' }, (modalDialog) => {
|
this._modalDialog = dialogContainer.div({ class: 'modal-dialog ', role: 'document' }, (modalDialog) => {
|
||||||
@@ -361,33 +355,14 @@ export abstract class Modal extends Disposable implements IThemable {
|
|||||||
* Show an error in the error message element
|
* Show an error in the error message element
|
||||||
* @param err Text to show in the error message
|
* @param err Text to show in the error message
|
||||||
*/
|
*/
|
||||||
protected setError(err: string, level: MessageLevel = MessageLevel.Error) {
|
protected setError(err: string) {
|
||||||
if (this._modalOptions.hasErrors) {
|
if (this._modalOptions.hasErrors) {
|
||||||
if (err === '') {
|
if (err === '') {
|
||||||
this._errorIconElement.style.visibility = 'hidden';
|
this._errorIconElement.style.visibility = 'hidden';
|
||||||
} else {
|
} else {
|
||||||
const levelClasses = ['info', 'warning', 'error'];
|
|
||||||
let selectedLevel = levelClasses[2];
|
|
||||||
let altText = ERROR_ALT_TEXT;
|
|
||||||
if (level === MessageLevel.Information) {
|
|
||||||
selectedLevel = levelClasses[0];
|
|
||||||
altText = INFO_ALT_TEXT;
|
|
||||||
} else if (level === MessageLevel.Warning) {
|
|
||||||
selectedLevel = levelClasses[1];
|
|
||||||
altText = WARNING_ALT_TEXT;
|
|
||||||
}
|
|
||||||
levelClasses.forEach(level => {
|
|
||||||
if (selectedLevel === level) {
|
|
||||||
this._errorIconElement.classList.add(level);
|
|
||||||
} else {
|
|
||||||
this._errorIconElement.classList.remove(level);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this._errorIconElement.title = altText;
|
|
||||||
this._errorIconElement.style.visibility = 'visible';
|
this._errorIconElement.style.visibility = 'visible';
|
||||||
}
|
}
|
||||||
this._errorMessage.text(err);
|
this._errorMessage.innerHtml(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -216,19 +216,4 @@
|
|||||||
|
|
||||||
.vs .icon.unpin {
|
.vs .icon.unpin {
|
||||||
background: url('unpin.svg') center center no-repeat;
|
background: url('unpin.svg') center center no-repeat;
|
||||||
}
|
|
||||||
|
|
||||||
.small {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.medium {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.large {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
}
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-blue{fill:#1BA1E2;} .icon-white{fill:#FFFFFF;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 8c0-4.418 3.582-8 8-8s8 3.582 8 8-3.582 8-8 8-8-3.582-8-8z" id="outline"/><path class="icon-vs-blue" d="M8 1c-3.865 0-7 3.135-7 7s3.135 7 7 7 7-3.135 7-7-3.135-7-7-7zm1 12h-2v-7h2v7zm0-8h-2v-2h2v2z" id="iconBg"/><path class="icon-white" d="M7 6h2v7h-2v-7zm0-1h2v-2h-2v2z" id="iconFg"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-blue{fill:#1BA1E2;} .icon-white{fill:#FFFFFF;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 8c0-4.418 3.582-8 8-8s8 3.582 8 8-3.582 8-8 8-8-3.582-8-8z" id="outline"/><path class="icon-vs-blue" d="M8 1c-3.865 0-7 3.135-7 7s3.135 7 7 7 7-3.135 7-7-3.135-7-7-7zm1 12h-2v-7h2v7zm0-8h-2v-2h2v2z" id="iconBg"/><path class="icon-white" d="M7 6h2v7h-2v-7zm0-1h2v-2h-2v2z" id="iconFg"/></svg>
|
||||||
|
Before Width: | Height: | Size: 624 B After Width: | Height: | Size: 627 B |
@@ -6,14 +6,12 @@
|
|||||||
import { NgModule, Inject, forwardRef, ApplicationRef, ComponentFactoryResolver, Type } from '@angular/core';
|
import { NgModule, Inject, forwardRef, ApplicationRef, ComponentFactoryResolver, Type } from '@angular/core';
|
||||||
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
|
|
||||||
import { IBootstrapParams, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
import { CreateLoginComponent, CREATELOGIN_SELECTOR } from 'sql/parts/admin/security/createLogin.component';
|
||||||
import { CreateLoginComponent } from 'sql/parts/admin/security/createLogin.component';
|
|
||||||
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
|
|
||||||
// Connection Dashboard main angular module
|
// Connection Dashboard main angular module
|
||||||
export const CreateLoginModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
|
export const CreateLoginModule = (params: IBootstrapParams, selector: string): Type<any> => {
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -26,8 +24,7 @@ export const CreateLoginModule = (params: IBootstrapParams, selector: string, in
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||||
{ provide: IBootstrapParams, useValue: params },
|
{ provide: IBootstrapParams, useValue: params }
|
||||||
...providerIterator(instantiationService)
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
class ModuleClass {
|
class ModuleClass {
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ export function convertEditorInput(input: EditorInput, options: IQueryEditorOpti
|
|||||||
let uri: URI = getQueryEditorFileUri(input);
|
let uri: URI = getQueryEditorFileUri(input);
|
||||||
if (uri) {
|
if (uri) {
|
||||||
const queryResultsInput: QueryResultsInput = instantiationService.createInstance(QueryResultsInput, uri.toString());
|
const queryResultsInput: QueryResultsInput = instantiationService.createInstance(QueryResultsInput, uri.toString());
|
||||||
let queryInput: QueryInput = instantiationService.createInstance(QueryInput, '', input, queryResultsInput, undefined);
|
let queryInput: QueryInput = instantiationService.createInstance(QueryInput, input.getName(), '', input, queryResultsInput, undefined);
|
||||||
return queryInput;
|
return queryInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
//QueryPlanInput
|
//QueryPlanInput
|
||||||
uri = getQueryPlanEditorUri(input);
|
uri = getQueryPlanEditorUri(input);
|
||||||
if (uri) {
|
if(uri) {
|
||||||
let queryPlanXml: string = fs.readFileSync(uri.fsPath);
|
let queryPlanXml: string = fs.readFileSync(uri.fsPath);
|
||||||
let queryPlanInput: QueryPlanInput = instantiationService.createInstance(QueryPlanInput, queryPlanXml, 'aaa', undefined);
|
let queryPlanInput: QueryPlanInput = instantiationService.createInstance(QueryPlanInput, queryPlanXml, 'aaa', undefined);
|
||||||
return queryPlanInput;
|
return queryPlanInput;
|
||||||
@@ -60,14 +60,14 @@ export function convertEditorInput(input: EditorInput, options: IQueryEditorOpti
|
|||||||
*/
|
*/
|
||||||
export function getSupportedInputResource(input: IEditorInput): URI {
|
export function getSupportedInputResource(input: IEditorInput): URI {
|
||||||
if (input instanceof UntitledEditorInput) {
|
if (input instanceof UntitledEditorInput) {
|
||||||
let untitledCast: UntitledEditorInput = <UntitledEditorInput>input;
|
let untitledCast: UntitledEditorInput = <UntitledEditorInput> input;
|
||||||
if (untitledCast) {
|
if (untitledCast) {
|
||||||
return untitledCast.getResource();
|
return untitledCast.getResource();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input instanceof FileEditorInput) {
|
if (input instanceof FileEditorInput) {
|
||||||
let fileCast: FileEditorInput = <FileEditorInput>input;
|
let fileCast: FileEditorInput = <FileEditorInput> input;
|
||||||
if (fileCast) {
|
if (fileCast) {
|
||||||
return fileCast.getResource();
|
return fileCast.getResource();
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ function getQueryEditorFileUri(input: EditorInput): URI {
|
|||||||
if (uri) {
|
if (uri) {
|
||||||
let isValidUri: boolean = !!uri && !!uri.toString;
|
let isValidUri: boolean = !!uri && !!uri.toString;
|
||||||
|
|
||||||
if (isValidUri && (hasFileExtension(sqlFileTypes, input, true) || hasSqlFileMode(input))) {
|
if (isValidUri && (hasFileExtension(sqlFileTypes, input, true) || hasSqlFileMode(input)) ) {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ function getQueryPlanEditorUri(input: EditorInput): URI {
|
|||||||
// If this editor is not already of type queryinput
|
// If this editor is not already of type queryinput
|
||||||
if (!(input instanceof QueryPlanInput)) {
|
if (!(input instanceof QueryPlanInput)) {
|
||||||
let uri: URI = getSupportedInputResource(input);
|
let uri: URI = getSupportedInputResource(input);
|
||||||
if (uri) {
|
if(uri) {
|
||||||
if (hasFileExtension(sqlPlanFileTypes, input, false)) {
|
if (hasFileExtension(sqlPlanFileTypes, input, false)) {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ function getQueryPlanEditorUri(input: EditorInput): URI {
|
|||||||
*/
|
*/
|
||||||
function hasSqlFileMode(input: EditorInput): boolean {
|
function hasSqlFileMode(input: EditorInput): boolean {
|
||||||
if (input instanceof UntitledEditorInput) {
|
if (input instanceof UntitledEditorInput) {
|
||||||
let untitledCast: UntitledEditorInput = <UntitledEditorInput>input;
|
let untitledCast: UntitledEditorInput = <UntitledEditorInput> input;
|
||||||
return untitledCast && (untitledCast.getModeId() === undefined || untitledCast.getModeId() === sqlModeId);
|
return untitledCast && (untitledCast.getModeId() === undefined || untitledCast.getModeId() === sqlModeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ export const capabilitiesOptions = 'OPTIONS_METADATA';
|
|||||||
export const configMaxRecentConnections = 'maxRecentConnections';
|
export const configMaxRecentConnections = 'maxRecentConnections';
|
||||||
|
|
||||||
export const mssqlProviderName = 'MSSQL';
|
export const mssqlProviderName = 'MSSQL';
|
||||||
export const anyProviderName = '*';
|
|
||||||
export const connectionProviderContextKey = 'connectionProvider';
|
|
||||||
|
|
||||||
export const applicationName = 'sqlops';
|
export const applicationName = 'sqlops';
|
||||||
|
|
||||||
|
|||||||
@@ -172,8 +172,7 @@ export class ConnectionWidget {
|
|||||||
strictSelection: false,
|
strictSelection: false,
|
||||||
placeholder: this._defaultDatabaseName,
|
placeholder: this._defaultDatabaseName,
|
||||||
maxHeight: 125,
|
maxHeight: 125,
|
||||||
ariaLabel: databaseOption.displayName,
|
ariaLabel: databaseOption.displayName
|
||||||
actionLabel: localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let serverGroupLabel = localize('serverGroup', 'Server group');
|
let serverGroupLabel = localize('serverGroup', 'Server group');
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ import { WEBVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWebvi
|
|||||||
import { MODELVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardModelViewContainer.contribution';
|
import { MODELVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardModelViewContainer.contribution';
|
||||||
import { CONTROLHOST_CONTAINER } from 'sql/parts/dashboard/containers/dashboardControlHostContainer.contribution';
|
import { CONTROLHOST_CONTAINER } from 'sql/parts/dashboard/containers/dashboardControlHostContainer.contribution';
|
||||||
import { NAV_SECTION } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
|
import { NAV_SECTION } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
|
||||||
import { IDashboardContainerRegistry, Extensions as DashboardContainerExtensions } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
|
import { IDashboardContainerRegistry, Extensions as DashboardContainerExtensions, IDashboardContainer, registerContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
|
||||||
|
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
|
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import * as Constants from 'sql/parts/connection/common/constants';
|
|
||||||
|
|
||||||
const dashboardcontainerRegistry = Registry.as<IDashboardContainerRegistry>(DashboardContainerExtensions.dashboardContainerContributions);
|
const dashboardcontainerRegistry = Registry.as<IDashboardContainerRegistry>(DashboardContainerExtensions.dashboardContainerContributions);
|
||||||
const containerTypes = [
|
const containerTypes = [
|
||||||
@@ -136,17 +136,13 @@ export function addProvider<T extends { connectionManagementService: SingleConne
|
|||||||
*/
|
*/
|
||||||
export function addEdition<T extends { connectionManagementService: SingleConnectionManagementService }>(config: WidgetConfig[], collection: DashboardServiceInterface): Array<WidgetConfig> {
|
export function addEdition<T extends { connectionManagementService: SingleConnectionManagementService }>(config: WidgetConfig[], collection: DashboardServiceInterface): Array<WidgetConfig> {
|
||||||
let connectionInfo: ConnectionManagementInfo = collection.connectionManagementService.connectionInfo;
|
let connectionInfo: ConnectionManagementInfo = collection.connectionManagementService.connectionInfo;
|
||||||
if (connectionInfo.serverInfo) {
|
let edition = connectionInfo.serverInfo.engineEditionId;
|
||||||
let edition = connectionInfo.serverInfo.engineEditionId;
|
return config.map((item) => {
|
||||||
return config.map((item) => {
|
if (item.edition === undefined) {
|
||||||
if (item.edition === undefined) {
|
item.edition = edition;
|
||||||
item.edition = edition;
|
}
|
||||||
}
|
return item;
|
||||||
return item;
|
});
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,11 +162,9 @@ export function addContext(config: WidgetConfig[], collection: any, context: str
|
|||||||
* Returns a filtered version of the widgets passed based on edition and provider
|
* Returns a filtered version of the widgets passed based on edition and provider
|
||||||
* @param config widgets to filter
|
* @param config widgets to filter
|
||||||
*/
|
*/
|
||||||
export function filterConfigs<T extends { provider?: string | string[], when?: string }, K extends { contextKeyService: IContextKeyService }>(config: T[], collection: K): Array<T> {
|
export function filterConfigs<T extends { when?: string }, K extends { contextKeyService: IContextKeyService }>(config: T[], collection: K): Array<T> {
|
||||||
return config.filter((item) => {
|
return config.filter((item) => {
|
||||||
if (!hasCompatibleProvider(item.provider, collection.contextKeyService)) {
|
if (!item.when) {
|
||||||
return false;
|
|
||||||
} else if (!item.when) {
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return collection.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(item.when));
|
return collection.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(item.when));
|
||||||
@@ -178,21 +172,6 @@ export function filterConfigs<T extends { provider?: string | string[], when?: s
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the listed providers contain '*' indicating any provider will do, or that they are a match
|
|
||||||
* for the currently scoped 'connectionProvider' context key.
|
|
||||||
*/
|
|
||||||
function hasCompatibleProvider(provider: string | string[], contextKeyService: IContextKeyService): boolean {
|
|
||||||
let isCompatible = true;
|
|
||||||
let connectionProvider = contextKeyService.getContextKeyValue<string>(Constants.connectionProviderContextKey);
|
|
||||||
if (connectionProvider) {
|
|
||||||
let providers = (provider instanceof Array) ? provider : [provider];
|
|
||||||
let matchingProvider = providers.find((p) => p === connectionProvider || p === Constants.anyProviderName);
|
|
||||||
isCompatible = (matchingProvider !== undefined);
|
|
||||||
} // Else there's no connection context so skip the check
|
|
||||||
return isCompatible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get registered container if it is specified as the key
|
* Get registered container if it is specified as the key
|
||||||
* @param container dashboard container
|
* @param container dashboard container
|
||||||
|
|||||||
@@ -17,12 +17,11 @@ import { IDashboardRegistry, Extensions as DashboardExtensions, IDashboardTab }
|
|||||||
import { PinUnpinTabAction, AddFeatureTabAction } from './actions';
|
import { PinUnpinTabAction, AddFeatureTabAction } from './actions';
|
||||||
import { TabComponent, TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
import { TabComponent, TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||||
import { AngularEventType, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
|
import { AngularEventType, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
|
||||||
import { DashboardTab, IConfigModifierCollection } from 'sql/parts/dashboard/common/interfaces';
|
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||||
import * as dashboardHelper from 'sql/parts/dashboard/common/dashboardHelper';
|
import * as dashboardHelper from 'sql/parts/dashboard/common/dashboardHelper';
|
||||||
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
|
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
|
||||||
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
|
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
|
||||||
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||||
import * as Constants from 'sql/parts/connection/common/constants';
|
|
||||||
|
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import * as types from 'vs/base/common/types';
|
import * as types from 'vs/base/common/types';
|
||||||
@@ -39,11 +38,16 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|||||||
|
|
||||||
const dashboardRegistry = Registry.as<IDashboardRegistry>(DashboardExtensions.DashboardContributions);
|
const dashboardRegistry = Registry.as<IDashboardRegistry>(DashboardExtensions.DashboardContributions);
|
||||||
|
|
||||||
|
interface IConfigModifierCollection {
|
||||||
|
connectionManagementService: SingleConnectionManagementService;
|
||||||
|
contextKeyService: IContextKeyService;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'dashboard-page',
|
selector: 'dashboard-page',
|
||||||
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/common/dashboardPage.component.html'))
|
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/common/dashboardPage.component.html'))
|
||||||
})
|
})
|
||||||
export abstract class DashboardPage extends AngularDisposable implements IConfigModifierCollection {
|
export abstract class DashboardPage extends AngularDisposable {
|
||||||
|
|
||||||
protected tabs: Array<TabConfig> = [];
|
protected tabs: Array<TabConfig> = [];
|
||||||
|
|
||||||
@@ -133,10 +137,21 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
|||||||
this._tabsDispose.forEach(i => i.dispose());
|
this._tabsDispose.forEach(i => i.dispose());
|
||||||
this._tabsDispose = [];
|
this._tabsDispose = [];
|
||||||
|
|
||||||
let allTabs = dashboardHelper.filterConfigs(dashboardRegistry.tabs, this);
|
// Create home tab
|
||||||
|
let homeTab: TabConfig = {
|
||||||
|
id: 'homeTab',
|
||||||
|
publisher: undefined,
|
||||||
|
title: this.homeTabTitle,
|
||||||
|
container: { 'widgets-container': homeWidgets },
|
||||||
|
context: this.context,
|
||||||
|
originalConfig: this._originalConfig,
|
||||||
|
editable: true,
|
||||||
|
canClose: false,
|
||||||
|
actions: []
|
||||||
|
};
|
||||||
|
this.addNewTab(homeTab);
|
||||||
|
|
||||||
// Before separating tabs into pinned / shown, ensure that the home tab is always set up as expected
|
let allTabs = dashboardHelper.filterConfigs(dashboardRegistry.tabs, this);
|
||||||
allTabs = this.setAndRemoveHomeTab(allTabs, homeWidgets);
|
|
||||||
|
|
||||||
// Load tab setting configs
|
// Load tab setting configs
|
||||||
this._tabSettingConfigs = this.dashboardService.getSettings<Array<TabSettingConfig>>([this.context, 'tabs'].join('.'));
|
this._tabSettingConfigs = this.dashboardService.getSettings<Array<TabSettingConfig>>([this.context, 'tabs'].join('.'));
|
||||||
@@ -184,32 +199,6 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private setAndRemoveHomeTab(allTabs: IDashboardTab[], homeWidgets: WidgetConfig[]): IDashboardTab[] {
|
|
||||||
let homeTabConfig: TabConfig = {
|
|
||||||
id: 'homeTab',
|
|
||||||
provider: Constants.anyProviderName,
|
|
||||||
publisher: undefined,
|
|
||||||
title: this.homeTabTitle,
|
|
||||||
container: { 'widgets-container': homeWidgets },
|
|
||||||
context: this.context,
|
|
||||||
originalConfig: this._originalConfig,
|
|
||||||
editable: true,
|
|
||||||
canClose: false,
|
|
||||||
actions: []
|
|
||||||
};
|
|
||||||
|
|
||||||
let homeTabIndex = allTabs.findIndex((tab) => tab.isHomeTab === true);
|
|
||||||
if (homeTabIndex !== undefined && homeTabIndex > -1) {
|
|
||||||
// Have a tab: get its information and copy over to the home tab definition
|
|
||||||
let homeTab = allTabs.splice(homeTabIndex, 1)[0];
|
|
||||||
let tabConfig = this.initTabComponents(homeTab);
|
|
||||||
homeTabConfig.id = tabConfig.id;
|
|
||||||
homeTabConfig.container = tabConfig.container;
|
|
||||||
}
|
|
||||||
this.addNewTab(homeTabConfig);
|
|
||||||
return allTabs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private rewriteConfig(): void {
|
private rewriteConfig(): void {
|
||||||
let writeableConfig = objects.deepClone(this._tabSettingConfigs);
|
let writeableConfig = objects.deepClone(this._tabSettingConfigs);
|
||||||
|
|
||||||
@@ -219,12 +208,35 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
|||||||
|
|
||||||
private loadNewTabs(dashboardTabs: IDashboardTab[], openLastTab: boolean = false) {
|
private loadNewTabs(dashboardTabs: IDashboardTab[], openLastTab: boolean = false) {
|
||||||
if (dashboardTabs && dashboardTabs.length > 0) {
|
if (dashboardTabs && dashboardTabs.length > 0) {
|
||||||
let selectedTabs = dashboardTabs.map(v => this.initTabComponents(v)).map(v => {
|
let selectedTabs = dashboardTabs.map(v => {
|
||||||
|
let containerResult = dashboardHelper.getDashboardContainer(v.container);
|
||||||
|
if (!containerResult.result) {
|
||||||
|
return { id: v.id, title: v.title, container: { 'error-container': undefined }, alwaysShow: v.alwaysShow };
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = Object.keys(containerResult.container)[0];
|
||||||
|
if (key === WIDGETS_CONTAINER || key === GRID_CONTAINER) {
|
||||||
|
let configs = <WidgetConfig[]>Object.values(containerResult.container)[0];
|
||||||
|
this._configModifiers.forEach(cb => {
|
||||||
|
configs = cb.apply(this, [configs, this.dashboardService, this.context]);
|
||||||
|
});
|
||||||
|
this._gridModifiers.forEach(cb => {
|
||||||
|
configs = cb.apply(this, [configs]);
|
||||||
|
});
|
||||||
|
if (key === WIDGETS_CONTAINER) {
|
||||||
|
return { id: v.id, title: v.title, container: { 'widgets-container': configs }, alwaysShow: v.alwaysShow };
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return { id: v.id, title: v.title, container: { 'grid-container': configs }, alwaysShow: v.alwaysShow };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { id: v.id, title: v.title, container: containerResult.container, alwaysShow: v.alwaysShow };
|
||||||
|
}).map(v => {
|
||||||
let actions = [];
|
let actions = [];
|
||||||
let tabSettingConfig = this._tabSettingConfigs.find(i => i.tabId === v.id);
|
let tabConfig = this._tabSettingConfigs.find(i => i.tabId === v.id);
|
||||||
let isPinned = false;
|
let isPinned = false;
|
||||||
if (tabSettingConfig) {
|
if (tabConfig) {
|
||||||
isPinned = tabSettingConfig.isPinned;
|
isPinned = tabConfig.isPinned;
|
||||||
} else if (v.alwaysShow) {
|
} else if (v.alwaysShow) {
|
||||||
isPinned = true;
|
isPinned = true;
|
||||||
}
|
}
|
||||||
@@ -249,30 +261,6 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private initTabComponents(value: IDashboardTab): { id: string; title: string; container: object; alwaysShow: boolean; } {
|
|
||||||
let containerResult = dashboardHelper.getDashboardContainer(value.container);
|
|
||||||
if (!containerResult.result) {
|
|
||||||
return { id: value.id, title: value.title, container: { 'error-container': undefined }, alwaysShow: value.alwaysShow };
|
|
||||||
}
|
|
||||||
let key = Object.keys(containerResult.container)[0];
|
|
||||||
if (key === WIDGETS_CONTAINER || key === GRID_CONTAINER) {
|
|
||||||
let configs = <WidgetConfig[]>Object.values(containerResult.container)[0];
|
|
||||||
this._configModifiers.forEach(cb => {
|
|
||||||
configs = cb.apply(this, [configs, this, this.context]);
|
|
||||||
});
|
|
||||||
this._gridModifiers.forEach(cb => {
|
|
||||||
configs = cb.apply(this, [configs]);
|
|
||||||
});
|
|
||||||
if (key === WIDGETS_CONTAINER) {
|
|
||||||
return { id: value.id, title: value.title, container: { 'widgets-container': configs }, alwaysShow: value.alwaysShow };
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return { id: value.id, title: value.title, container: { 'grid-container': configs }, alwaysShow: value.alwaysShow };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { id: value.id, title: value.title, container: containerResult.container, alwaysShow: value.alwaysShow };
|
|
||||||
}
|
|
||||||
|
|
||||||
private getContentType(tab: TabConfig): string {
|
private getContentType(tab: TabConfig): string {
|
||||||
return tab.container ? Object.keys(tab.container)[0] : '';
|
return tab.container ? Object.keys(tab.container)[0] : '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,8 @@
|
|||||||
import 'vs/css!./dashboardPanel';
|
import 'vs/css!./dashboardPanel';
|
||||||
|
|
||||||
import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||||
import {
|
import { TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_ACTIVE_BORDER, TAB_INACTIVE_BACKGROUND, TAB_INACTIVE_FOREGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND, TAB_BORDER } from 'vs/workbench/common/theme';
|
||||||
TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_ACTIVE_BORDER, TAB_INACTIVE_BACKGROUND,
|
import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||||
TAB_INACTIVE_FOREGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND, TAB_BORDER, EDITOR_GROUP_BORDER
|
|
||||||
} from 'vs/workbench/common/theme';
|
|
||||||
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
|
||||||
|
|
||||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||||
|
|
||||||
@@ -101,14 +98,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const divider = theme.getColor(EDITOR_GROUP_BORDER);
|
|
||||||
if (divider) {
|
|
||||||
collector.addRule(`
|
|
||||||
panel.dashboard-panel > .tabbedPanel > .title .tabList .tab-header {
|
|
||||||
border-right-width: 1px;
|
|
||||||
border-right-style: solid;
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
@@ -7,7 +7,6 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
|||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import * as types from 'vs/base/common/types';
|
import * as types from 'vs/base/common/types';
|
||||||
|
|
||||||
import * as Constants from 'sql/parts/connection/common/constants';
|
|
||||||
import { registerTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
import { registerTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
import { generateContainerTypeSchemaProperties } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
|
import { generateContainerTypeSchemaProperties } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
|
||||||
import { NAV_SECTION, validateNavSectionContributionAndRegisterIcon } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
|
import { NAV_SECTION, validateNavSectionContributionAndRegisterIcon } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
|
||||||
@@ -18,11 +17,9 @@ export interface IDashboardTabContrib {
|
|||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
container: object;
|
container: object;
|
||||||
provider: string | string[];
|
|
||||||
when?: string;
|
when?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
alwaysShow?: boolean;
|
alwaysShow?: boolean;
|
||||||
isHomeTab?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabSchema: IJSONSchema = {
|
const tabSchema: IJSONSchema = {
|
||||||
@@ -44,11 +41,6 @@ const tabSchema: IJSONSchema = {
|
|||||||
description: localize('sqlops.extension.contributes.tab.when', 'Condition which must be true to show this item'),
|
description: localize('sqlops.extension.contributes.tab.when', 'Condition which must be true to show this item'),
|
||||||
type: 'string'
|
type: 'string'
|
||||||
},
|
},
|
||||||
provider: {
|
|
||||||
description: localize('sqlops.extension.contributes.tab.provider', 'Defines the connection types this tab is compatible with. Defaults to "MSSQL" if not set'),
|
|
||||||
type: ['string', 'array']
|
|
||||||
|
|
||||||
},
|
|
||||||
container: {
|
container: {
|
||||||
description: localize('sqlops.extension.contributes.dashboard.tab.container', "The container that will be displayed in this tab."),
|
description: localize('sqlops.extension.contributes.dashboard.tab.container', "The container that will be displayed in this tab."),
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -57,10 +49,6 @@ const tabSchema: IJSONSchema = {
|
|||||||
alwaysShow: {
|
alwaysShow: {
|
||||||
description: localize('sqlops.extension.contributes.dashboard.tab.alwaysShow', "Whether or not this tab should always be shown or only when the user adds it."),
|
description: localize('sqlops.extension.contributes.dashboard.tab.alwaysShow', "Whether or not this tab should always be shown or only when the user adds it."),
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
},
|
|
||||||
isHomeTab: {
|
|
||||||
description: localize('sqlops.extension.contributes.dashboard.tab.isHomeTab', "Whether or not this tab should be used as the Home tab for a connection type."),
|
|
||||||
type: 'boolean'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -79,7 +67,7 @@ const tabContributionSchema: IJSONSchema = {
|
|||||||
ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabContrib[]>('dashboard.tabs', [], tabContributionSchema).setHandler(extensions => {
|
ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabContrib[]>('dashboard.tabs', [], tabContributionSchema).setHandler(extensions => {
|
||||||
|
|
||||||
function handleCommand(tab: IDashboardTabContrib, extension: IExtensionPointUser<any>) {
|
function handleCommand(tab: IDashboardTabContrib, extension: IExtensionPointUser<any>) {
|
||||||
let { description, container, provider, title, when, id, alwaysShow, isHomeTab } = tab;
|
let { description, container, title, when, id, alwaysShow } = tab;
|
||||||
|
|
||||||
// If always show is not specified, set it to true by default.
|
// If always show is not specified, set it to true by default.
|
||||||
if (!types.isBoolean(alwaysShow)) {
|
if (!types.isBoolean(alwaysShow)) {
|
||||||
@@ -100,13 +88,6 @@ ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabCo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!provider) {
|
|
||||||
// Use a default. Consider warning extension developers about this in the future if in development mode
|
|
||||||
provider = Constants.mssqlProviderName;
|
|
||||||
// Cannot be a home tab if it did not specify a provider
|
|
||||||
isHomeTab = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(container).length !== 1) {
|
if (Object.keys(container).length !== 1) {
|
||||||
extension.collector.error(localize('dashboardTab.contribution.moreThanOneDashboardContainersError', 'Exactly 1 dashboard container must be defined per space'));
|
extension.collector.error(localize('dashboardTab.contribution.moreThanOneDashboardContainersError', 'Exactly 1 dashboard container must be defined per space'));
|
||||||
return;
|
return;
|
||||||
@@ -129,7 +110,7 @@ ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
registerTab({ description, title, container, provider, when, id, alwaysShow, publisher, isHomeTab });
|
registerTab({ description, title, container, when, id, alwaysShow, publisher });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ import { OnDestroy } from '@angular/core';
|
|||||||
|
|
||||||
import { Event } from 'vs/base/common/event';
|
import { Event } from 'vs/base/common/event';
|
||||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|
||||||
|
|
||||||
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||||
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
|
|
||||||
|
|
||||||
export enum Conditional {
|
export enum Conditional {
|
||||||
'equals',
|
'equals',
|
||||||
@@ -52,8 +50,3 @@ export abstract class DashboardTab extends TabChild implements OnDestroy {
|
|||||||
this.dispose();
|
this.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IConfigModifierCollection {
|
|
||||||
connectionManagementService: SingleConnectionManagementService;
|
|
||||||
contextKeyService: IContextKeyService;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,27 +5,28 @@
|
|||||||
|
|
||||||
import 'vs/css!./dashboardNavSection';
|
import 'vs/css!./dashboardNavSection';
|
||||||
|
|
||||||
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, OnChanges, AfterContentInit } from '@angular/core';
|
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, EventEmitter, OnChanges, AfterContentInit } from '@angular/core';
|
||||||
|
|
||||||
import { CommonServiceInterface, SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { WidgetConfig, TabConfig, NavSectionConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
import { WidgetConfig, TabConfig, NavSectionConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||||
import { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
|
import { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
|
||||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
import { TabComponent, TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||||
import { DashboardTab, IConfigModifierCollection } from 'sql/parts/dashboard/common/interfaces';
|
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||||
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
|
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
|
||||||
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
|
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
|
||||||
import * as dashboardHelper from 'sql/parts/dashboard/common/dashboardHelper';
|
import * as dashboardHelper from 'sql/parts/dashboard/common/dashboardHelper';
|
||||||
|
|
||||||
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'dashboard-nav-section',
|
selector: 'dashboard-nav-section',
|
||||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => DashboardNavSection) }],
|
providers: [{ provide: TabChild, useExisting: forwardRef(() => DashboardNavSection) }],
|
||||||
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/containers/dashboardNavSection.component.html'))
|
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/containers/dashboardNavSection.component.html'))
|
||||||
})
|
})
|
||||||
export class DashboardNavSection extends DashboardTab implements OnDestroy, OnChanges, AfterContentInit, IConfigModifierCollection {
|
export class DashboardNavSection extends DashboardTab implements OnDestroy, OnChanges, AfterContentInit {
|
||||||
@Input() private tab: TabConfig;
|
@Input() private tab: TabConfig;
|
||||||
protected tabs: Array<TabConfig> = [];
|
protected tabs: Array<TabConfig> = [];
|
||||||
private _onResize = new Emitter<void>();
|
private _onResize = new Emitter<void>();
|
||||||
@@ -37,7 +38,7 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh
|
|||||||
};
|
};
|
||||||
|
|
||||||
// a set of config modifiers
|
// a set of config modifiers
|
||||||
private readonly _configModifiers: Array<(item: Array<WidgetConfig>, collection: IConfigModifierCollection, context: string) => Array<WidgetConfig>> = [
|
private readonly _configModifiers: Array<(item: Array<WidgetConfig>, dashboardServer: DashboardServiceInterface, context: string) => Array<WidgetConfig>> = [
|
||||||
dashboardHelper.removeEmpty,
|
dashboardHelper.removeEmpty,
|
||||||
dashboardHelper.initExtensionConfigs,
|
dashboardHelper.initExtensionConfigs,
|
||||||
dashboardHelper.addProvider,
|
dashboardHelper.addProvider,
|
||||||
@@ -102,7 +103,7 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh
|
|||||||
if (key === WIDGETS_CONTAINER || key === GRID_CONTAINER) {
|
if (key === WIDGETS_CONTAINER || key === GRID_CONTAINER) {
|
||||||
let configs = <WidgetConfig[]>Object.values(containerResult.container)[0];
|
let configs = <WidgetConfig[]>Object.values(containerResult.container)[0];
|
||||||
this._configModifiers.forEach(cb => {
|
this._configModifiers.forEach(cb => {
|
||||||
configs = cb.apply(this, [configs, this, this.tab.context]);
|
configs = cb.apply(this, [configs, this.dashboardService, this.tab.context]);
|
||||||
});
|
});
|
||||||
this._gridModifiers.forEach(cb => {
|
this._gridModifiers.forEach(cb => {
|
||||||
configs = cb.apply(this, [configs]);
|
configs = cb.apply(this, [configs]);
|
||||||
@@ -168,12 +169,4 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get connectionManagementService(): SingleConnectionManagementService {
|
|
||||||
return this.dashboardService.connectionManagementService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get contextKeyService(): IContextKeyService {
|
|
||||||
return this.dashboardService.scopedContextKeyService;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Inject, NgModule, forwardRef, ApplicationRef, ComponentFactoryResolver } from '@angular/core';
|
import { Inject, NgModule, forwardRef, ApplicationRef, ComponentFactoryResolver, NgModuleRef, NgModuleFactory } from '@angular/core';
|
||||||
import { CommonModule, APP_BASE_HREF } from '@angular/common';
|
import { CommonModule, APP_BASE_HREF } from '@angular/common';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { RouterModule, Routes, UrlSerializer, Router, NavigationEnd } from '@angular/router';
|
import { RouterModule, Routes, UrlSerializer, Router, NavigationEnd } from '@angular/router';
|
||||||
@@ -14,7 +14,7 @@ import { ChartsModule } from 'ng2-charts/ng2-charts';
|
|||||||
import CustomUrlSerializer from 'sql/common/urlSerializer';
|
import CustomUrlSerializer from 'sql/common/urlSerializer';
|
||||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||||
import { Extensions as ComponentExtensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
import { Extensions as ComponentExtensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
|
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ import { CommonServiceInterface } from 'sql/services/common/commonServiceInterfa
|
|||||||
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
||||||
|
|
||||||
/* Base Components */
|
/* Base Components */
|
||||||
import { DashboardComponent } from 'sql/parts/dashboard/dashboard.component';
|
import { DashboardComponent, DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component';
|
||||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
|
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
|
||||||
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
|
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
|
||||||
import { DashboardGridContainer } from 'sql/parts/dashboard/containers/dashboardGridContainer.component';
|
import { DashboardGridContainer } from 'sql/parts/dashboard/containers/dashboardGridContainer.component';
|
||||||
@@ -51,10 +51,7 @@ import { ControlHostContent } from 'sql/parts/dashboard/contents/controlHostCont
|
|||||||
import { DashboardControlHostContainer } from 'sql/parts/dashboard/containers/dashboardControlHostContainer.component';
|
import { DashboardControlHostContainer } from 'sql/parts/dashboard/containers/dashboardControlHostContainer.component';
|
||||||
import { JobsViewComponent } from 'sql/parts/jobManagement/views/jobsView.component';
|
import { JobsViewComponent } from 'sql/parts/jobManagement/views/jobsView.component';
|
||||||
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
||||||
import { AlertsViewComponent } from 'sql/parts/jobManagement/views/alertsView.component';
|
|
||||||
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
||||||
import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsView.component';
|
|
||||||
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
|
|
||||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
||||||
@@ -62,8 +59,9 @@ import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
|||||||
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
||||||
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
|
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
|
||||||
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
|
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
|
||||||
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, AlertsViewComponent, ProxiesViewComponent, OperatorsViewComponent,
|
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper, Checkbox,
|
||||||
DashboardModelViewContainer, ModelComponentWrapper, Checkbox, SelectBox, InputBox,];
|
SelectBox,
|
||||||
|
InputBox,];
|
||||||
|
|
||||||
/* Panel */
|
/* Panel */
|
||||||
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
||||||
@@ -82,8 +80,7 @@ import { ExplorerWidget } from 'sql/parts/dashboard/widgets/explorer/explorerWid
|
|||||||
import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.component';
|
import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.component';
|
||||||
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
|
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
|
||||||
import { WebviewWidget } from 'sql/parts/dashboard/widgets/webview/webviewWidget.component';
|
import { WebviewWidget } from 'sql/parts/dashboard/widgets/webview/webviewWidget.component';
|
||||||
import { JobStepsViewComponent } from 'sql/parts/jobManagement/views/jobStepsView.component';
|
import { JobStepsViewComponent } from '../jobManagement/views/jobStepsView.component';
|
||||||
import { IInstantiationService, _util } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
|
|
||||||
let widgetComponents = [
|
let widgetComponents = [
|
||||||
PropertiesWidgetComponent,
|
PropertiesWidgetComponent,
|
||||||
@@ -112,7 +109,7 @@ const appRoutes: Routes = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Connection Dashboard main angular module
|
// Connection Dashboard main angular module
|
||||||
export const DashboardModule = (params, selector: string, instantiationService: IInstantiationService): any => {
|
export const DashboardModule = (params, selector: string): any => {
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
...baseComponents,
|
...baseComponents,
|
||||||
@@ -143,31 +140,31 @@ export const DashboardModule = (params, selector: string, instantiationService:
|
|||||||
{ provide: IBreadcrumbService, useClass: BreadcrumbService },
|
{ provide: IBreadcrumbService, useClass: BreadcrumbService },
|
||||||
{ provide: CommonServiceInterface, useClass: DashboardServiceInterface },
|
{ provide: CommonServiceInterface, useClass: DashboardServiceInterface },
|
||||||
{ provide: UrlSerializer, useClass: CustomUrlSerializer },
|
{ provide: UrlSerializer, useClass: CustomUrlSerializer },
|
||||||
{ provide: IBootstrapParams, useValue: params },
|
{ provide: IBootstrapParams, useValue: params }
|
||||||
{ provide: ISelector, useValue: selector },
|
|
||||||
...providerIterator(instantiationService)
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
class ModuleClass {
|
class ModuleClass {
|
||||||
private navigations = 0;
|
private _bootstrap: DashboardServiceInterface;
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
||||||
|
@Inject(forwardRef(() => CommonServiceInterface)) bootstrap: CommonServiceInterface,
|
||||||
@Inject(forwardRef(() => Router)) private _router: Router,
|
@Inject(forwardRef(() => Router)) private _router: Router,
|
||||||
@Inject(ITelemetryService) private telemetryService: ITelemetryService,
|
@Inject(ITelemetryService) private telemetryService: ITelemetryService
|
||||||
@Inject(ISelector) private selector: string
|
|
||||||
) {
|
) {
|
||||||
|
this._bootstrap = bootstrap as DashboardServiceInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngDoBootstrap(appRef: ApplicationRef) {
|
ngDoBootstrap(appRef: ApplicationRef) {
|
||||||
const factory = this._resolver.resolveComponentFactory(DashboardComponent);
|
const factory = this._resolver.resolveComponentFactory(DashboardComponent);
|
||||||
(<any>factory).factory.selector = this.selector;
|
this._bootstrap.selector = selector;
|
||||||
|
(<any>factory).factory.selector = selector;
|
||||||
appRef.bootstrap(factory);
|
appRef.bootstrap(factory);
|
||||||
|
|
||||||
this._router.events.subscribe(e => {
|
this._router.events.subscribe(e => {
|
||||||
if (e instanceof NavigationEnd) {
|
if (e instanceof NavigationEnd) {
|
||||||
this.navigations++;
|
this._bootstrap.handlePageNavigation();
|
||||||
TelemetryUtils.addTelemetry(this.telemetryService, TelemetryKeys.DashboardNavigated, {
|
TelemetryUtils.addTelemetry(this.telemetryService, TelemetryKeys.DashboardNavigated, {
|
||||||
numberOfNavigations: this.navigations,
|
numberOfNavigations: this._bootstrap.getNumberOfPageNavigations(),
|
||||||
routeUrl: e.url
|
routeUrl: e.url
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,12 +96,16 @@ let defaultVal = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
widget: {
|
widget: {
|
||||||
'backup-history-server-insight': null
|
'backup-history-server-insight': {
|
||||||
|
cacheId: '0c7cba8b-c87a-4bcc-ae54-2f40a5503a90'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
widget: {
|
widget: {
|
||||||
'all-database-size-server-insight': null
|
'all-database-size-server-insight': {
|
||||||
|
cacheId: '1d7cba8b-c87a-4bcc-ae54-2f40a5503a90'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -117,7 +121,8 @@ export const serverDashboardTabsSchema: IJSONSchema = {
|
|||||||
type: ['array'],
|
type: ['array'],
|
||||||
description: nls.localize('dashboardServerTabs', 'Customizes the Server dashboard tabs'),
|
description: nls.localize('dashboardServerTabs', 'Customizes the Server dashboard tabs'),
|
||||||
items: generateDashboardTabSchema('server'),
|
items: generateDashboardTabSchema('server'),
|
||||||
default: []
|
default: [
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SERVER_DASHBOARD_SETTING = 'dashboard.server.widgets';
|
export const SERVER_DASHBOARD_SETTING = 'dashboard.server.widgets';
|
||||||
|
|||||||
@@ -4,29 +4,50 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* Node Modules */
|
/* Node Modules */
|
||||||
import { Injectable, Inject, forwardRef } from '@angular/core';
|
import { Injectable, Inject, forwardRef, OnDestroy } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
/* SQL imports */
|
/* SQL imports */
|
||||||
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
|
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
|
||||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
import { IMetadataService } from 'sql/services/metadata/metadataService';
|
import { IMetadataService } from 'sql/services/metadata/metadataService';
|
||||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||||
|
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
||||||
import { IAdminService } from 'sql/parts/admin/common/adminService';
|
import { IAdminService } from 'sql/parts/admin/common/adminService';
|
||||||
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
|
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
|
||||||
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
|
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
|
||||||
|
import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces';
|
||||||
|
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
||||||
|
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||||
import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
|
import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
|
||||||
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||||
import { TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
import { TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
|
||||||
|
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||||
|
import { ConnectionContextkey } from 'sql/parts/connection/common/connectionContextKey';
|
||||||
|
import { SingleConnectionMetadataService, SingleConnectionManagementService, SingleAdminService, SingleQueryManagementService, CommonServiceInterface }
|
||||||
|
from 'sql/services/common/commonServiceInterface.service';
|
||||||
|
|
||||||
|
import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'sqlops';
|
||||||
|
|
||||||
/* VS imports */
|
/* VS imports */
|
||||||
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
|
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||||
|
import { ConfigurationEditingService, IConfigurationValue } from 'vs/workbench/services/configuration/node/configurationEditingService';
|
||||||
|
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||||
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import Severity from 'vs/base/common/severity';
|
import Severity from 'vs/base/common/severity';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||||
import { deepClone } from 'vs/base/common/objects';
|
import { deepClone } from 'vs/base/common/objects';
|
||||||
import { RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||||
|
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||||
|
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
|
|
||||||
const DASHBOARD_SETTINGS = 'dashboard';
|
const DASHBOARD_SETTINGS = 'dashboard';
|
||||||
@@ -64,22 +85,45 @@ export class DashboardServiceInterface extends CommonServiceInterface {
|
|||||||
private _numberOfPageNavigations = 0;
|
private _numberOfPageNavigations = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(forwardRef(() => Router)) private _router: Router,
|
||||||
|
@Inject(INotificationService) private _notificationService: INotificationService,
|
||||||
@Inject(IMetadataService) metadataService: IMetadataService,
|
@Inject(IMetadataService) metadataService: IMetadataService,
|
||||||
@Inject(IConnectionManagementService) connectionManagementService: IConnectionManagementService,
|
@Inject(IConnectionManagementService) connectionManagementService: IConnectionManagementService,
|
||||||
@Inject(IAdminService) adminService: IAdminService,
|
@Inject(IAdminService) adminService: IAdminService,
|
||||||
@Inject(IQueryManagementService) queryManagementService: IQueryManagementService,
|
@Inject(IQueryManagementService) queryManagementService: IQueryManagementService,
|
||||||
@Inject(IBootstrapParams) params: IDashboardComponentParams,
|
|
||||||
@Inject(forwardRef(() => Router)) private _router: Router,
|
|
||||||
@Inject(INotificationService) private _notificationService: INotificationService,
|
|
||||||
@Inject(IAngularEventingService) private angularEventingService: IAngularEventingService,
|
@Inject(IAngularEventingService) private angularEventingService: IAngularEventingService,
|
||||||
@Inject(IConfigurationService) private _configService: IConfigurationService
|
@Inject(IConfigurationService) private _configService: IConfigurationService,
|
||||||
|
@Inject(IBootstrapParams) _params: IDashboardComponentParams
|
||||||
) {
|
) {
|
||||||
super(params, metadataService, connectionManagementService, adminService, queryManagementService);
|
super(_params, metadataService, connectionManagementService, adminService, queryManagementService);
|
||||||
// during testing there may not be params
|
}
|
||||||
if (this._params) {
|
|
||||||
this.dashboardContextKey = this._dashboardContextKey.bindTo(this.scopedContextKeyService);
|
private get params(): IDashboardComponentParams {
|
||||||
this._register(toDisposableSubscription(this.angularEventingService.onAngularEvent(this._uri, (event) => this.handleDashboardEvent(event))));
|
return this._params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selector for this dashboard instance, should only be set once
|
||||||
|
*/
|
||||||
|
public set selector(selector: string) {
|
||||||
|
this._uniqueSelector = selector;
|
||||||
|
this._getbootstrapParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _getbootstrapParams(): void {
|
||||||
|
this.scopedContextKeyService = this.params.scopedContextService;
|
||||||
|
this._connectionContextKey = this.params.connectionContextKey;
|
||||||
|
this.dashboardContextKey = this._dashboardContextKey.bindTo(this.scopedContextKeyService);
|
||||||
|
this.uri = this.params.ownerUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the uri for this dashboard instance, should only be set once
|
||||||
|
* Inits all the services that depend on knowing a uri
|
||||||
|
*/
|
||||||
|
protected set uri(uri: string) {
|
||||||
|
super.setUri(uri);
|
||||||
|
this._register(toDisposableSubscription(this.angularEventingService.onAngularEvent(this._uri, (event) => this.handleDashboardEvent(event))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import { IContextViewService, IContextMenuService } from 'vs/platform/contextvie
|
|||||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||||
import * as types from 'vs/base/common/types';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'explorer-widget',
|
selector: 'explorer-widget',
|
||||||
@@ -121,8 +120,6 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget,
|
|||||||
let currentProfile = this._bootstrap.connectionManagementService.connectionInfo.connectionProfile;
|
let currentProfile = this._bootstrap.connectionManagementService.connectionInfo.connectionProfile;
|
||||||
this._register(toDisposableSubscription(this._bootstrap.metadataService.databaseNames.subscribe(
|
this._register(toDisposableSubscription(this._bootstrap.metadataService.databaseNames.subscribe(
|
||||||
data => {
|
data => {
|
||||||
// Handle the case where there is no metadata service
|
|
||||||
data = data || [];
|
|
||||||
let profileData = data.map(d => {
|
let profileData = data.map(d => {
|
||||||
let profile = new ConnectionProfile(this.capabilitiesService, currentProfile);
|
let profile = new ConnectionProfile(this.capabilitiesService, currentProfile);
|
||||||
profile.databaseName = d;
|
profile.databaseName = d;
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
|
|||||||
}
|
}
|
||||||
|
|
||||||
public runTask(task: ICommandAction) {
|
public runTask(task: ICommandAction) {
|
||||||
|
let serverInfo = this._bootstrap.connectionManagementService.connectionInfo.serverInfo;
|
||||||
this.commandService.executeCommand(task.id, this._profile);
|
this.commandService.executeCommand(task.id, this._profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,23 +4,20 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ApplicationRef, ComponentFactoryResolver, NgModule,
|
ApplicationRef, ComponentFactoryResolver, ModuleWithProviders, NgModule,
|
||||||
Inject, forwardRef, Type
|
Inject, forwardRef, Type
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
import { BackupComponent, BACKUP_SELECTOR } from 'sql/parts/disasterRecovery/backup/backup.component';
|
||||||
import { BackupComponent } from 'sql/parts/disasterRecovery/backup/backup.component';
|
|
||||||
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
|
|
||||||
// work around
|
// work around
|
||||||
const BrowserAnimationsModule = (<any>require.__$__nodeRequire('@angular/platform-browser/animations')).BrowserAnimationsModule;
|
const BrowserAnimationsModule = (<any>require.__$__nodeRequire('@angular/platform-browser/animations')).BrowserAnimationsModule;
|
||||||
|
|
||||||
// Backup wizard main angular module
|
// Backup wizard main angular module
|
||||||
export const BackupModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
|
export const BackupModule = (params: IBootstrapParams, selector: string): Type<any> => {
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
BackupComponent
|
BackupComponent
|
||||||
@@ -34,22 +31,19 @@ export const BackupModule = (params: IBootstrapParams, selector: string, instant
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||||
{ provide: IBootstrapParams, useValue: params },
|
{ provide: IBootstrapParams, useValue: params }
|
||||||
{ provide: ISelector, useValue: selector },
|
|
||||||
...providerIterator(instantiationService)
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
class ModuleClass {
|
class ModuleClass {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver
|
||||||
@Inject(ISelector) private selector: string
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngDoBootstrap(appRef: ApplicationRef) {
|
ngDoBootstrap(appRef: ApplicationRef) {
|
||||||
const factory = this._resolver.resolveComponentFactory(BackupComponent);
|
const factory = this._resolver.resolveComponentFactory(BackupComponent);
|
||||||
(<any>factory).factory.selector = this.selector;
|
(<any>factory).factory.selector = selector;
|
||||||
appRef.bootstrap(factory);
|
appRef.bootstrap(factory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,8 +235,7 @@ export class RestoreDialog extends Modal {
|
|||||||
this._databaseDropdown = new Dropdown(inputCellContainer.getHTMLElement(), this._contextViewService, this._themeService,
|
this._databaseDropdown = new Dropdown(inputCellContainer.getHTMLElement(), this._contextViewService, this._themeService,
|
||||||
{
|
{
|
||||||
strictSelection: false,
|
strictSelection: false,
|
||||||
ariaLabel: LocalizedStrings.TARGETDATABASE,
|
ariaLabel: LocalizedStrings.TARGETDATABASE
|
||||||
actionLabel: localize('toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this._databaseDropdown.onValueChange(s => {
|
this._databaseDropdown.onValueChange(s => {
|
||||||
|
|||||||
@@ -7,14 +7,12 @@
|
|||||||
import { ApplicationRef, ComponentFactoryResolver, NgModule, Inject, forwardRef, Type } from '@angular/core';
|
import { ApplicationRef, ComponentFactoryResolver, NgModule, Inject, forwardRef, Type } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { EditDataComponent, EDITDATA_SELECTOR } from 'sql/parts/grid/views/editData/editData.component';
|
||||||
import { SlickGrid } from 'angular2-slickgrid';
|
import { SlickGrid } from 'angular2-slickgrid';
|
||||||
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
|
|
||||||
import { EditDataComponent } from 'sql/parts/grid/views/editData/editData.component';
|
export const EditDataModule = (params: IBootstrapParams, selector: string): Type<any> => {
|
||||||
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';
|
|
||||||
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
|
|
||||||
export const EditDataModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type<any> => {
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
|
||||||
@@ -32,22 +30,19 @@ export const EditDataModule = (params: IBootstrapParams, selector: string, insta
|
|||||||
EditDataComponent
|
EditDataComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: IBootstrapParams, useValue: params },
|
{ provide: IBootstrapParams, useValue: params }
|
||||||
{ provide: ISelector, useValue: selector },
|
|
||||||
...providerIterator(instantiationService)
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
class ModuleClass {
|
class ModuleClass {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver
|
||||||
@Inject(ISelector) private selector: string
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngDoBootstrap(appRef: ApplicationRef) {
|
ngDoBootstrap(appRef: ApplicationRef) {
|
||||||
const factory = this._resolver.resolveComponentFactory(EditDataComponent);
|
const factory = this._resolver.resolveComponentFactory(EditDataComponent);
|
||||||
(<any>factory).factory.selector = this.selector;
|
(<any>factory).factory.selector = selector;
|
||||||
appRef.bootstrap(factory);
|
appRef.bootstrap(factory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,4 @@
|
|||||||
|
|
||||||
.editdata-component * {
|
.editdata-component * {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
|
||||||
|
|
||||||
#workbench\.editor\.editDataEditor .monaco-toolbar .monaco-select-box {
|
|
||||||
margin-top: 4px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
}
|
||||||
@@ -4,42 +4,17 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
-->
|
-->
|
||||||
<div id="agentViewDiv" class="fullsize">
|
|
||||||
<panel class="dashboard-panel" [options]="panelOpt">
|
<panel class="dashboard-panel" [options]="panelOpt">
|
||||||
<tab [title]="jobsComponentTitle" class="fullsize" [identifier]="jobsTabIdentifier"
|
<tab [title]="jobsComponentTitle" class="fullsize" [identifier]="jobsTabIdentifier"
|
||||||
[iconClass]="jobsIconClass">
|
[iconClass]="jobsIconClass">
|
||||||
<ng-template>
|
<ng-template>
|
||||||
<div id="jobsDiv" class="fullsize" *ngIf="showHistory === false">
|
<div id="jobsDiv" class="fullsize" *ngIf="showHistory === false">
|
||||||
<jobsview-component></jobsview-component>
|
<jobsview-component></jobsview-component>
|
||||||
</div>
|
</div>
|
||||||
<div id="historyDiv" class="fullsize" *ngIf="showHistory === true">
|
<div id="historyDiv" class="fullsize" *ngIf="showHistory === true">
|
||||||
<jobhistory-component></jobhistory-component>
|
<jobhistory-component></jobhistory-component>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</tab>
|
</tab>
|
||||||
<tab [title]="alertsComponentTitle" class="fullsize" [identifier]="alertsTabIdentifier"
|
</panel>
|
||||||
[iconClass]="alertsIconClass">
|
|
||||||
<ng-template>
|
|
||||||
<div id="alertsDiv" class="fullsize">
|
|
||||||
<jobalertsview-component></jobalertsview-component>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</tab>
|
|
||||||
<tab [title]="operatorsComponentTitle" class="fullsize" [identifier]="operatorsTabIdentifier"
|
|
||||||
[iconClass]="operatorsIconClass">
|
|
||||||
<ng-template>
|
|
||||||
<div id="operatorsDiv" class="fullsize">
|
|
||||||
<joboperatorsview-component></joboperatorsview-component>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</tab>
|
|
||||||
<tab [title]="proxiesComponentTitle" class="fullsize" [identifier]="proxiesTabIdentifier"
|
|
||||||
[iconClass]="proxiesIconClass">
|
|
||||||
<ng-template>
|
|
||||||
<div id="proxiesDiv" class="fullsize">
|
|
||||||
<jobproxiesview-component></jobproxiesview-component>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</tab>
|
|
||||||
</panel>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -33,10 +33,6 @@ export class AgentViewComponent {
|
|||||||
|
|
||||||
// tslint:disable:no-unused-variable
|
// tslint:disable:no-unused-variable
|
||||||
private readonly jobsComponentTitle: string = nls.localize('jobview.Jobs', "Jobs");
|
private readonly jobsComponentTitle: string = nls.localize('jobview.Jobs', "Jobs");
|
||||||
private readonly alertsComponentTitle: string = nls.localize('jobview.Alerts', "Alerts");
|
|
||||||
private readonly proxiesComponentTitle: string = nls.localize('jobview.Proxies', "Proxies");
|
|
||||||
private readonly operatorsComponentTitle: string = nls.localize('jobview.Operators', "Operators");
|
|
||||||
|
|
||||||
private _showHistory: boolean = false;
|
private _showHistory: boolean = false;
|
||||||
private _jobId: string = null;
|
private _jobId: string = null;
|
||||||
private _agentJobInfo: AgentJobInfo = null;
|
private _agentJobInfo: AgentJobInfo = null;
|
||||||
@@ -44,9 +40,6 @@ export class AgentViewComponent {
|
|||||||
private _expanded: Map<string, string>;
|
private _expanded: Map<string, string>;
|
||||||
|
|
||||||
public jobsIconClass: string = 'jobsview-icon';
|
public jobsIconClass: string = 'jobsview-icon';
|
||||||
public alertsIconClass: string = 'alertsview-icon';
|
|
||||||
public proxiesIconClass: string = 'proxiesview-icon';
|
|
||||||
public operatorsIconClass: string = 'operatorsview-icon';
|
|
||||||
|
|
||||||
// tslint:disable-next-line:no-unused-variable
|
// tslint:disable-next-line:no-unused-variable
|
||||||
private readonly panelOpt: IPanelOptions = {
|
private readonly panelOpt: IPanelOptions = {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { Table } from 'sql/base/browser/ui/table/table';
|
||||||
import { JobCacheObject } from './jobManagementService';
|
import { JobCacheObject } from './jobManagementService';
|
||||||
|
|
||||||
export const SERVICE_ID = 'jobManagementService';
|
export const SERVICE_ID = 'jobManagementService';
|
||||||
@@ -20,12 +21,6 @@ export interface IJobManagementService {
|
|||||||
|
|
||||||
getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult>;
|
getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult>;
|
||||||
|
|
||||||
getAlerts(connectionUri: string): Thenable<sqlops.AgentAlertsResult>;
|
|
||||||
|
|
||||||
getOperators(connectionUri: string): Thenable<sqlops.AgentOperatorsResult>;
|
|
||||||
|
|
||||||
getProxies(connectionUri: string): Thenable<sqlops.AgentProxiesResult>;
|
|
||||||
|
|
||||||
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult>;
|
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult>;
|
||||||
|
|
||||||
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus>;
|
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus>;
|
||||||
|
|||||||
@@ -7,8 +7,11 @@
|
|||||||
|
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
|
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
|
||||||
|
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
||||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||||
|
|
||||||
|
|
||||||
@@ -19,7 +22,8 @@ export class JobManagementService implements IJobManagementService {
|
|||||||
private _jobCacheObject : {[server: string]: JobCacheObject; } = {};
|
private _jobCacheObject : {[server: string]: JobCacheObject; } = {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IConnectionManagementService private _connectionService: IConnectionManagementService
|
@IConnectionManagementService private _connectionService: IConnectionManagementService,
|
||||||
|
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,25 +33,6 @@ export class JobManagementService implements IJobManagementService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAlerts(connectionUri: string): Thenable<sqlops.AgentAlertsResult> {
|
|
||||||
return this._runAction(connectionUri, (runner) => {
|
|
||||||
return runner.getAlerts(connectionUri);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getOperators(connectionUri: string): Thenable<sqlops.AgentOperatorsResult> {
|
|
||||||
return this._runAction(connectionUri, (runner) => {
|
|
||||||
return runner.getOperators(connectionUri);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getProxies(connectionUri: string): Thenable<sqlops.AgentProxiesResult> {
|
|
||||||
return this._runAction(connectionUri, (runner) => {
|
|
||||||
return runner.getProxies(connectionUri);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
|
public getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
|
||||||
return this._runAction(connectionUri, (runner) => {
|
return this._runAction(connectionUri, (runner) => {
|
||||||
return runner.getJobHistory(connectionUri, jobID);
|
return runner.getJobHistory(connectionUri, jobID);
|
||||||
@@ -94,8 +79,7 @@ export class JobManagementService implements IJobManagementService {
|
|||||||
export class JobCacheObject {
|
export class JobCacheObject {
|
||||||
_serviceBrand: any;
|
_serviceBrand: any;
|
||||||
private _jobs: sqlops.AgentJobInfo[] = [];
|
private _jobs: sqlops.AgentJobInfo[] = [];
|
||||||
private _jobHistories: { [jobID: string]: sqlops.AgentJobHistoryInfo[]; } = {};
|
private _jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = {};
|
||||||
private _runCharts: { [jobID: string]: string[]; } = {};
|
|
||||||
private _prevJobID: string;
|
private _prevJobID: string;
|
||||||
private _serverName: string;
|
private _serverName: string;
|
||||||
private _dataView: Slick.Data.DataView<any>;
|
private _dataView: Slick.Data.DataView<any>;
|
||||||
@@ -105,7 +89,7 @@ export class JobCacheObject {
|
|||||||
return this._jobs;
|
return this._jobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get jobHistories(): { [jobID: string]: sqlops.AgentJobHistoryInfo[] } {
|
public get jobHistories(): { [jobId: string]: sqlops.AgentJobHistoryInfo[] } {
|
||||||
return this._jobHistories;
|
return this._jobHistories;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,16 +109,12 @@ export class JobCacheObject {
|
|||||||
return this._dataView;
|
return this._dataView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getRunChart(jobID: string): string[] {
|
|
||||||
return this._runCharts[jobID];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setters */
|
/* Setters */
|
||||||
public set jobs(value: sqlops.AgentJobInfo[]) {
|
public set jobs(value: sqlops.AgentJobInfo[]) {
|
||||||
this._jobs = value;
|
this._jobs = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set jobHistories(value: { [jobID: string]: sqlops.AgentJobHistoryInfo[]; }) {
|
public set jobHistories(value: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; }) {
|
||||||
this._jobHistories = value;
|
this._jobHistories = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,10 +126,6 @@ export class JobCacheObject {
|
|||||||
this._jobHistories[jobID] = value;
|
this._jobHistories[jobID] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setRunChart(jobID: string, value: string[]) {
|
|
||||||
this._runCharts[jobID] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public set serverName(value: string) {
|
public set serverName(value: string) {
|
||||||
this._serverName = value;
|
this._serverName = value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#212121;}</style></defs><title>blocker</title><polygon class="cls-1" points="0.99 3.99 -0.01 3.99 -0.01 0.03 3.98 0.03 3.98 1.03 0.99 1.03 0.99 3.99"/><polygon class="cls-1" points="16.01 3.99 15.01 3.99 15.01 1.03 12.02 1.03 12.02 0.03 16.01 0.03 16.01 3.99"/><polygon class="cls-1" points="16.01 15.97 12.02 15.97 12.02 14.97 15.01 14.97 15.01 12.01 16.01 12.01 16.01 15.97"/><polygon class="cls-1" points="4 15.97 0.01 15.97 0.01 12.01 1.01 12.01 1.01 14.97 4 14.97 4 15.97"/><path class="cls-1" d="M8.41,3.18A4.82,4.82,0,1,0,13.23,8,4.83,4.83,0,0,0,8.41,3.18Zm0,.74A4.08,4.08,0,0,1,12.49,8a4,4,0,0,1-.85,2.47L5.69,5A4,4,0,0,1,8.41,3.93Zm0,8.15A4.08,4.08,0,0,1,4.34,8a4,4,0,0,1,.85-2.47L11.14,11A4,4,0,0,1,8.41,12.07Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 847 B |
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>blocker_inverse</title><polygon class="cls-1" points="0.99 3.99 -0.01 3.99 -0.01 0.03 3.98 0.03 3.98 1.03 0.99 1.03 0.99 3.99"/><polygon class="cls-1" points="16.01 3.99 15.01 3.99 15.01 1.03 12.02 1.03 12.02 0.03 16.01 0.03 16.01 3.99"/><polygon class="cls-1" points="16.01 15.97 12.02 15.97 12.02 14.97 15.01 14.97 15.01 12.01 16.01 12.01 16.01 15.97"/><polygon class="cls-1" points="4 15.97 0.01 15.97 0.01 12.01 1.01 12.01 1.01 14.97 4 14.97 4 15.97"/><path class="cls-1" d="M8.41,3.18A4.82,4.82,0,1,0,13.23,8,4.83,4.83,0,0,0,8.41,3.18Zm0,.74A4.08,4.08,0,0,1,12.49,8a4,4,0,0,1-.85,2.47L5.69,5A4,4,0,0,1,8.41,3.93Zm0,8.15A4.08,4.08,0,0,1,4.34,8a4,4,0,0,1,.85-2.47L11.14,11A4,4,0,0,1,8.41,12.07Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 852 B |
@@ -31,7 +31,7 @@ jobhistory-component {
|
|||||||
border-bottom: 3px solid #444444;
|
border-bottom: 3px solid #444444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jobview-grid {
|
#jobsDiv .jobview-grid {
|
||||||
height: 94.7%;
|
height: 94.7%;
|
||||||
width : 100%;
|
width : 100%;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -49,18 +49,6 @@ jobhistory-component {
|
|||||||
font-size: larger;
|
font-size: larger;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vs-dark #agentViewDiv .slick-header-column {
|
|
||||||
background: #333333 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#agentViewDiv .slick-header-column {
|
|
||||||
background-color: transparent !important;
|
|
||||||
background: white !important;
|
|
||||||
border: 0px !important;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: larger;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vs-dark #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
.vs-dark #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
||||||
background:#333333;
|
background:#333333;
|
||||||
}
|
}
|
||||||
@@ -109,7 +97,7 @@ jobhistory-component {
|
|||||||
|
|
||||||
#jobsDiv .jobview-grid .slick-cell.l1.r1 .jobview-jobnametext {
|
#jobsDiv .jobview-grid .slick-cell.l1.r1 .jobview-jobnametext {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
width: 250px;
|
width: 200px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -119,14 +107,18 @@ jobhistory-component {
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.error-row {
|
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.error-row {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: orangered;
|
color: orangered;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.error-row {
|
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.error-row {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.error-row {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,30 +182,6 @@ jobhistory-component {
|
|||||||
background-image: url('./job_inverse.svg');
|
background-image: url('./job_inverse.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.alertsview-icon {
|
|
||||||
background-image: url('./alert.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.vs-dark .alertsview-icon {
|
|
||||||
background-image: url('./alert_inverse.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.proxiesview-icon {
|
|
||||||
background-image: url('./proxy.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.vs-dark .proxiesview-icon {
|
|
||||||
background-image: url('./proxy_inverse.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.operatorsview-icon {
|
|
||||||
background-image: url('./operator.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.vs-dark .operatorsview-icon {
|
|
||||||
background-image: url('./operator_inverse.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.even > .slick-cell,
|
agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.even > .slick-cell,
|
||||||
agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.odd > .slick-cell {
|
agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.odd > .slick-cell {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -251,8 +219,8 @@ agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.od
|
|||||||
background: #444444 !important;
|
background: #444444 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.jobprevruns div.bar0, table.jobprevruns div.bar1, table.jobprevruns div.bar2,
|
table.jobprevruns div.bar1, table.jobprevruns div.bar2, table.jobprevruns div.bar3,
|
||||||
table.jobprevruns div.bar3, table.jobprevruns div.bar4, table.jobprevruns div.bar5 {
|
table.jobprevruns div.bar4, table.jobprevruns div.bar5 {
|
||||||
padding-top: 3px;
|
padding-top: 3px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
@@ -264,54 +232,10 @@ table.jobprevruns div.bar3, table.jobprevruns div.bar4, table.jobprevruns div.ba
|
|||||||
}
|
}
|
||||||
|
|
||||||
table.jobprevruns {
|
table.jobprevruns {
|
||||||
|
margin: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.jobprevruns > tbody {
|
table.jobprevruns > tbody {
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jobs-view-toolbar{
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 15px 10px 15px 30px;
|
|
||||||
font-size: 16px;
|
|
||||||
border-bottom: 3px solid #f4f4f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jobs-view-toolbar .vs-dark {
|
|
||||||
border-bottom: 3px solid #444444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jobs-view-toolbar > div{
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
align-items: flex-start;
|
|
||||||
margin-right: 25px;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jobs-view-toolbar span{
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#alertsDiv .jobalertsview-grid {
|
|
||||||
height: 94.7%;
|
|
||||||
width : 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#operatorsDiv .joboperatorsview-grid {
|
|
||||||
height: 94.7%;
|
|
||||||
width : 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#proxiesDiv .jobproxiesview-grid {
|
|
||||||
height: 94.7%;
|
|
||||||
width : 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#231f20;}.cls-2{fill:#212121;}</style></defs><title>new_16x16</title><polygon class="cls-1" points="13.59 2.21 13.58 2.22 13.58 2.2 13.59 2.21"/><path class="cls-2" d="M16,7.5v1H8.5V16h-1V8.5H0v-1H7.5V0h1V7.5Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 336 B |
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>new_inverse_16x16</title><polygon class="cls-1" points="13.59 2.21 13.58 2.22 13.58 2.2 13.59 2.21"/><path class="cls-1" d="M16,7.5v1H8.5V16h-1V8.5H0v-1H7.5V0h1V7.5Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 320 B |
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>security</title><path d="M6,8a4.88,4.88,0,0,0-1.33.18,5.11,5.11,0,0,0-1.2.5,5,5,0,0,0-1.79,1.79,5.11,5.11,0,0,0-.5,1.2A4.88,4.88,0,0,0,1,13H0a5.9,5.9,0,0,1,.28-1.79,6.12,6.12,0,0,1,2-2.94,5.33,5.33,0,0,1,1.58-.88,4.18,4.18,0,0,1-.79-.65,4,4,0,0,1-.59-.8A4.05,4.05,0,0,1,2.13,5a4,4,0,0,1,.18-2.57A4,4,0,0,1,4.44.31a4,4,0,0,1,3.12,0A4,4,0,0,1,9.69,2.44,4,4,0,0,1,9.87,5a4.05,4.05,0,0,1-.37.93,4,4,0,0,1-.59.8,4.18,4.18,0,0,1-.79.65,6.14,6.14,0,0,1,1,.5,5.73,5.73,0,0,1,.91.69l-.68.74a5,5,0,0,0-1.57-1A4.93,4.93,0,0,0,6,8ZM3,4a2.92,2.92,0,0,0,.23,1.17,3,3,0,0,0,1.6,1.6,3,3,0,0,0,2.33,0,3,3,0,0,0,1.6-1.6,3,3,0,0,0,0-2.33,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.92,2.92,0,0,0,3,4Zm12,8a2.45,2.45,0,0,1,0,1l1,.4-.38.93-1-.41a2.59,2.59,0,0,1-.67.67l.41,1-.93.38L13,15a2.45,2.45,0,0,1-1,0l-.4,1-.93-.38.41-1a2.59,2.59,0,0,1-.67-.67l-1,.41-.38-.93,1-.4a2.45,2.45,0,0,1,0-1l-1-.4.38-.93,1,.41a2.59,2.59,0,0,1,.67-.67l-.41-1,.93-.38.4,1a2.45,2.45,0,0,1,1,0l.4-1,.93.38-.41,1a2.59,2.59,0,0,1,.67.67l1-.41.38.93ZM12.5,14a1.47,1.47,0,0,0,.59-.12,1.49,1.49,0,0,0,.8-.8,1.52,1.52,0,0,0,0-1.17,1.49,1.49,0,0,0-.8-.8,1.52,1.52,0,0,0-1.17,0,1.49,1.49,0,0,0-.8.8,1.52,1.52,0,0,0,0,1.17,1.49,1.49,0,0,0,.8.8A1.47,1.47,0,0,0,12.5,14Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>security_inverse</title><path class="cls-1" d="M6,8a4.88,4.88,0,0,0-1.33.18,5.11,5.11,0,0,0-1.2.5,5,5,0,0,0-1.79,1.79,5.11,5.11,0,0,0-.5,1.2A4.88,4.88,0,0,0,1,13H0a5.9,5.9,0,0,1,.28-1.79,6.12,6.12,0,0,1,2-2.94,5.33,5.33,0,0,1,1.58-.88,4.18,4.18,0,0,1-.79-.65,4,4,0,0,1-.59-.8A4.05,4.05,0,0,1,2.13,5a4,4,0,0,1,.18-2.57A4,4,0,0,1,4.44.31a4,4,0,0,1,3.12,0A4,4,0,0,1,9.69,2.44,4,4,0,0,1,9.87,5a4.05,4.05,0,0,1-.37.93,4,4,0,0,1-.59.8,4.18,4.18,0,0,1-.79.65,6.14,6.14,0,0,1,1,.5,5.73,5.73,0,0,1,.91.69l-.68.74a5,5,0,0,0-1.57-1A4.93,4.93,0,0,0,6,8ZM3,4a2.92,2.92,0,0,0,.23,1.17,3,3,0,0,0,1.6,1.6,3,3,0,0,0,2.33,0,3,3,0,0,0,1.6-1.6,3,3,0,0,0,0-2.33,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.92,2.92,0,0,0,3,4Zm12,8a2.45,2.45,0,0,1,0,1l1,.4-.38.93-1-.41a2.59,2.59,0,0,1-.67.67l.41,1-.93.38L13,15a2.45,2.45,0,0,1-1,0l-.4,1-.93-.38.41-1a2.59,2.59,0,0,1-.67-.67l-1,.41-.38-.93,1-.4a2.45,2.45,0,0,1,0-1l-1-.4.38-.93,1,.41a2.59,2.59,0,0,1,.67-.67l-.41-1,.93-.38.4,1a2.45,2.45,0,0,1,1,0l.4-1,.93.38-.41,1a2.59,2.59,0,0,1,.67.67l1-.41.38.93ZM12.5,14a1.47,1.47,0,0,0,.59-.12,1.49,1.49,0,0,0,.8-.8,1.52,1.52,0,0,0,0-1.17,1.49,1.49,0,0,0-.8-.8,1.52,1.52,0,0,0-1.17,0,1.49,1.49,0,0,0-.8.8,1.52,1.52,0,0,0,0,1.17,1.49,1.49,0,0,0,.8.8A1.47,1.47,0,0,0,12.5,14Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#101e23;}.cls-2{fill:#4bb8d1;}.cls-3{fill:#0c1011;}</style></defs><title>health</title><path class="cls-1" d="M12.58,1.51A6.36,6.36,0,0,0,8,3.9,6.32,6.32,0,0,0,3.41,1.51,3.81,3.81,0,0,0,0,5.35,5.7,5.7,0,0,0,.64,7.88h.72l0-.08A5.18,5.18,0,0,1,.64,5.39c.07-1.25.87-3.14,2.8-3.23h.12A5.81,5.81,0,0,1,7.73,4.63L8,5.06l.27-.43a5.72,5.72,0,0,1,4.28-2.47c1.93.09,2.73,2,2.8,3.23a5.15,5.15,0,0,1-.64,2.34l0,0a2.38,2.38,0,0,1-.34.68,19.45,19.45,0,0,1-6.57,6.06,11.11,11.11,0,0,1-1.25-.81c-.34-.25-.66-.52-1-.8h0a22.83,22.83,0,0,1-2.76-3H2a18.68,18.68,0,0,0,5.76,5.29h0l0,0h0c3.49-1.63,7-5.73,7.49-7.18V8A5.85,5.85,0,0,0,16,5.35,3.81,3.81,0,0,0,12.58,1.51Z"/><path class="cls-1" d="M1.41,8l-.1-.15h0Z"/><path class="cls-1" d="M7.79,15.22v0h0Z"/><path class="cls-1" d="M7.76,15.23h0v0Z"/><path class="cls-1" d="M14.72,7.73l0,0a.13.13,0,0,0,0,0Z"/><path class="cls-2" d="M12.62,8.7v.12a.48.48,0,0,1-.48.48H8.66l0,.07L7.38,12.65h0A.72.72,0,0,1,6,12.53V9.44H6V6.6L5,9.05H5a.56.56,0,0,1-.52.37H.92V8.36H4.13l0-.07L5.41,5h0a.72.72,0,0,1,1.42.12V8.22h0v2.84L7.77,8.6h0a.56.56,0,0,1,.52-.37h3.84A.48.48,0,0,1,12.62,8.7Z"/><path class="cls-3" d="M12.62,8.7v.12a.48.48,0,0,1-.48.48H8.66l0,.07L7.38,12.65h0A.72.72,0,0,1,6,12.53V9.44H6V6.6L5,9.05H5a.56.56,0,0,1-.52.37H.92V8.36H4.13l0-.07L5.41,5h0a.72.72,0,0,1,1.42.12V8.22h0v2.84L7.77,8.6h0a.56.56,0,0,1,.52-.37h3.84A.48.48,0,0,1,12.62,8.7Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#101e23;}.cls-3{fill:#4bb8d1;}</style></defs><title>health_inverse</title><path class="cls-1" d="M12.58,1.51A6.36,6.36,0,0,0,8,3.9,6.32,6.32,0,0,0,3.41,1.51,3.81,3.81,0,0,0,0,5.35,5.7,5.7,0,0,0,.64,7.88h.72l0-.08A5.18,5.18,0,0,1,.64,5.39c.07-1.25.87-3.14,2.8-3.23h.12A5.81,5.81,0,0,1,7.73,4.63L8,5.06l.27-.43a5.72,5.72,0,0,1,4.28-2.47c1.93.09,2.73,2,2.8,3.23a5.15,5.15,0,0,1-.64,2.34l0,0a2.38,2.38,0,0,1-.34.68,19.45,19.45,0,0,1-6.57,6.06,11.11,11.11,0,0,1-1.25-.81c-.34-.25-.66-.52-1-.8h0a22.83,22.83,0,0,1-2.76-3H2a18.68,18.68,0,0,0,5.76,5.29h0l0,0h0c3.49-1.63,7-5.73,7.49-7.18V8A5.85,5.85,0,0,0,16,5.35,3.81,3.81,0,0,0,12.58,1.51Z"/><path class="cls-2" d="M1.41,8l-.1-.15h0Z"/><path class="cls-2" d="M7.79,15.22v0h0Z"/><path class="cls-2" d="M7.76,15.23h0v0Z"/><path class="cls-2" d="M14.72,7.73l0,0a.13.13,0,0,0,0,0Z"/><path class="cls-3" d="M12.62,8.7v.12a.48.48,0,0,1-.48.48H8.66l0,.07L7.38,12.65h0A.72.72,0,0,1,6,12.53V9.44H6V6.6L5,9.05H5a.56.56,0,0,1-.52.37H.92V8.36H4.13l0-.07L5.41,5h0a.72.72,0,0,1,1.42.12V8.22h0v2.84L7.77,8.6h0a.56.56,0,0,1,.52-.37h3.84A.48.48,0,0,1,12.62,8.7Z"/><path class="cls-1" d="M12.62,8.7v.12a.48.48,0,0,1-.48.48H8.66l0,.07L7.38,12.65h0A.72.72,0,0,1,6,12.53V9.44H6V6.6L5,9.05H5a.56.56,0,0,1-.52.37H.92V8.36H4.13l0-.07L5.41,5h0a.72.72,0,0,1,1.42.12V8.22h0v2.84L7.77,8.6h0a.56.56,0,0,1,.52-.37h3.84A.48.48,0,0,1,12.62,8.7Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,17 +0,0 @@
|
|||||||
<!--
|
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
-->
|
|
||||||
<div class="job-heading-container">
|
|
||||||
<h1 class="job-heading" *ngIf="_isCloud === false">Alerts</h1>
|
|
||||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Alerts Available</h1>
|
|
||||||
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
|
|
||||||
</div>
|
|
||||||
<div class="jobs-view-toolbar">
|
|
||||||
<div (click)="refreshJobs()" tabindex="0"><div class="small icon refresh"></div><span>{{RefreshText}}</span></div>
|
|
||||||
<div (click)="openCreateJobDialog()" tabindex="0"><div class="small icon new"></div><span>{{NewAlertText}}</span></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div #jobalertsgrid class="jobview-grid"></div>
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import 'vs/css!sql/parts/grid/media/slickColorTheme';
|
|
||||||
import 'vs/css!sql/parts/grid/media/flexbox';
|
|
||||||
import 'vs/css!sql/parts/grid/media/styles';
|
|
||||||
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 nls from 'vs/nls';
|
|
||||||
import { Table } from 'sql/base/browser/ui/table/table';
|
|
||||||
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
|
||||||
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';
|
|
||||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
|
||||||
export const VIEW_SELECTOR: string = 'jobalertsview-component';
|
|
||||||
export const ROW_HEIGHT: number = 45;
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: VIEW_SELECTOR,
|
|
||||||
templateUrl: decodeURI(require.toUrl('./alertsView.component.html')),
|
|
||||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => AlertsViewComponent) }],
|
|
||||||
})
|
|
||||||
export class AlertsViewComponent implements AfterContentChecked {
|
|
||||||
|
|
||||||
private columns: Array<Slick.Column<any>> = [
|
|
||||||
{ name: nls.localize('jobAlertColumns.name', 'Name'), field: 'name', width: 200, id: 'name' },
|
|
||||||
{ name: nls.localize('jobAlertColumns.lastOccurrenceDate', 'Last Occurrence'), field: 'lastOccurrenceDate', width: 200, id: 'lastOccurrenceDate' },
|
|
||||||
{ name: nls.localize('jobAlertColumns.enabled', 'Enabled'), field: 'enabled', width: 200, id: 'enabled' },
|
|
||||||
{ name: nls.localize('jobAlertColumns.databaseName', 'Database Name'), field: 'databaseName', width: 200, id: 'databaseName' },
|
|
||||||
{ name: nls.localize('jobAlertColumns.categoryName', 'Category Name'), field: 'categoryName', width: 200, id: 'categoryName' },
|
|
||||||
];
|
|
||||||
|
|
||||||
private options: Slick.GridOptions<any> = {
|
|
||||||
syncColumnCellResize: true,
|
|
||||||
enableColumnReorder: false,
|
|
||||||
rowHeight: 45,
|
|
||||||
enableCellNavigation: true,
|
|
||||||
editable: false
|
|
||||||
};
|
|
||||||
|
|
||||||
private dataView: any;
|
|
||||||
|
|
||||||
@ViewChild('jobalertsgrid') _gridEl: ElementRef;
|
|
||||||
private isVisible: boolean = false;
|
|
||||||
private isInitialized: boolean = false;
|
|
||||||
private isRefreshing: boolean = false;
|
|
||||||
private _table: Table<any>;
|
|
||||||
public alerts: sqlops.AgentAlertInfo[];
|
|
||||||
private _serverName: string;
|
|
||||||
private _isCloud: boolean;
|
|
||||||
private _showProgressWheel: boolean;
|
|
||||||
|
|
||||||
private NewAlertText: string = nls.localize('jobAlertToolbar-NewJob', "New Alert");
|
|
||||||
private RefreshText: string = nls.localize('jobAlertToolbar-Refresh', "Refresh");
|
|
||||||
|
|
||||||
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(IThemeService) private _themeService: IThemeService,
|
|
||||||
@Inject(ICommandService) private _commandService: ICommandService
|
|
||||||
) {
|
|
||||||
this._isCloud = this._dashboardService.connectionManagementService.connectionInfo.serverInfo.isCloud;
|
|
||||||
}
|
|
||||||
|
|
||||||
public layout() {
|
|
||||||
this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement)));
|
|
||||||
}
|
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
|
||||||
if (this.isVisible === false && this._gridEl.nativeElement.offsetParent !== null) {
|
|
||||||
this.isVisible = true;
|
|
||||||
if (!this.isInitialized) {
|
|
||||||
this._showProgressWheel = true;
|
|
||||||
this.onFirstVisible(false);
|
|
||||||
this.isInitialized = true;
|
|
||||||
}
|
|
||||||
} 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._gridEl.nativeElement.offsetParent === null) {
|
|
||||||
this.isVisible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFirstVisible(cached?: boolean) {
|
|
||||||
let self = this;
|
|
||||||
let columns = this.columns.map((column) => {
|
|
||||||
column.rerenderOnResize = true;
|
|
||||||
return column;
|
|
||||||
});
|
|
||||||
let options = <Slick.GridOptions<any>>{
|
|
||||||
syncColumnCellResize: true,
|
|
||||||
enableColumnReorder: false,
|
|
||||||
rowHeight: ROW_HEIGHT,
|
|
||||||
enableCellNavigation: true,
|
|
||||||
forceFitColumns: true
|
|
||||||
};
|
|
||||||
|
|
||||||
this.dataView = new Slick.Data.DataView();
|
|
||||||
|
|
||||||
$(this._gridEl.nativeElement).empty();
|
|
||||||
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
|
|
||||||
this._table.grid.setData(this.dataView, true);
|
|
||||||
|
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
|
||||||
this._jobManagementService.getAlerts(ownerUri).then((result) => {
|
|
||||||
if (result && result.alerts) {
|
|
||||||
self.alerts = result.alerts;
|
|
||||||
self.onAlertsAvailable(result.alerts);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onAlertsAvailable(alerts: sqlops.AgentAlertInfo[]) {
|
|
||||||
let items: any = alerts.map((item) => {
|
|
||||||
return {
|
|
||||||
id: item.id,
|
|
||||||
name: item.name,
|
|
||||||
lastOccurrenceDate: item.lastOccurrenceDate,
|
|
||||||
enabled: item.isEnabled,
|
|
||||||
databaseName: item.databaseName,
|
|
||||||
categoryName: item.categoryName
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
this.dataView.beginUpdate();
|
|
||||||
this.dataView.setItems(items);
|
|
||||||
this.dataView.endUpdate();
|
|
||||||
this._table.autosizeColumns();
|
|
||||||
this._table.resizeCanvas();
|
|
||||||
|
|
||||||
this._showProgressWheel = false;
|
|
||||||
this._cd.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
private openCreateJobDialog() {
|
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
|
||||||
this._commandService.executeCommand('agent.openCreateAlertDialog', ownerUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
private refreshJobs() {
|
|
||||||
this._agentViewComponent.refresh = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
<!-- Job History details -->
|
<!-- Job History details -->
|
||||||
<div class='history-details'>
|
<div class='history-details'>
|
||||||
<!-- Previous run list -->
|
<!-- Previous run list -->
|
||||||
<div class="prev-run-list-container" style="min-width: 275px; height: 75vh">
|
<div style="min-width: 275px">
|
||||||
<table *ngIf="_showPreviousRuns === true">
|
<table *ngIf="_showPreviousRuns === true">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="date-column">
|
<td class="date-column">
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
import 'vs/css!./jobHistory';
|
import 'vs/css!./jobHistory';
|
||||||
import 'vs/css!sql/media/icons/common-icons';
|
import 'vs/css!sql/media/icons/common-icons';
|
||||||
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
||||||
import * as sqlops from 'sqlops';
|
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, NewStepAction } from 'sql/parts/jobManagement/views/jobActions';
|
import { RunJobAction, StopJobAction } from 'sql/parts/jobManagement/views/jobHistoryActions';
|
||||||
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
|
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
|
||||||
import { AgentJobUtilities } from '../common/agentJobUtilities';
|
import { AgentJobUtilities } from '../common/agentJobUtilities';
|
||||||
import { IJobManagementService } from '../common/interfaces';
|
import { IJobManagementService } from '../common/interfaces';
|
||||||
@@ -46,9 +46,9 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
@ViewChild('table') private _tableContainer: ElementRef;
|
@ViewChild('table') private _tableContainer: ElementRef;
|
||||||
@ViewChild('actionbarContainer') private _actionbarContainer: ElementRef;
|
@ViewChild('actionbarContainer') private _actionbarContainer: ElementRef;
|
||||||
|
|
||||||
@Input() public agentJobInfo: sqlops.AgentJobInfo = undefined;
|
@Input() public agentJobInfo: AgentJobInfo = undefined;
|
||||||
@Input() public agentJobHistories: sqlops.AgentJobHistoryInfo[] = undefined;
|
@Input() public agentJobHistories: AgentJobHistoryInfo[] = undefined;
|
||||||
public agentJobHistoryInfo: sqlops.AgentJobHistoryInfo = undefined;
|
public agentJobHistoryInfo: AgentJobHistoryInfo = undefined;
|
||||||
|
|
||||||
private _isVisible: boolean = false;
|
private _isVisible: boolean = false;
|
||||||
private _stepRows: JobStepsViewRow[] = [];
|
private _stepRows: JobStepsViewRow[] = [];
|
||||||
@@ -56,12 +56,8 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
private _showPreviousRuns: boolean = undefined;
|
private _showPreviousRuns: boolean = undefined;
|
||||||
private _runStatus: string = undefined;
|
private _runStatus: string = undefined;
|
||||||
private _jobCacheObject: JobCacheObject;
|
private _jobCacheObject: JobCacheObject;
|
||||||
private _agentJobInfo: sqlops.AgentJobInfo;
|
private _agentJobInfo: AgentJobInfo;
|
||||||
private _noJobsAvailable: boolean = false;
|
private _noJobsAvailable: boolean = false;
|
||||||
private _serverName: string;
|
|
||||||
|
|
||||||
private static readonly INITIAL_TREE_HEIGHT: number = 780;
|
|
||||||
private static readonly HEADING_HEIGHT: number = 24;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
||||||
@@ -80,14 +76,14 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
this._treeRenderer = new JobHistoryRenderer();
|
this._treeRenderer = new JobHistoryRenderer();
|
||||||
this._treeFilter = new JobHistoryFilter();
|
this._treeFilter = new JobHistoryFilter();
|
||||||
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
||||||
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
let serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||||
let jobCache = jobCacheObjectMap[this._serverName];
|
let jobCache = jobCacheObjectMap[serverName];
|
||||||
if (jobCache) {
|
if (jobCache) {
|
||||||
this._jobCacheObject = jobCache;
|
this._jobCacheObject = jobCache;
|
||||||
} else {
|
} else {
|
||||||
this._jobCacheObject = new JobCacheObject();
|
this._jobCacheObject = new JobCacheObject();
|
||||||
this._jobCacheObject.serverName = this._serverName;
|
this._jobCacheObject.serverName = serverName;
|
||||||
this._jobManagementService.addToCache(this._serverName, this._jobCacheObject);
|
this._jobManagementService.addToCache(serverName, this._jobCacheObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,17 +124,8 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
renderer: this._treeRenderer
|
renderer: this._treeRenderer
|
||||||
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
||||||
this._register(attachListStyler(this._tree, this.themeService));
|
this._register(attachListStyler(this._tree, this.themeService));
|
||||||
this._tree.layout(JobHistoryComponent.INITIAL_TREE_HEIGHT);
|
this._tree.layout(1024);
|
||||||
this._initActionBar();
|
this._initActionBar();
|
||||||
$(window).resize(() => {
|
|
||||||
let historyDetails = $('.overview-container').get(0);
|
|
||||||
let statusBar = $('.part.statusbar').get(0);
|
|
||||||
if (historyDetails && statusBar) {
|
|
||||||
let historyBottom = historyDetails.getBoundingClientRect().bottom;
|
|
||||||
let statusTop = statusBar.getBoundingClientRect().top;
|
|
||||||
this._tree.layout(statusTop - historyBottom - JobHistoryComponent.HEADING_HEIGHT);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
ngAfterContentChecked() {
|
||||||
@@ -223,7 +210,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildHistoryTree(self: any, jobHistories: sqlops.AgentJobHistoryInfo[]) {
|
private buildHistoryTree(self: any, jobHistories: AgentJobHistoryInfo[]) {
|
||||||
self._treeController.jobHistories = jobHistories;
|
self._treeController.jobHistories = jobHistories;
|
||||||
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, jobHistories);
|
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, jobHistories);
|
||||||
let jobHistoryRows = this._treeController.jobHistories.map(job => self.convertToJobHistoryRow(job));
|
let jobHistoryRows = this._treeController.jobHistories.map(job => self.convertToJobHistoryRow(job));
|
||||||
@@ -250,7 +237,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
this._agentViewComponent.showHistory = false;
|
this._agentViewComponent.showHistory = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertToJobHistoryRow(historyInfo: sqlops.AgentJobHistoryInfo): JobHistoryRow {
|
private convertToJobHistoryRow(historyInfo: AgentJobHistoryInfo): JobHistoryRow {
|
||||||
let jobHistoryRow = new JobHistoryRow();
|
let jobHistoryRow = new JobHistoryRow();
|
||||||
jobHistoryRow.runDate = this.formatTime(historyInfo.runDate);
|
jobHistoryRow.runDate = this.formatTime(historyInfo.runDate);
|
||||||
jobHistoryRow.runStatus = AgentJobUtilities.convertToStatusString(historyInfo.runStatus);
|
jobHistoryRow.runStatus = AgentJobUtilities.convertToStatusString(historyInfo.runStatus);
|
||||||
@@ -276,14 +263,12 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
private _initActionBar() {
|
private _initActionBar() {
|
||||||
let runJobAction = this.instantiationService.createInstance(RunJobAction);
|
let runJobAction = this.instantiationService.createInstance(RunJobAction);
|
||||||
let stopJobAction = this.instantiationService.createInstance(StopJobAction);
|
let stopJobAction = this.instantiationService.createInstance(StopJobAction);
|
||||||
let newStepAction = this.instantiationService.createInstance(NewStepAction);
|
|
||||||
let taskbar = <HTMLElement>this._actionbarContainer.nativeElement;
|
let taskbar = <HTMLElement>this._actionbarContainer.nativeElement;
|
||||||
this._actionBar = new Taskbar(taskbar, this.contextMenuService);
|
this._actionBar = new Taskbar(taskbar, this.contextMenuService);
|
||||||
this._actionBar.context = this;
|
this._actionBar.context = this;
|
||||||
this._actionBar.setContent([
|
this._actionBar.setContent([
|
||||||
{ action: runJobAction },
|
{ action: runJobAction },
|
||||||
{ action: stopJobAction },
|
{ action: stopJobAction }
|
||||||
{ action: newStepAction }
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,10 +286,6 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
return this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
return this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get serverName(): string {
|
|
||||||
return this._serverName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** SETTERS */
|
/** SETTERS */
|
||||||
|
|
||||||
public set showSteps(value: boolean) {
|
public set showSteps(value: boolean) {
|
||||||
|
|||||||
@@ -122,15 +122,6 @@ input#accordion:checked ~ .accordion-content {
|
|||||||
background-image: url('../common/media/stop.svg');
|
background-image: url('../common/media/stop.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.vs .action-label.icon.newStepIcon {
|
|
||||||
background-image: url('../common/media/new.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.vs-dark .action-label.icon.newStepIcon,
|
|
||||||
.hc-black .action-label.icon.newStepIcon {
|
|
||||||
background-image: url('../common/media/new_inverse.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
a.action-label.icon.runJobIcon.non-runnable {
|
a.action-label.icon.runJobIcon.non-runnable {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
@@ -230,6 +221,10 @@ table.step-list tr.step-row td {
|
|||||||
min-height: 40px !important;
|
min-height: 40px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jobhistory-component .history-details .step-table.prev-run-list .monaco-scrollable-element {
|
||||||
|
overflow-y: scroll !important;
|
||||||
|
}
|
||||||
|
|
||||||
jobhistory-component .jobhistory-heading-container {
|
jobhistory-component .jobhistory-heading-container {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,11 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
|||||||
import Severity from 'vs/base/common/severity';
|
import Severity from 'vs/base/common/severity';
|
||||||
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
||||||
import { IJobManagementService } from '../common/interfaces';
|
import { IJobManagementService } from '../common/interfaces';
|
||||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
|
||||||
import { IConnectionManagementService } from '../../connection/common/connectionManagement';
|
|
||||||
|
|
||||||
export enum JobActions {
|
export enum JobHistoryActions {
|
||||||
Run = 'run',
|
Run = 'run',
|
||||||
Stop = 'stop',
|
Stop = 'stop',
|
||||||
NewStep = 'newStep'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RunJobAction extends Action {
|
export class RunJobAction extends Action {
|
||||||
public static ID = 'jobaction.runJob';
|
public static ID = 'jobaction.runJob';
|
||||||
public static LABEL = nls.localize('jobaction.run', "Run");
|
public static LABEL = nls.localize('jobaction.run', "Run");
|
||||||
@@ -34,7 +30,7 @@ export class RunJobAction extends Action {
|
|||||||
let jobName = context.agentJobInfo.name;
|
let jobName = context.agentJobInfo.name;
|
||||||
let ownerUri = context.ownerUri;
|
let ownerUri = context.ownerUri;
|
||||||
return new TPromise<boolean>((resolve, reject) => {
|
return new TPromise<boolean>((resolve, reject) => {
|
||||||
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(result => {
|
this.jobManagementService.jobAction(ownerUri, jobName, JobHistoryActions.Run).then(result => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
var startMsg = nls.localize('jobSuccessfullyStarted', ': The job was successfully started.');
|
var startMsg = nls.localize('jobSuccessfullyStarted', ': The job was successfully started.');
|
||||||
this.notificationService.notify({
|
this.notificationService.notify({
|
||||||
@@ -69,7 +65,7 @@ export class StopJobAction extends Action {
|
|||||||
let jobName = context.agentJobInfo.name;
|
let jobName = context.agentJobInfo.name;
|
||||||
let ownerUri = context.ownerUri;
|
let ownerUri = context.ownerUri;
|
||||||
return new TPromise<boolean>((resolve, reject) => {
|
return new TPromise<boolean>((resolve, reject) => {
|
||||||
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(result => {
|
this.jobManagementService.jobAction(ownerUri, jobName, JobHistoryActions.Stop).then(result => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
var stopMsg = nls.localize('jobSuccessfullyStopped', ': The job was successfully stopped.');
|
var stopMsg = nls.localize('jobSuccessfullyStopped', ': The job was successfully stopped.');
|
||||||
this.notificationService.notify({
|
this.notificationService.notify({
|
||||||
@@ -87,30 +83,4 @@ export class StopJobAction extends Action {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export class NewStepAction extends Action {
|
|
||||||
public static ID = 'jobaction.newStep';
|
|
||||||
public static LABEL = nls.localize('jobaction.newStep', "New Step");
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@INotificationService private notificationService: INotificationService,
|
|
||||||
@ICommandService private _commandService: ICommandService,
|
|
||||||
@IConnectionManagementService private _connectionService
|
|
||||||
) {
|
|
||||||
super(NewStepAction.ID, NewStepAction.LABEL, 'newStepIcon');
|
|
||||||
}
|
|
||||||
|
|
||||||
public run(context: JobHistoryComponent): TPromise<boolean> {
|
|
||||||
let ownerUri = context.ownerUri;
|
|
||||||
let jobName = context.agentJobInfo.name;
|
|
||||||
let server = context.serverName;
|
|
||||||
let stepId = 0;
|
|
||||||
if (context.agentJobHistoryInfo && context.agentJobHistoryInfo.steps) {
|
|
||||||
stepId = context.agentJobHistoryInfo.steps.length + 1;
|
|
||||||
}
|
|
||||||
return new TPromise<boolean>((resolve, reject) => {
|
|
||||||
resolve(this._commandService.executeCommand('agent.openNewStepDialog', ownerUri, jobName, server, stepId));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -9,9 +9,5 @@
|
|||||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Jobs Available</h1>
|
<h1 class="job-heading" *ngIf="_isCloud === true">No Jobs Available</h1>
|
||||||
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
|
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="jobs-view-toolbar">
|
|
||||||
<div (click)="refreshJobs()" tabindex="0"><div class="small icon refresh"></div><span>{{RefreshText}}</span></div>
|
|
||||||
<div (click)="openCreateJobDialog()" tabindex="0"><div class="small icon new"></div><span>{{NewJobText}}</span></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div #jobsgrid class="jobview-grid"></div>
|
<div #jobsgrid class="jobview-grid"></div>
|
||||||
@@ -27,7 +27,7 @@ import { IJobManagementService } from '../common/interfaces';
|
|||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
|
||||||
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
|
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
|
||||||
export const ROW_HEIGHT: number = 45;
|
export const ROW_HEIGHT: number = 45;
|
||||||
|
|
||||||
@@ -44,28 +44,16 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
private _disposables = new Array<vscode.Disposable>();
|
private _disposables = new Array<vscode.Disposable>();
|
||||||
|
|
||||||
private columns: Array<Slick.Column<any>> = [
|
private columns: Array<Slick.Column<any>> = [
|
||||||
{
|
{ 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.name', 'Name'),
|
{ name: nls.localize('jobColumns.lastRun','Last Run'), field: 'lastRun', width: 120, id: 'lastRun' },
|
||||||
field: 'name',
|
{ name: nls.localize('jobColumns.nextRun','Next Run'), field: 'nextRun', width: 120, id: 'nextRun' },
|
||||||
formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext),
|
{ name: nls.localize('jobColumns.enabled','Enabled'), field: 'enabled', width: 50, id: 'enabled' },
|
||||||
width: 150,
|
{ name: nls.localize('jobColumns.status','Status'), field: 'currentExecutionStatus', width: 60, id: 'currentExecutionStatus' },
|
||||||
id: 'name'
|
{ 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.lastRun', 'Last Run'), field: 'lastRun', width: 80, id: 'lastRun' },
|
{ name: nls.localize('jobColumns.schedule','Schedule'), field: 'hasSchedule', width: 60, id: 'hasSchedule' },
|
||||||
{ name: nls.localize('jobColumns.nextRun', 'Next Run'), field: 'nextRun', width: 80, id: 'nextRun' },
|
{ name: nls.localize('jobColumns.lastRunOutcome', 'Last Run Outcome'), field: 'lastRunOutcome', width: 120, id: 'lastRunOutcome' },
|
||||||
{ name: nls.localize('jobColumns.enabled', 'Enabled'), field: 'enabled', width: 60, id: 'enabled' },
|
{ name: nls.localize('jobColumns.previousRuns', 'Previous Runs'), formatter: this.renderChartsPostHistory, field: 'previousRuns', width: 80, id: 'previousRuns'}
|
||||||
{ name: nls.localize('jobColumns.status', 'Status'), field: 'currentExecutionStatus', width: 50, id: 'currentExecutionStatus' },
|
|
||||||
{ name: nls.localize('jobColumns.category', 'Category'), field: 'category', width: 100, 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: 100, id: 'lastRunOutcome' },
|
|
||||||
{
|
|
||||||
name: nls.localize('jobColumns.previousRuns', 'Previous Runs'),
|
|
||||||
formatter: (row, cell, value, columnDef, dataContext) => this.renderChartsPostHistory(row, cell, value, columnDef, dataContext),
|
|
||||||
field: 'previousRuns',
|
|
||||||
width: 100,
|
|
||||||
id: 'previousRuns'
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
@@ -91,22 +79,19 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
private _serverName: string;
|
private _serverName: string;
|
||||||
private _isCloud: boolean;
|
private _isCloud: boolean;
|
||||||
private _showProgressWheel: boolean;
|
private _showProgressWheel: boolean;
|
||||||
private filterStylingMap: { [columnName: string]: [any]; } = {};
|
private _tabHeight: number;
|
||||||
|
private filterStylingMap: { [columnName: string]: [any] ;} = {};
|
||||||
private filterStack = ['start'];
|
private filterStack = ['start'];
|
||||||
private filterValueMap: { [columnName: string]: string[]; } = {};
|
private filterValueMap: { [columnName: string]: string[] ;} = {};
|
||||||
private sortingStylingMap: { [columnName: string]: any; } = {};
|
private sortingStylingMap: { [columnName: string]: any; } = {};
|
||||||
|
|
||||||
private NewJobText: string = nls.localize("jobsToolbar-NewJob", "New Job");
|
|
||||||
private RefreshText: string = nls.localize("jobsToolbar-Refresh", "Refresh");
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
|
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
||||||
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
|
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
|
||||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||||
@Inject(IThemeService) private _themeService: IThemeService,
|
@Inject(IThemeService) private _themeService: IThemeService
|
||||||
@Inject(ICommandService) private _commandService: ICommandService
|
|
||||||
) {
|
) {
|
||||||
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
||||||
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||||
@@ -145,13 +130,13 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
this.onFirstVisible(false);
|
this.onFirstVisible(false);
|
||||||
this.isRefreshing = true;
|
this.isRefreshing = true;
|
||||||
this._agentViewComponent.refresh = false;
|
this._agentViewComponent.refresh = false;
|
||||||
} /*else if (this.isVisible === true && this._agentViewComponent.refresh === false) {
|
} else if (this.isVisible === true && this._agentViewComponent.refresh === false) {
|
||||||
if (!this.isRefreshing) {
|
if (!this.isRefreshing) {
|
||||||
this._showProgressWheel = true;
|
this._showProgressWheel = true;
|
||||||
this.onFirstVisible(true);
|
this.onFirstVisible(true);
|
||||||
}
|
}
|
||||||
this.isRefreshing = false;
|
this.isRefreshing = false;
|
||||||
}*/ else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) {
|
} else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) {
|
||||||
this.isVisible = false;
|
this.isVisible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,9 +241,9 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
// apply the previous filter styling
|
// apply the previous filter styling
|
||||||
let currentItems = this.dataView.getFilteredItems();
|
let currentItems = this.dataView.getFilteredItems();
|
||||||
let styledItems = this.filterValueMap[this.filterStack[this.filterStack.length - 1]][1];
|
let styledItems = this.filterValueMap[this.filterStack[this.filterStack.length-1]][1];
|
||||||
if (styledItems === currentItems) {
|
if (styledItems === currentItems) {
|
||||||
let lastColStyle = this.filterStylingMap[this.filterStack[this.filterStack.length - 1]];
|
let lastColStyle = this.filterStylingMap[this.filterStack[this.filterStack.length-1]];
|
||||||
for (let i = 0; i < lastColStyle.length; i++) {
|
for (let i = 0; i < lastColStyle.length; i++) {
|
||||||
this._table.grid.setCellCssStyles(lastColStyle[i][0], lastColStyle[i][1]);
|
this._table.grid.setCellCssStyles(lastColStyle[i][0], lastColStyle[i][1]);
|
||||||
}
|
}
|
||||||
@@ -266,7 +251,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
// style it all over again
|
// style it all over again
|
||||||
let seenJobs = 0;
|
let seenJobs = 0;
|
||||||
for (let i = 0; i < currentItems.length; i++) {
|
for (let i = 0; i < currentItems.length; i++) {
|
||||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
this._table.grid.removeCellCssStyles('error-row'+i.toString());
|
||||||
let item = this.dataView.getFilteredItems()[i];
|
let item = this.dataView.getFilteredItems()[i];
|
||||||
if (item.lastRunOutcome === 'Failed') {
|
if (item.lastRunOutcome === 'Failed') {
|
||||||
this.addToStyleHash(seenJobs, false, this.filterStylingMap, args.column.name);
|
this.addToStyleHash(seenJobs, false, this.filterStylingMap, args.column.name);
|
||||||
@@ -276,7 +261,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
// one expansion for the row and one for
|
// one expansion for the row and one for
|
||||||
// the error detail
|
// the error detail
|
||||||
seenJobs++;
|
seenJobs ++;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
seenJobs++;
|
seenJobs++;
|
||||||
@@ -292,7 +277,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
} else {
|
} else {
|
||||||
let seenJobs = 0;
|
let seenJobs = 0;
|
||||||
for (let i = 0; i < this.jobs.length; i++) {
|
for (let i = 0; i < this.jobs.length; i++) {
|
||||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
this._table.grid.removeCellCssStyles('error-row'+i.toString());
|
||||||
let item = this.dataView.getItemByIdx(i);
|
let item = this.dataView.getItemByIdx(i);
|
||||||
// current filter
|
// current filter
|
||||||
if (_.contains(filterValues, item[args.column.field])) {
|
if (_.contains(filterValues, item[args.column.field])) {
|
||||||
@@ -306,7 +291,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
// one expansion for the row and one for
|
// one expansion for the row and one for
|
||||||
// the error detail
|
// the error detail
|
||||||
seenJobs++;
|
seenJobs ++;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
seenJobs++;
|
seenJobs++;
|
||||||
@@ -346,20 +331,21 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
currentTarget.title = currentTarget.innerText;
|
currentTarget.title = currentTarget.innerText;
|
||||||
});
|
});
|
||||||
this._showProgressWheel = false;
|
this._showProgressWheel = false;
|
||||||
|
this._cd.detectChanges();
|
||||||
if (this.isVisible) {
|
|
||||||
this._cd.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
this._tabHeight = $('agentview-component #jobsDiv .jobview-grid').get(0).clientHeight;
|
||||||
$(window).resize(() => {
|
$(window).resize(() => {
|
||||||
let jobsViewToolbar = $('jobsview-component .jobs-view-toolbar').get(0);
|
let currentTab = $('agentview-component #jobsDiv .jobview-grid').get(0);
|
||||||
let statusBar = $('.part.statusbar').get(0);
|
if (currentTab) {
|
||||||
if (jobsViewToolbar && statusBar) {
|
let currentTabHeight = currentTab.clientHeight;
|
||||||
let toolbarBottom = jobsViewToolbar.getBoundingClientRect().bottom;
|
if (currentTabHeight < self._tabHeight) {
|
||||||
let statusTop = statusBar.getBoundingClientRect().top;
|
$('agentview-component #jobsDiv div.ui-widget').css('height', `${currentTabHeight - 22}px`);
|
||||||
$('agentview-component #jobsDiv .jobview-grid').css('height', statusTop - toolbarBottom);
|
self._table.resizeCanvas();
|
||||||
self._table.resizeCanvas();
|
} else {
|
||||||
|
$('agentview-component #jobsDiv div.ui-widget').css('height', `${currentTabHeight}px`);
|
||||||
|
self._table.resizeCanvas();
|
||||||
|
}
|
||||||
|
self._tabHeight = currentTabHeight;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this._table.grid.onColumnsResized.subscribe((e, data: any) => {
|
this._table.grid.onColumnsResized.subscribe((e, data: any) => {
|
||||||
@@ -371,17 +357,49 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
|
|
||||||
// generate job charts again
|
// generate job charts again
|
||||||
self.jobs.forEach(job => {
|
self.jobs.forEach(job => {
|
||||||
|
let jobId = job.jobId;
|
||||||
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
||||||
let previousRuns = jobHistories.slice(jobHistories.length - 5, jobHistories.length);
|
let previousRuns = jobHistories.slice(jobHistories.length-5, jobHistories.length);
|
||||||
self.createJobChart(job.jobId, previousRuns);
|
self.createJobChart(job.jobId, previousRuns);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$('#jobsDiv .jobview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e1) =>
|
$('#jobsDiv .jobview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e) => {
|
||||||
this.highlightErrorRows(e1), (e2) => this.hightlightNonErrorRows(e2));
|
// highlight the error row as well if a failing job row is hovered
|
||||||
|
if (e.currentTarget.children.item(0).classList.contains('job-with-error')) {
|
||||||
this._table.grid.onScroll.subscribe((e) => {
|
let target = $(e.currentTarget);
|
||||||
$('#jobsDiv .jobview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e1) =>
|
let targetChildren = $(e.currentTarget.children);
|
||||||
this.highlightErrorRows(e1), (e2) => this.hightlightNonErrorRows(e2));
|
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
|
// cache the dataview for future use
|
||||||
this._jobCacheObject.dataView = this.dataView;
|
this._jobCacheObject.dataView = this.dataView;
|
||||||
@@ -389,47 +407,6 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
this.loadJobHistories();
|
this.loadJobHistories();
|
||||||
}
|
}
|
||||||
|
|
||||||
private highlightErrorRows(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private hightlightNonErrorRows(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setRowWithErrorClass(hash: { [index: number]: { [id: string]: string; } }, row: number, errorClass: string) {
|
private setRowWithErrorClass(hash: { [index: number]: { [id: string]: string; } }, row: number, errorClass: string) {
|
||||||
hash[row] = {
|
hash[row] = {
|
||||||
'_detail_selector': errorClass,
|
'_detail_selector': errorClass,
|
||||||
@@ -450,27 +427,27 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private addToStyleHash(row: number, start: boolean, map: any, columnName: string) {
|
private addToStyleHash(row: number, start: boolean, map: any, columnName: string) {
|
||||||
let hash: {
|
let hash : {
|
||||||
[index: number]: {
|
[index: number]: {
|
||||||
[id: string]: string;
|
[id: string]: string;
|
||||||
}
|
}
|
||||||
} = {};
|
} = {};
|
||||||
hash = this.setRowWithErrorClass(hash, row, 'job-with-error');
|
hash = this.setRowWithErrorClass(hash, row, 'job-with-error');
|
||||||
hash = this.setRowWithErrorClass(hash, row + 1, 'error-row');
|
hash = this.setRowWithErrorClass(hash, row+1, 'error-row');
|
||||||
if (start) {
|
if (start) {
|
||||||
if (map['start']) {
|
if (map['start']) {
|
||||||
map['start'].push(['error-row' + row.toString(), hash]);
|
map['start'].push(['error-row'+row.toString(), hash]);
|
||||||
} else {
|
} else {
|
||||||
map['start'] = [['error-row' + row.toString(), hash]];
|
map['start'] = [['error-row'+row.toString(), hash]];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (map[columnName]) {
|
if (map[columnName]) {
|
||||||
map[columnName].push(['error-row' + row.toString(), hash]);
|
map[columnName].push(['error-row'+row.toString(), hash]);
|
||||||
} else {
|
} else {
|
||||||
map[columnName] = [['error-row' + row.toString(), hash]];
|
map[columnName] = [['error-row'+row.toString(), hash]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._table.grid.setCellCssStyles('error-row' + row.toString(), hash);
|
this._table.grid.setCellCssStyles('error-row'+row.toString(), hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderName(row, cell, value, columnDef, dataContext) {
|
private renderName(row, cell, value, columnDef, dataContext) {
|
||||||
@@ -500,28 +477,14 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderChartsPostHistory(row, cell, value, columnDef, dataContext) {
|
private renderChartsPostHistory(row, cell, value, columnDef, dataContext) {
|
||||||
let runChart = this._jobCacheObject.getRunChart(dataContext.id);
|
return `<table class="jobprevruns" id="${dataContext.id}">
|
||||||
if (runChart && runChart.length > 0) {
|
|
||||||
return `<table class="jobprevruns" id="${dataContext.id}">
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>${runChart[0] ? runChart[0] : '<div></div>' }</td>
|
<td><div class="bar1"></div></td>
|
||||||
<td>${runChart[1] ? runChart[1] : '<div></div>' }</td>
|
<td><div class="bar2"></div></td>
|
||||||
<td>${runChart[2] ? runChart[2] : '<div></div>' }</td>
|
<td><div class="bar3"></div></td>
|
||||||
<td>${runChart[3] ? runChart[3] : '<div></div>' }</td>
|
<td><div class="bar4"></div></td>
|
||||||
<td>${runChart[4] ? runChart[4] : '<div></div>' }</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>`;
|
</table>`;
|
||||||
} else {
|
|
||||||
return `<table class="jobprevruns" id="${dataContext.id}">
|
|
||||||
<tr>
|
|
||||||
<td><div class="bar0"></div></td>
|
|
||||||
<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 {
|
private expandJobRowDetails(rowIdx: number, message?: string): void {
|
||||||
@@ -594,13 +557,13 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
self.jobHistories[job.jobId] = result.jobs;
|
self.jobHistories[job.jobId] = result.jobs;
|
||||||
self._jobCacheObject.setJobHistory(job.jobId, result.jobs);
|
self._jobCacheObject.setJobHistory(job.jobId, result.jobs);
|
||||||
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
||||||
let previousRuns = jobHistories.slice(jobHistories.length - 5, jobHistories.length);
|
let previousRuns = jobHistories.slice(jobHistories.length-5, jobHistories.length);
|
||||||
self.createJobChart(job.jobId, previousRuns);
|
self.createJobChart(job.jobId, previousRuns);
|
||||||
if (self._agentViewComponent.expanded.has(job.jobId)) {
|
if (self._agentViewComponent.expanded.has(job.jobId)) {
|
||||||
let lastJobHistory = jobHistories[result.jobs.length - 1];
|
let lastJobHistory = jobHistories[result.jobs.length-1];
|
||||||
let item = self.dataView.getItemById(job.jobId + '.error');
|
let item = self.dataView.getItemById(job.jobId + '.error');
|
||||||
let noStepsMessage = nls.localize('jobsView.noSteps', 'No Steps available for this job.');
|
let noStepsMessage = nls.localize('jobsView.noSteps', 'No Steps available for this job.');
|
||||||
let errorMessage = lastJobHistory ? lastJobHistory.message : noStepsMessage;
|
let errorMessage = lastJobHistory ? lastJobHistory.message: noStepsMessage;
|
||||||
item['name'] = nls.localize('jobsView.error', 'Error: ') + errorMessage;
|
item['name'] = nls.localize('jobsView.error', 'Error: ') + errorMessage;
|
||||||
self._agentViewComponent.setExpanded(job.jobId, item['name']);
|
self._agentViewComponent.setExpanded(job.jobId, item['name']);
|
||||||
self.dataView.updateItem(job.jobId + '.error', item);
|
self.dataView.updateItem(job.jobId + '.error', item);
|
||||||
@@ -612,9 +575,8 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
|
|
||||||
private createJobChart(jobId: string, jobHistories: sqlops.AgentJobHistoryInfo[]): void {
|
private createJobChart(jobId: string, jobHistories: sqlops.AgentJobHistoryInfo[]): void {
|
||||||
let chartHeights = this.getChartHeights(jobHistories);
|
let chartHeights = this.getChartHeights(jobHistories);
|
||||||
let runCharts = [];
|
|
||||||
for (let i = 0; i < jobHistories.length; i++) {
|
for (let i = 0; i < jobHistories.length; i++) {
|
||||||
let runGraph = $(`table#${jobId}.jobprevruns > tbody > tr > td > div.bar${i}`);
|
let runGraph = $(`table#${jobId}.jobprevruns > tbody > tr > td > div.bar${i+1}`);
|
||||||
if (jobHistories && jobHistories.length > 0) {
|
if (jobHistories && jobHistories.length > 0) {
|
||||||
runGraph.css('height', chartHeights[i]);
|
runGraph.css('height', chartHeights[i]);
|
||||||
let bgColor = jobHistories[i].runStatus === 0 ? 'red' : 'green';
|
let bgColor = jobHistories[i].runStatus === 0 ? 'red' : 'green';
|
||||||
@@ -623,9 +585,6 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
let currentTarget = e.currentTarget;
|
let currentTarget = e.currentTarget;
|
||||||
currentTarget.title = jobHistories[i].runDuration;
|
currentTarget.title = jobHistories[i].runDuration;
|
||||||
});
|
});
|
||||||
if (runGraph.get(0)) {
|
|
||||||
runCharts.push(runGraph.get(0).outerHTML);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
runGraph.css('height', '5px');
|
runGraph.css('height', '5px');
|
||||||
runGraph.css('background', 'red');
|
runGraph.css('background', 'red');
|
||||||
@@ -635,19 +594,16 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (runCharts.length > 0) {
|
|
||||||
this._jobCacheObject.setRunChart(jobId, runCharts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// chart height normalization logic
|
// chart height normalization logic
|
||||||
private getChartHeights(jobHistories: sqlops.AgentJobHistoryInfo[]): string[] {
|
private getChartHeights(jobHistories: sqlops.AgentJobHistoryInfo[]): string[] {
|
||||||
if (!jobHistories || jobHistories.length === 0) {
|
if (!jobHistories || jobHistories.length === 0) {
|
||||||
return ['5px', '5px', '5px', '5px', '5px'];
|
return ['5px','5px','5px','5px','5px'];
|
||||||
}
|
}
|
||||||
let maxDuration: number = 0;
|
let maxDuration: number = 0;
|
||||||
jobHistories.forEach(history => {
|
jobHistories.forEach(history => {
|
||||||
let historyDuration = AgentJobUtilities.convertDurationToSeconds(history.runDuration);
|
let historyDuration = AgentJobUtilities.convertDurationToSeconds(history.runDuration) ;
|
||||||
if (historyDuration > maxDuration) {
|
if (historyDuration > maxDuration) {
|
||||||
maxDuration = historyDuration;
|
maxDuration = historyDuration;
|
||||||
}
|
}
|
||||||
@@ -657,7 +613,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
let chartHeights = [];
|
let chartHeights = [];
|
||||||
for (let i = 0; i < jobHistories.length; i++) {
|
for (let i = 0; i < jobHistories.length; i++) {
|
||||||
let duration = jobHistories[i].runDuration;
|
let duration = jobHistories[i].runDuration;
|
||||||
let chartHeight = (maxBarHeight * AgentJobUtilities.convertDurationToSeconds(duration)) / maxDuration;
|
let chartHeight = (maxBarHeight * AgentJobUtilities.convertDurationToSeconds(duration))/maxDuration;
|
||||||
chartHeights.push(`${chartHeight}px`);
|
chartHeights.push(`${chartHeight}px`);
|
||||||
}
|
}
|
||||||
return chartHeights;
|
return chartHeights;
|
||||||
@@ -669,14 +625,14 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
let expandedJobs = this._agentViewComponent.expanded;
|
let expandedJobs = this._agentViewComponent.expanded;
|
||||||
let expansions = 0;
|
let expansions = 0;
|
||||||
for (let i = 0; i < this.jobs.length; i++) {
|
for (let i = 0; i < this.jobs.length; i++){
|
||||||
let job = this.jobs[i];
|
let job = this.jobs[i];
|
||||||
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
|
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
|
||||||
this.expandJobRowDetails(i + expandedJobs.size);
|
this.expandJobRowDetails(i+expandedJobs.size);
|
||||||
this.addToStyleHash(i + expandedJobs.size, start, this.filterStylingMap, undefined);
|
this.addToStyleHash(i+expandedJobs.size, start, this.filterStylingMap, undefined);
|
||||||
this._agentViewComponent.setExpanded(job.jobId, 'Loading Error...');
|
this._agentViewComponent.setExpanded(job.jobId, 'Loading Error...');
|
||||||
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
|
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
|
||||||
this.addToStyleHash(i + expansions, start, this.filterStylingMap, undefined);
|
this.addToStyleHash(i+expansions, start, this.filterStylingMap, undefined);
|
||||||
expansions++;
|
expansions++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -692,7 +648,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
if (item._parent) {
|
if (item._parent) {
|
||||||
value = value && _.contains(filterValues, item._parent[col.field]);
|
value = value && _.contains(filterValues, item._parent[col.field]);
|
||||||
} else {
|
} else {
|
||||||
value = value && _.contains(filterValues, item[col.field]);
|
value = value && _.contains(filterValues, item[col.field]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -705,8 +661,8 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
let jobItems = items.filter(x => x._parent === undefined);
|
let jobItems = items.filter(x => x._parent === undefined);
|
||||||
let errorItems = items.filter(x => x._parent !== undefined);
|
let errorItems = items.filter(x => x._parent !== undefined);
|
||||||
this.sortingStylingMap[column] = items;
|
this.sortingStylingMap[column] = items;
|
||||||
switch (column) {
|
switch(column) {
|
||||||
case ('Name'): {
|
case('Name'): {
|
||||||
this.dataView.setItems(jobItems);
|
this.dataView.setItems(jobItems);
|
||||||
// sort the actual jobs
|
// sort the actual jobs
|
||||||
this.dataView.sort((item1, item2) => {
|
this.dataView.sort((item1, item2) => {
|
||||||
@@ -714,13 +670,13 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}, isAscending);
|
}, isAscending);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ('Last Run'): {
|
case('Last Run'): {
|
||||||
this.dataView.setItems(jobItems);
|
this.dataView.setItems(jobItems);
|
||||||
// sort the actual jobs
|
// sort the actual jobs
|
||||||
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, true), isAscending);
|
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, true), isAscending);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ('Next Run'): {
|
case ('Next Run') : {
|
||||||
this.dataView.setItems(jobItems);
|
this.dataView.setItems(jobItems);
|
||||||
// sort the actual jobs
|
// sort the actual jobs
|
||||||
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, false), isAscending);
|
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, false), isAscending);
|
||||||
@@ -781,7 +737,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
let item = jobItems[i];
|
let item = jobItems[i];
|
||||||
if (item._child) {
|
if (item._child) {
|
||||||
let child = errorItems.find(error => error === item._child);
|
let child = errorItems.find(error => error === item._child);
|
||||||
jobItems.splice(i + 1, 0, child);
|
jobItems.splice(i+1, 0, child);
|
||||||
jobItemsLength++;
|
jobItemsLength++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -795,12 +751,12 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < this.jobs.length; i++) {
|
for (let i = 0; i < this.jobs.length; i++) {
|
||||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
this._table.grid.removeCellCssStyles('error-row'+i.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add new style to the items back again
|
// add new style to the items back again
|
||||||
items = this.filterStack.length > 1 ? this.dataView.getFilteredItems() : this.dataView.getItems();
|
items = this.filterStack.length > 1 ? this.dataView.getFilteredItems() : this.dataView.getItems();
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i ++) {
|
||||||
let item = items[i];
|
let item = items[i];
|
||||||
if (item.lastRunOutcome === 'Failed') {
|
if (item.lastRunOutcome === 'Failed') {
|
||||||
this.addToStyleHash(i, false, this.sortingStylingMap, column);
|
this.addToStyleHash(i, false, this.sortingStylingMap, column);
|
||||||
@@ -828,13 +784,4 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private openCreateJobDialog() {
|
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
|
||||||
this._commandService.executeCommand('agent.openCreateJobDialog', ownerUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
private refreshJobs() {
|
|
||||||
this._agentViewComponent.refresh = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<!--
|
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
-->
|
|
||||||
<div class="job-heading-container">
|
|
||||||
<h1 class="job-heading" *ngIf="_isCloud === false">Operators</h1>
|
|
||||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Operators Available</h1>
|
|
||||||
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
|
|
||||||
</div>
|
|
||||||
<div class="jobs-view-toolbar">
|
|
||||||
<div (click)="refreshJobs()" tabindex="0"><div class="small icon refresh"></div><span>{{RefreshText}}</span></div>
|
|
||||||
<div (click)="openCreateOperatorDialog()" tabindex="0"><div class="small icon new"></div><span>{{NewOperatorText}}</span></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div #operatorsgrid class="joboperatorsview-grid"></div>
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import 'vs/css!sql/parts/grid/media/slickColorTheme';
|
|
||||||
import 'vs/css!sql/parts/grid/media/flexbox';
|
|
||||||
import 'vs/css!sql/parts/grid/media/styles';
|
|
||||||
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 nls from 'vs/nls';
|
|
||||||
import { Table } from 'sql/base/browser/ui/table/table';
|
|
||||||
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
|
||||||
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';
|
|
||||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
|
||||||
export const VIEW_SELECTOR: string = 'joboperatorsview-component';
|
|
||||||
export const ROW_HEIGHT: number = 45;
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: VIEW_SELECTOR,
|
|
||||||
templateUrl: decodeURI(require.toUrl('./operatorsView.component.html')),
|
|
||||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => OperatorsViewComponent) }],
|
|
||||||
})
|
|
||||||
|
|
||||||
export class OperatorsViewComponent implements AfterContentChecked {
|
|
||||||
|
|
||||||
private columns: Array<Slick.Column<any>> = [
|
|
||||||
{ name: nls.localize('jobOperatorsView.name', 'Name'), field: 'name', width: 200, id: 'name' },
|
|
||||||
{ name: nls.localize('jobOperatorsView.emailAddress', 'Email Address'), field: 'emailAddress', width: 200, id: 'emailAddress' },
|
|
||||||
{ name: nls.localize('jobOperatorsView.enabled', 'Enabled'), field: 'enabled', width: 200, id: 'enabled' },
|
|
||||||
];
|
|
||||||
|
|
||||||
private options: Slick.GridOptions<any> = {
|
|
||||||
syncColumnCellResize: true,
|
|
||||||
enableColumnReorder: false,
|
|
||||||
rowHeight: 45,
|
|
||||||
enableCellNavigation: true,
|
|
||||||
editable: false
|
|
||||||
};
|
|
||||||
|
|
||||||
private dataView: any;
|
|
||||||
|
|
||||||
@ViewChild('operatorsgrid') _gridEl: ElementRef;
|
|
||||||
private isVisible: boolean = false;
|
|
||||||
private isInitialized: boolean = false;
|
|
||||||
private isRefreshing: boolean = false;
|
|
||||||
private _table: Table<any>;
|
|
||||||
public operators: sqlops.AgentOperatorInfo[];
|
|
||||||
private _serverName: string;
|
|
||||||
private _isCloud: boolean;
|
|
||||||
private _showProgressWheel: boolean;
|
|
||||||
|
|
||||||
private NewOperatorText: string = nls.localize('jobOperatorToolbar-NewItem', "New Operator");
|
|
||||||
private RefreshText: string = nls.localize('jobOperatorToolbar-Refresh', "Refresh");
|
|
||||||
|
|
||||||
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(IThemeService) private _themeService: IThemeService,
|
|
||||||
@Inject(ICommandService) private _commandService: ICommandService
|
|
||||||
) {
|
|
||||||
this._isCloud = this._dashboardService.connectionManagementService.connectionInfo.serverInfo.isCloud;
|
|
||||||
}
|
|
||||||
|
|
||||||
public layout() {
|
|
||||||
this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement)));
|
|
||||||
}
|
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
|
||||||
if (this.isVisible === false && this._gridEl.nativeElement.offsetParent !== null) {
|
|
||||||
this.isVisible = true;
|
|
||||||
if (!this.isInitialized) {
|
|
||||||
this._showProgressWheel = true;
|
|
||||||
this.onFirstVisible(false);
|
|
||||||
this.isInitialized = true;
|
|
||||||
}
|
|
||||||
} 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._gridEl.nativeElement.offsetParent === null) {
|
|
||||||
this.isVisible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFirstVisible(cached?: boolean) {
|
|
||||||
let self = this;
|
|
||||||
let columns = this.columns.map((column) => {
|
|
||||||
column.rerenderOnResize = true;
|
|
||||||
return column;
|
|
||||||
});
|
|
||||||
let options = <Slick.GridOptions<any>>{
|
|
||||||
syncColumnCellResize: true,
|
|
||||||
enableColumnReorder: false,
|
|
||||||
rowHeight: ROW_HEIGHT,
|
|
||||||
enableCellNavigation: true,
|
|
||||||
forceFitColumns: true
|
|
||||||
};
|
|
||||||
|
|
||||||
this.dataView = new Slick.Data.DataView();
|
|
||||||
|
|
||||||
$(this._gridEl.nativeElement).empty();
|
|
||||||
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
|
|
||||||
this._table.grid.setData(this.dataView, true);
|
|
||||||
|
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
|
||||||
this._jobManagementService.getOperators(ownerUri).then((result) => {
|
|
||||||
if (result && result.operators) {
|
|
||||||
self.operators = result.operators;
|
|
||||||
self.onOperatorsAvailable(result.operators);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onOperatorsAvailable(operators: sqlops.AgentOperatorInfo[]) {
|
|
||||||
let items: any = operators.map((item) => {
|
|
||||||
return {
|
|
||||||
id: item.id,
|
|
||||||
name: item.name,
|
|
||||||
emailAddress: item.emailAddress,
|
|
||||||
enabled: item.enabled
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
this.dataView.beginUpdate();
|
|
||||||
this.dataView.setItems(items);
|
|
||||||
this.dataView.endUpdate();
|
|
||||||
this._table.autosizeColumns();
|
|
||||||
this._table.resizeCanvas();
|
|
||||||
|
|
||||||
this._showProgressWheel = false;
|
|
||||||
this._cd.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
private openCreateOperatorDialog() {
|
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
|
||||||
this._commandService.executeCommand('agent.openCreateOperatorDialog', ownerUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
private refreshJobs() {
|
|
||||||
this._agentViewComponent.refresh = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<!--
|
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
-->
|
|
||||||
<div class="job-heading-container">
|
|
||||||
<h1 class="job-heading" *ngIf="_isCloud === false">Proxies</h1>
|
|
||||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Proxies Available</h1>
|
|
||||||
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
|
|
||||||
</div>
|
|
||||||
<div class="jobs-view-toolbar">
|
|
||||||
<div (click)="refreshJobs()" tabindex="0"><div class="small icon refresh"></div><span>{{RefreshText}}</span></div>
|
|
||||||
<div (click)="openCreateProxyDialog()" tabindex="0"><div class="small icon new"></div><span>{{NewProxyText}}</span></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div #proxiesgrid class="jobproxiesview-grid"></div>
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import 'vs/css!sql/parts/grid/media/slickColorTheme';
|
|
||||||
import 'vs/css!sql/parts/grid/media/flexbox';
|
|
||||||
import 'vs/css!sql/parts/grid/media/styles';
|
|
||||||
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 nls from 'vs/nls';
|
|
||||||
import { Table } from 'sql/base/browser/ui/table/table';
|
|
||||||
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
|
||||||
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';
|
|
||||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
|
||||||
export const VIEW_SELECTOR: string = 'jobproxiesview-component';
|
|
||||||
export const ROW_HEIGHT: number = 45;
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: VIEW_SELECTOR,
|
|
||||||
templateUrl: decodeURI(require.toUrl('./proxiesView.component.html')),
|
|
||||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => ProxiesViewComponent) }],
|
|
||||||
})
|
|
||||||
|
|
||||||
export class ProxiesViewComponent implements AfterContentChecked {
|
|
||||||
|
|
||||||
private columns: Array<Slick.Column<any>> = [
|
|
||||||
{ name: nls.localize('jobProxiesView.accountName', 'Account Name'), field: 'accountName', width: 200, id: 'accountName' },
|
|
||||||
{ name: nls.localize('jobProxiesView.credentialName', 'Credential Name'), field: 'credentialName', width: 200, id: 'credentialName' },
|
|
||||||
];
|
|
||||||
|
|
||||||
private options: Slick.GridOptions<any> = {
|
|
||||||
syncColumnCellResize: true,
|
|
||||||
enableColumnReorder: false,
|
|
||||||
rowHeight: 45,
|
|
||||||
enableCellNavigation: true,
|
|
||||||
editable: false
|
|
||||||
};
|
|
||||||
|
|
||||||
private dataView: any;
|
|
||||||
|
|
||||||
@ViewChild('proxiesgrid') _gridEl: ElementRef;
|
|
||||||
private isVisible: boolean = false;
|
|
||||||
private isInitialized: boolean = false;
|
|
||||||
private isRefreshing: boolean = false;
|
|
||||||
private _table: Table<any>;
|
|
||||||
public proxies: sqlops.AgentProxyInfo[];
|
|
||||||
private _serverName: string;
|
|
||||||
private _isCloud: boolean;
|
|
||||||
private _showProgressWheel: boolean;
|
|
||||||
|
|
||||||
private NewProxyText: string = nls.localize('jobProxyToolbar-NewItem', "New Proxy");
|
|
||||||
private RefreshText: string = nls.localize('jobProxyToolbar-Refresh', "Refresh");
|
|
||||||
|
|
||||||
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(IThemeService) private _themeService: IThemeService,
|
|
||||||
@Inject(ICommandService) private _commandService: ICommandService
|
|
||||||
) {
|
|
||||||
this._isCloud = this._dashboardService.connectionManagementService.connectionInfo.serverInfo.isCloud;
|
|
||||||
}
|
|
||||||
|
|
||||||
public layout() {
|
|
||||||
this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement)));
|
|
||||||
}
|
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
|
||||||
if (this.isVisible === false && this._gridEl.nativeElement.offsetParent !== null) {
|
|
||||||
this.isVisible = true;
|
|
||||||
if (!this.isInitialized) {
|
|
||||||
this._showProgressWheel = true;
|
|
||||||
this.onFirstVisible(false);
|
|
||||||
this.isInitialized = true;
|
|
||||||
}
|
|
||||||
} 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._gridEl.nativeElement.offsetParent === null) {
|
|
||||||
this.isVisible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFirstVisible(cached?: boolean) {
|
|
||||||
let self = this;
|
|
||||||
let columns = this.columns.map((column) => {
|
|
||||||
column.rerenderOnResize = true;
|
|
||||||
return column;
|
|
||||||
});
|
|
||||||
let options = <Slick.GridOptions<any>>{
|
|
||||||
syncColumnCellResize: true,
|
|
||||||
enableColumnReorder: false,
|
|
||||||
rowHeight: ROW_HEIGHT,
|
|
||||||
enableCellNavigation: true,
|
|
||||||
forceFitColumns: true
|
|
||||||
};
|
|
||||||
|
|
||||||
this.dataView = new Slick.Data.DataView();
|
|
||||||
|
|
||||||
$(this._gridEl.nativeElement).empty();
|
|
||||||
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
|
|
||||||
this._table.grid.setData(this.dataView, true);
|
|
||||||
|
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
|
||||||
this._jobManagementService.getProxies(ownerUri).then((result) => {
|
|
||||||
if (result && result.proxies) {
|
|
||||||
self.proxies = result.proxies;
|
|
||||||
self.onProxiesAvailable(result.proxies);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onProxiesAvailable(proxies: sqlops.AgentProxyInfo[]) {
|
|
||||||
let items: any = proxies.map((item) => {
|
|
||||||
return {
|
|
||||||
id: item.id,
|
|
||||||
accountName: item.accountName,
|
|
||||||
credentialName: item.credentialName
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
this.dataView.beginUpdate();
|
|
||||||
this.dataView.setItems(items);
|
|
||||||
this.dataView.endUpdate();
|
|
||||||
this._table.autosizeColumns();
|
|
||||||
this._table.resizeCanvas();
|
|
||||||
|
|
||||||
this._showProgressWheel = false;
|
|
||||||
this._cd.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
private openCreateProxyDialog() {
|
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
|
||||||
this._commandService.executeCommand('agent.openCreateProxyDialog', ownerUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
private refreshJobs() {
|
|
||||||
this._agentViewComponent.refresh = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,16 +10,21 @@ import {
|
|||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
|
|
||||||
import { ComponentWithIconBase } from 'sql/parts/modelComponents/componentWithIconBase';
|
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
import { attachButtonStyler } from 'sql/common/theme/styler';
|
import { attachButtonStyler } from 'sql/common/theme/styler';
|
||||||
import { Button } from 'sql/base/browser/ui/button/button';
|
import { Button } from 'sql/base/browser/ui/button/button';
|
||||||
|
|
||||||
import { SIDE_BAR_BACKGROUND, SIDE_BAR_TITLE_FOREGROUND } 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 { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
|
import URI from 'vs/base/common/uri';
|
||||||
|
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||||
|
import { createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom';
|
||||||
import { focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry';
|
import { focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry';
|
||||||
import { Color } from 'vs/base/common/color';
|
import { Color } from 'vs/base/common/color';
|
||||||
|
|
||||||
|
type IUserFriendlyIcon = string | URI | { light: string | URI; dark: string | URI };
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'modelview-button',
|
selector: 'modelview-button',
|
||||||
@@ -27,10 +32,12 @@ import { Color } from 'vs/base/common/color';
|
|||||||
<div #input style="width: 100%"></div>
|
<div #input style="width: 100%"></div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export default class ButtonComponent extends ComponentWithIconBase implements IComponent, OnDestroy, AfterViewInit {
|
export default class ButtonComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
@Input() descriptor: IComponentDescriptor;
|
@Input() descriptor: IComponentDescriptor;
|
||||||
@Input() modelStore: IModelStore;
|
@Input() modelStore: IModelStore;
|
||||||
private _button: Button;
|
private _button: Button;
|
||||||
|
private _iconClass: string;
|
||||||
|
private _iconPath: IUserFriendlyIcon;
|
||||||
|
|
||||||
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||||
constructor(
|
constructor(
|
||||||
@@ -64,6 +71,9 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
if (this._iconClass) {
|
||||||
|
removeCSSRulesContainingSelector(this._iconClass);
|
||||||
|
}
|
||||||
this.baseDestroy();
|
this.baseDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,19 +93,22 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
|||||||
this._button.enabled = this.enabled;
|
this._button.enabled = this.enabled;
|
||||||
this._button.label = this.label;
|
this._button.label = this.label;
|
||||||
if (this.width) {
|
if (this.width) {
|
||||||
this._button.setWidth(this.convertSize(this.width.toString()));
|
this._button.setWidth(this.width.toString());
|
||||||
}
|
}
|
||||||
if (this.height) {
|
if (this.height) {
|
||||||
this._button.setWidth(this.convertSize(this.height.toString()));
|
this._button.setWidth(this.height.toString());
|
||||||
}
|
}
|
||||||
this.updateIcon();
|
this.updateIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updateIcon() {
|
private updateIcon() {
|
||||||
if (this.iconPath) {
|
if (this.iconPath && this.iconPath !== this._iconPath) {
|
||||||
|
this._iconPath = this.iconPath;
|
||||||
if (!this._iconClass) {
|
if (!this._iconClass) {
|
||||||
super.updateIcon();
|
const ids = new IdGenerator('button-component-icon-' + Math.round(Math.random() * 1000));
|
||||||
|
this._iconClass = ids.nextId();
|
||||||
this._button.icon = this._iconClass + ' icon';
|
this._button.icon = this._iconClass + ' icon';
|
||||||
|
|
||||||
// Styling for icon button
|
// Styling for icon button
|
||||||
this._register(attachButtonStyler(this._button, this.themeService, {
|
this._register(attachButtonStyler(this._button, this.themeService, {
|
||||||
buttonBackground: Color.transparent.toString(),
|
buttonBackground: Color.transparent.toString(),
|
||||||
@@ -104,6 +117,36 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
|||||||
buttonForeground: foreground
|
buttonForeground: foreground
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeCSSRulesContainingSelector(this._iconClass);
|
||||||
|
const icon = this.getLightIconPath(this.iconPath);
|
||||||
|
const iconDark = this.getDarkIconPath(this.iconPath) || icon;
|
||||||
|
createCSSRule(`.icon.${this._iconClass}`, `background-image: url("${icon}")`);
|
||||||
|
createCSSRule(`.vs-dark .icon.${this._iconClass}, .hc-black .icon.${this._iconClass}`, `background-image: url("${iconDark}")`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getLightIconPath(iconPath: IUserFriendlyIcon): string {
|
||||||
|
if (iconPath && iconPath['light']) {
|
||||||
|
return this.getIconPath(iconPath['light']);
|
||||||
|
} else {
|
||||||
|
return this.getIconPath(<string | URI>iconPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDarkIconPath(iconPath: IUserFriendlyIcon): string {
|
||||||
|
if (iconPath && iconPath['dark']) {
|
||||||
|
return this.getIconPath(iconPath['dark']);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getIconPath(iconPath: string | URI): string {
|
||||||
|
if (typeof iconPath === 'string') {
|
||||||
|
return URI.file(iconPath).toString();
|
||||||
|
} else {
|
||||||
|
let uri = URI.revive(iconPath);
|
||||||
|
return uri.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +160,13 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
|||||||
this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setValueProperties, newValue);
|
this.setPropertyFromUI<sqlops.ButtonProperties, string>(this.setValueProperties, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get iconPath(): string | URI | { light: string | URI; dark: string | URI } {
|
||||||
|
return this.getPropertyOrDefault<sqlops.ButtonProperties, IUserFriendlyIcon>((props) => props.iconPath, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set iconPath(newValue: string | URI | { light: string | URI; dark: string | URI }) {
|
||||||
|
this.setPropertyFromUI<sqlops.ButtonProperties, IUserFriendlyIcon>((properties, iconPath) => { properties.iconPath = iconPath; }, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
private setValueProperties(properties: sqlops.ButtonProperties, label: string): void {
|
private setValueProperties(properties: sqlops.ButtonProperties, label: string): void {
|
||||||
properties.label = label;
|
properties.label = label;
|
||||||
|
|||||||
@@ -1,32 +1,19 @@
|
|||||||
<div *ngIf="label" [class]="getClass()" (click)="onCardClick()" (mouseover)="onCardHoverChanged($event)" (mouseout)="onCardHoverChanged($event)">
|
<div *ngIf="label" class="model-card">
|
||||||
<span *ngIf="hasStatus" class="card-status">
|
<span *ngIf="hasStatus" class="card-status">
|
||||||
<div class="status-content" [style.backgroundColor]="statusColor"></div>
|
<div class="status-content" [style.backgroundColor]="statusColor"></div>
|
||||||
</span>
|
</span>
|
||||||
|
<div class="card-content">
|
||||||
<ng-container *ngIf="isVerticalButton">
|
<h4 class="card-label">{{label}}</h4>
|
||||||
<div class="card-vertical-button">
|
<p class="card-value">{{value}}</p>
|
||||||
<div *ngIf="iconPath" class="iconContainer"><div [class]="iconClass" [style.width]="iconWidth" [style.height]="iconHeight"></div>
|
<span *ngIf="actions">
|
||||||
<hr/>
|
<table class="model-table">
|
||||||
<h4 class="card-label">{{label}}</h4>
|
<tr *ngFor="let action of actions">
|
||||||
</div>
|
<td class="table-row">{{action.label}}</td>
|
||||||
</div>
|
<td *ngIf="action.actionTitle" class="table-row">
|
||||||
</ng-container>
|
<a class="pointer prominent" (click)="onDidActionClick(action)">{{action.actionTitle}}</a>
|
||||||
|
</td>
|
||||||
<ng-container *ngIf="isDetailsCard">
|
</tr>
|
||||||
<div class="card-content">
|
</table>
|
||||||
<h4 class="card-label">{{label}}</h4>
|
</span>
|
||||||
<p class="card-value">{{value}}</p>
|
</div>
|
||||||
<span *ngIf="actions">
|
|
||||||
<table class="model-table">
|
|
||||||
<tr *ngFor="let action of actions">
|
|
||||||
<td class="table-row">{{action.label}}</td>
|
|
||||||
<td *ngIf="action.actionTitle" class="table-row">
|
|
||||||
<a class="pointer prominent" (click)="onDidActionClick(action)">{{action.actionTitle}}</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -15,14 +15,14 @@ import * as colors from 'vs/platform/theme/common/colorRegistry';
|
|||||||
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
|
|
||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||||
import { ComponentWithIconBase } from 'sql/parts/modelComponents/componentWithIconBase';
|
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
import { StatusIndicator, CardProperties, ActionDescriptor } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { StatusIndicator, CardProperties, ActionDescriptor } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: decodeURI(require.toUrl('sql/parts/modelComponents/card.component.html'))
|
templateUrl: decodeURI(require.toUrl('sql/parts/modelComponents/card.component.html'))
|
||||||
})
|
})
|
||||||
export default class CardComponent extends ComponentWithIconBase implements IComponent, OnDestroy {
|
export default class CardComponent extends ComponentBase implements IComponent, OnDestroy {
|
||||||
@Input() descriptor: IComponentDescriptor;
|
@Input() descriptor: IComponentDescriptor;
|
||||||
@Input() modelStore: IModelStore;
|
@Input() modelStore: IModelStore;
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
|
|||||||
|
|
||||||
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
constructor(@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
||||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
|
||||||
) {
|
) {
|
||||||
super(changeRef);
|
super(changeRef);
|
||||||
}
|
}
|
||||||
@@ -46,39 +46,6 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
|
|||||||
this.baseDestroy();
|
this.baseDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _defaultBorderColor = 'rgb(214, 214, 214)';
|
|
||||||
private _hasFocus: boolean;
|
|
||||||
|
|
||||||
public onCardClick() {
|
|
||||||
if (this.selectable) {
|
|
||||||
this.selected = !this.selected;
|
|
||||||
this._changeRef.detectChanges();
|
|
||||||
this._onEventEmitter.fire({
|
|
||||||
eventType: ComponentEventType.onDidClick,
|
|
||||||
args: this.selected
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getBorderColor() {
|
|
||||||
if (this.selectable && this.selected || this._hasFocus) {
|
|
||||||
return 'Blue';
|
|
||||||
} else {
|
|
||||||
return this._defaultBorderColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getClass(): string {
|
|
||||||
return (this.selectable && this.selected || this._hasFocus) ? 'model-card selected' :
|
|
||||||
'model-card unselected';
|
|
||||||
}
|
|
||||||
|
|
||||||
public onCardHoverChanged(event: any) {
|
|
||||||
if (this.selectable) {
|
|
||||||
this._hasFocus = event.type === 'mouseover';
|
|
||||||
this._changeRef.detectChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// IComponent implementation
|
/// IComponent implementation
|
||||||
|
|
||||||
public layout(): void {
|
public layout(): void {
|
||||||
@@ -90,19 +57,6 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
|
|||||||
this.layout();
|
this.layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setProperties(properties: { [key: string]: any; }): void {
|
|
||||||
super.setProperties(properties);
|
|
||||||
this.updateIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
public get iconClass(): string {
|
|
||||||
return this._iconClass + ' icon' + ' cardIcon';
|
|
||||||
}
|
|
||||||
|
|
||||||
private get selectable(): boolean {
|
|
||||||
return this.cardType === 'VerticalButton';
|
|
||||||
}
|
|
||||||
|
|
||||||
// CSS-bound properties
|
// CSS-bound properties
|
||||||
|
|
||||||
public get label(): string {
|
public get label(): string {
|
||||||
@@ -113,27 +67,6 @@ export default class CardComponent extends ComponentWithIconBase implements ICom
|
|||||||
return this.getPropertyOrDefault<CardProperties, string>((props) => props.value, '');
|
return this.getPropertyOrDefault<CardProperties, string>((props) => props.value, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
public get cardType(): string {
|
|
||||||
return this.getPropertyOrDefault<CardProperties, string>((props) => props.cardType, 'Details');
|
|
||||||
}
|
|
||||||
|
|
||||||
public get selected(): boolean {
|
|
||||||
return this.getPropertyOrDefault<sqlops.CardProperties, boolean>((props) => props.selected, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public set selected(newValue: boolean) {
|
|
||||||
this.setPropertyFromUI<sqlops.CardProperties, boolean>((props, value) => props.selected = value, newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get isDetailsCard(): boolean {
|
|
||||||
return !this.cardType || this.cardType === 'Details';
|
|
||||||
}
|
|
||||||
|
|
||||||
public get isVerticalButton(): boolean {
|
|
||||||
return this.cardType === 'VerticalButton';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public get actions(): ActionDescriptor[] {
|
public get actions(): ActionDescriptor[] {
|
||||||
return this.getPropertyOrDefault<CardProperties, ActionDescriptor[]>((props) => props.actions, []);
|
return this.getPropertyOrDefault<CardProperties, ActionDescriptor[]>((props) => props.actions, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,26 +7,12 @@
|
|||||||
margin: 15px;
|
margin: 15px;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
|
border-color: rgb(214, 214, 214);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
box-shadow: rgba(120, 120, 120, 0.75) 0px 0px 6px;
|
box-shadow: rgba(120, 120, 120, 0.75) 0px 0px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-card.selected {
|
|
||||||
border-color: darkblue
|
|
||||||
}
|
|
||||||
|
|
||||||
.vs-dark .monaco-workbench .model-card.selected,
|
|
||||||
.hc-black .monaco-workbench .model-card.selected {
|
|
||||||
border-color: darkblue
|
|
||||||
}
|
|
||||||
|
|
||||||
.model-card.unselected {
|
|
||||||
border-color: rgb(214, 214, 214);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.model-card .card-content {
|
.model-card .card-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -37,16 +23,6 @@
|
|||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-card .card-vertical-button {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
height: auto;
|
|
||||||
width: auto;
|
|
||||||
padding: 5px 5px 5px 5px;
|
|
||||||
min-height: 130px;
|
|
||||||
min-width: 130px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.model-card .card-label {
|
.model-card .card-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -57,19 +33,6 @@
|
|||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-card .iconContainer {
|
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
|
||||||
text-align: center;
|
|
||||||
padding: 10px 0px 10px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.model-card .cardIcon {
|
|
||||||
display: inline-block;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.model-card .card-status {
|
.model-card .card-status {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 7px;
|
top: 7px;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { attachInputBoxStyler, attachListStyler } from 'vs/platform/theme/common
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'modelview-checkbox',
|
selector: 'modelview-checkbox',
|
||||||
template: `
|
template: `
|
||||||
<div #input [style.width]="getWidth()"></div>
|
<div #input style="width: 100%"></div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export default class CheckBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
export default class CheckBoxComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
|
|||||||
@@ -18,12 +18,6 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
|
|||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
||||||
import URI from 'vs/base/common/uri';
|
|
||||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
|
||||||
import { createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom';
|
|
||||||
|
|
||||||
|
|
||||||
export type IUserFriendlyIcon = string | URI | { light: string | URI; dark: string | URI };
|
|
||||||
|
|
||||||
export class ItemDescriptor<T> {
|
export class ItemDescriptor<T> {
|
||||||
constructor(public descriptor: IComponentDescriptor, public config: T) { }
|
constructor(public descriptor: IComponentDescriptor, public config: T) { }
|
||||||
@@ -104,6 +98,10 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
|||||||
if (enabled === undefined) {
|
if (enabled === undefined) {
|
||||||
enabled = true;
|
enabled = true;
|
||||||
properties['enabled'] = enabled;
|
properties['enabled'] = enabled;
|
||||||
|
this.fireEvent({
|
||||||
|
eventType: ComponentEventType.PropertiesChanged,
|
||||||
|
args: this.getProperties()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return <boolean>enabled;
|
return <boolean>enabled;
|
||||||
}
|
}
|
||||||
@@ -148,12 +146,11 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
|||||||
return this.height ? this.convertSize(this.height) : '';
|
return this.height ? this.convertSize(this.height) : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected convertSize(size: number | string, defaultValue?: string): string {
|
protected convertSize(size: number | string): string {
|
||||||
defaultValue = defaultValue || '';
|
|
||||||
if (types.isUndefinedOrNull(size)) {
|
if (types.isUndefinedOrNull(size)) {
|
||||||
return defaultValue;
|
return '100%';
|
||||||
}
|
}
|
||||||
let convertedSize: string = size ? size.toString() : defaultValue;
|
let convertedSize: string = size ? size.toString() : '100%';
|
||||||
if (!convertedSize.toLowerCase().endsWith('px') && !convertedSize.toLowerCase().endsWith('%')) {
|
if (!convertedSize.toLowerCase().endsWith('px') && !convertedSize.toLowerCase().endsWith('%')) {
|
||||||
convertedSize = convertedSize + 'px';
|
convertedSize = convertedSize + 'px';
|
||||||
}
|
}
|
||||||
@@ -207,9 +204,7 @@ export abstract class ContainerBase<T> extends ComponentBase {
|
|||||||
) {
|
) {
|
||||||
super(_changeRef);
|
super(_changeRef);
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this._validations.push(() => this.items.every(item => {
|
this._validations.push(() => this.items.every(item => this.modelStore.getComponent(item.descriptor.id).valid));
|
||||||
return this.modelStore.getComponent(item.descriptor.id).valid;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// IComponent container-related implementation
|
/// IComponent container-related implementation
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
|
||||||
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, OnInit, QueryList
|
|
||||||
} from '@angular/core';
|
|
||||||
|
|
||||||
import { IComponent, IComponentDescriptor, IModelStore, IComponentEventArgs, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import URI from 'vs/base/common/uri';
|
|
||||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
|
||||||
import { createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom';
|
|
||||||
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
|
||||||
|
|
||||||
|
|
||||||
export type IUserFriendlyIcon = string | URI | { light: string | URI; dark: string | URI };
|
|
||||||
|
|
||||||
export class ItemDescriptor<T> {
|
|
||||||
constructor(public descriptor: IComponentDescriptor, public config: T) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class ComponentWithIconBase extends ComponentBase {
|
|
||||||
|
|
||||||
protected _iconClass: string;
|
|
||||||
protected _iconPath: IUserFriendlyIcon;
|
|
||||||
constructor(
|
|
||||||
changeRef: ChangeDetectorRef) {
|
|
||||||
super(changeRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// IComponent implementation
|
|
||||||
|
|
||||||
public get iconClass(): string {
|
|
||||||
return this._iconClass + ' icon';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updateIcon() {
|
|
||||||
if (this.iconPath && this.iconPath !== this._iconPath) {
|
|
||||||
this._iconPath = this.iconPath;
|
|
||||||
if (!this._iconClass) {
|
|
||||||
const ids = new IdGenerator('model-view-component-icon-' + Math.round(Math.random() * 1000));
|
|
||||||
this._iconClass = ids.nextId();
|
|
||||||
}
|
|
||||||
|
|
||||||
removeCSSRulesContainingSelector(this._iconClass);
|
|
||||||
const icon = this.getLightIconPath(this.iconPath);
|
|
||||||
const iconDark = this.getDarkIconPath(this.iconPath) || icon;
|
|
||||||
createCSSRule(`.icon.${this._iconClass}`, `background-image: url("${icon}")`);
|
|
||||||
createCSSRule(`.vs-dark .icon.${this._iconClass}, .hc-black .icon.${this._iconClass}`, `background-image: url("${iconDark}")`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getLightIconPath(iconPath: IUserFriendlyIcon): string {
|
|
||||||
if (iconPath && iconPath['light']) {
|
|
||||||
return this.getIconPath(iconPath['light']);
|
|
||||||
} else {
|
|
||||||
return this.getIconPath(<string | URI>iconPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getDarkIconPath(iconPath: IUserFriendlyIcon): string {
|
|
||||||
if (iconPath && iconPath['dark']) {
|
|
||||||
return this.getIconPath(iconPath['dark']);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getIconPath(iconPath: string | URI): string {
|
|
||||||
if (typeof iconPath === 'string') {
|
|
||||||
return URI.file(iconPath).toString();
|
|
||||||
} else {
|
|
||||||
let uri = URI.revive(iconPath);
|
|
||||||
return uri.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getIconWidth(): string {
|
|
||||||
return this.convertSize(this.iconWidth, '40px');
|
|
||||||
}
|
|
||||||
|
|
||||||
public getIconHeight(): string {
|
|
||||||
return this.convertSize(this.iconHeight, '40px');
|
|
||||||
}
|
|
||||||
|
|
||||||
public get iconPath(): string | URI | { light: string | URI; dark: string | URI } {
|
|
||||||
return this.getPropertyOrDefault<sqlops.ComponentWithIcon, IUserFriendlyIcon>((props) => props.iconPath, undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get iconHeight(): number | string {
|
|
||||||
return this.getPropertyOrDefault<sqlops.ComponentWithIcon, number | string>((props) => props.iconHeight, '40px');
|
|
||||||
}
|
|
||||||
|
|
||||||
public get iconWidth(): number | string {
|
|
||||||
return this.getPropertyOrDefault<sqlops.ComponentWithIcon, number | string>((props) => props.iconWidth, '40px');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
if (this._iconClass) {
|
|
||||||
removeCSSRulesContainingSelector(this._iconClass);
|
|
||||||
}
|
|
||||||
super.ngOnDestroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,9 +32,9 @@
|
|||||||
border: 1px solid gray;
|
border: 1px solid gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.declarative-table [role="gridcell"]:focus,
|
[role="gridcell"]:focus,
|
||||||
.declarative-table [role="gridcell"] *:focus,
|
[role="gridcell"] *:focus,
|
||||||
.declarative-table [role="grid"] [tabindex="0"]:focus {
|
[role="grid"] [tabindex="0"]:focus {
|
||||||
outline: #005a9c;
|
outline: #005a9c;
|
||||||
outline-style: dotted;
|
outline-style: dotted;
|
||||||
outline-width: 3px;
|
outline-width: 3px;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Component, Input, Inject, ChangeDetectorRef, forwardRef,
|
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||||
ViewChild, ElementRef, OnDestroy, AfterViewInit
|
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList, AfterViewInit
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
@@ -14,11 +14,14 @@ import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
|||||||
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown';
|
import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/dropdown';
|
||||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
||||||
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
|
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
|
||||||
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||||
|
|
||||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'modelview-dropdown',
|
selector: 'modelview-dropdown',
|
||||||
@@ -57,8 +60,7 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
|||||||
strictSelection: false,
|
strictSelection: false,
|
||||||
placeholder: '',
|
placeholder: '',
|
||||||
maxHeight: 125,
|
maxHeight: 125,
|
||||||
ariaLabel: '',
|
ariaLabel: ''
|
||||||
actionLabel: ''
|
|
||||||
};
|
};
|
||||||
this._editableDropdown = new Dropdown(this._editableDropDownContainer.nativeElement, this.contextViewService, this.themeService,
|
this._editableDropdown = new Dropdown(this._editableDropDownContainer.nativeElement, this.contextViewService, this.themeService,
|
||||||
dropdownOptions);
|
dropdownOptions);
|
||||||
@@ -143,22 +145,18 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getSelectedValue(): string {
|
private getSelectedValue(): string {
|
||||||
if (this.values && this.values.length > 0 && this.valuesHaveDisplayName()) {
|
if (this.values && this.valuesHaveDisplayName()) {
|
||||||
let selectedValue = <sqlops.CategoryValue>this.value || <sqlops.CategoryValue>this.values[0];
|
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.name === this.value);
|
||||||
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.name === selectedValue.name);
|
|
||||||
return valueCategory && valueCategory.displayName;
|
return valueCategory && valueCategory.displayName;
|
||||||
} else {
|
} else {
|
||||||
if (!this.value && this.values && this.values.length > 0) {
|
return this.value;
|
||||||
return <string>this.values[0];
|
|
||||||
}
|
|
||||||
return <string>this.value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private setSelectedValue(newValue: string): void {
|
private setSelectedValue(newValue: string): void {
|
||||||
if (this.values && this.valuesHaveDisplayName()) {
|
if (this.values && this.valuesHaveDisplayName()) {
|
||||||
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.displayName === newValue);
|
let valueCategory = (<sqlops.CategoryValue[]>this.values).find(v => v.displayName === newValue);
|
||||||
this.value = valueCategory;
|
this.value = valueCategory && valueCategory.name;
|
||||||
} else {
|
} else {
|
||||||
this.value = newValue;
|
this.value = newValue;
|
||||||
}
|
}
|
||||||
@@ -166,8 +164,8 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
|||||||
|
|
||||||
// CSS-bound properties
|
// CSS-bound properties
|
||||||
|
|
||||||
private get value(): string | sqlops.CategoryValue {
|
private get value(): string {
|
||||||
return this.getPropertyOrDefault<sqlops.DropDownProperties, string | sqlops.CategoryValue>((props) => props.value, '');
|
return this.getPropertyOrDefault<sqlops.DropDownProperties, string>((props) => props.value, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
private get editable(): boolean {
|
private get editable(): boolean {
|
||||||
@@ -182,8 +180,8 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
|||||||
return !this.editable ? '' : 'none';
|
return !this.editable ? '' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
private set value(newValue: string | sqlops.CategoryValue) {
|
private set value(newValue: string) {
|
||||||
this.setPropertyFromUI<sqlops.DropDownProperties, string | sqlops.CategoryValue>(this.setValueProperties, newValue);
|
this.setPropertyFromUI<sqlops.DropDownProperties, string>(this.setValueProperties, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get values(): string[] | sqlops.CategoryValue[] {
|
private get values(): string[] | sqlops.CategoryValue[] {
|
||||||
@@ -194,11 +192,11 @@ export default class DropDownComponent extends ComponentBase implements ICompone
|
|||||||
this.setPropertyFromUI<sqlops.DropDownProperties, string[] | sqlops.CategoryValue[]>(this.setValuesProperties, newValue);
|
this.setPropertyFromUI<sqlops.DropDownProperties, string[] | sqlops.CategoryValue[]>(this.setValuesProperties, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setValueProperties(properties: sqlops.DropDownProperties, value: string | sqlops.CategoryValue): void {
|
private setValueProperties(properties: sqlops.DropDownProperties, value: string): void {
|
||||||
properties.value = value;
|
properties.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setValuesProperties(properties: sqlops.DropDownProperties, values: string[] | sqlops.CategoryValue[]): void {
|
private setValuesProperties(properties: sqlops.DropDownProperties, values: string[]): void {
|
||||||
properties.values = values;
|
properties.values = values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class FlexItem {
|
|||||||
template: `
|
template: `
|
||||||
<div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent"
|
<div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent"
|
||||||
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height" [style.width]="width">
|
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height" [style.width]="width">
|
||||||
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.textAlign]="textAlign" [style.order]="getItemOrder(item)" >
|
<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 [descriptor]="item.descriptor" [modelStore]="modelStore">
|
||||||
</model-component-wrapper>
|
</model-component-wrapper>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,7 +40,6 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
|
|||||||
private _justifyContent: string;
|
private _justifyContent: string;
|
||||||
private _alignItems: string;
|
private _alignItems: string;
|
||||||
private _alignContent: string;
|
private _alignContent: string;
|
||||||
private _textAlign: string;
|
|
||||||
private _height: string;
|
private _height: string;
|
||||||
private _width: string;
|
private _width: string;
|
||||||
|
|
||||||
@@ -66,7 +65,6 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
|
|||||||
this._justifyContent = layout.justifyContent ? layout.justifyContent : '';
|
this._justifyContent = layout.justifyContent ? layout.justifyContent : '';
|
||||||
this._alignItems = layout.alignItems ? layout.alignItems : '';
|
this._alignItems = layout.alignItems ? layout.alignItems : '';
|
||||||
this._alignContent = layout.alignContent ? layout.alignContent : '';
|
this._alignContent = layout.alignContent ? layout.alignContent : '';
|
||||||
this._textAlign = layout.textAlign ? layout.textAlign : '';
|
|
||||||
this._height = this.convertSize(layout.height);
|
this._height = this.convertSize(layout.height);
|
||||||
this._width = this.convertSize(layout.width);
|
this._width = this.convertSize(layout.width);
|
||||||
|
|
||||||
@@ -98,10 +96,6 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
|
|||||||
return this._alignContent;
|
return this._alignContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get textAlign(): string {
|
|
||||||
return this._textAlign;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getItemFlex(item: FlexItem): string {
|
private getItemFlex(item: FlexItem): string {
|
||||||
return item.config ? item.config.flex : '1 1 auto';
|
return item.config ? item.config.flex : '1 1 auto';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import 'vs/css!./formLayout';
|
import 'vs/css!./formLayout';
|
||||||
import 'vs/css!sql/media/icons/common-icons';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||||
@@ -17,18 +16,13 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
|
|||||||
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
|
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { getContentHeight, getContentWidth, Dimension } from 'vs/base/browser/dom';
|
|
||||||
|
|
||||||
export interface TitledFormItemLayout {
|
export interface TitledFormItemLayout {
|
||||||
title: string;
|
title: string;
|
||||||
actions?: string[];
|
actions?: string[];
|
||||||
isFormComponent: Boolean;
|
isFormComponent: Boolean;
|
||||||
horizontal: boolean;
|
horizontal: boolean;
|
||||||
componentWidth?: number | string;
|
componentWidth: number;
|
||||||
componentHeight?: number | string;
|
|
||||||
titleFontSize?: number | string;
|
|
||||||
required?: boolean;
|
|
||||||
info?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormLayout {
|
export interface FormLayout {
|
||||||
@@ -41,15 +35,12 @@ class FormItem {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<div #container *ngIf="items" class="form-table" [style.padding]="getFormPadding()" [style.width]="getFormWidth()" [style.height]="getFormHeight()">
|
<div #container *ngIf="items" class="form-table" [style.width]="getFormWidth()" [style.height]="getFormHeight()">
|
||||||
<ng-container *ngFor="let item of items">
|
<ng-container *ngFor="let item of items">
|
||||||
<div class="form-row" *ngIf="isFormComponent(item)" [style.height]="getRowHeight(item)">
|
<div class="form-row" *ngIf="isFormComponent(item)">
|
||||||
|
|
||||||
<ng-container *ngIf="isHorizontal(item)">
|
<ng-container *ngIf="isHorizontal(item)">
|
||||||
<div class="form-cell" [style.font-size]="getItemTitleFontSize(item)">
|
<div class="form-cell">{{getItemTitle(item)}}</div>
|
||||||
{{getItemTitle(item)}}<span class="form-required" *ngIf="isItemRequired(item)">*</span>
|
|
||||||
<span class="icon info form-info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span>
|
|
||||||
</div>
|
|
||||||
<div class="form-cell">
|
<div class="form-cell">
|
||||||
<div class="form-component-container">
|
<div class="form-component-container">
|
||||||
<div [style.width]="getComponentWidth(item)" [ngClass]="{'form-input-flex': !getComponentWidth(item)}">
|
<div [style.width]="getComponentWidth(item)" [ngClass]="{'form-input-flex': !getComponentWidth(item)}">
|
||||||
@@ -65,13 +56,10 @@ class FormItem {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<div class="form-vertical-container" *ngIf="isVertical(item)" [style.height]="getRowHeight(item)">
|
<div class="form-vertical-container" *ngIf="isVertical(item)">
|
||||||
<div class="form-item-row" [style.font-size]="getItemTitleFontSize(item)">
|
<div class="form-item-row">{{getItemTitle(item)}}</div>
|
||||||
{{getItemTitle(item)}}<span class="form-required" *ngIf="isItemRequired(item)">*</span>
|
<div class="form-item-row" [style.width]="getComponentWidth(item)">
|
||||||
<span class="icon info form-info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span>
|
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)">
|
||||||
</div>
|
|
||||||
<div class="form-item-row" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
|
|
||||||
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
|
|
||||||
</model-component-wrapper>
|
</model-component-wrapper>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="itemHasActions(item)" class="form-item-row form-actions-table form-item-last-row">
|
<div *ngIf="itemHasActions(item)" class="form-item-row form-actions-table form-item-last-row">
|
||||||
@@ -113,10 +101,6 @@ export default class FormContainer extends ContainerBase<FormItemLayout> impleme
|
|||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
public layout(): void {
|
|
||||||
super.layout();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// IComponent implementation
|
/// IComponent implementation
|
||||||
|
|
||||||
public get alignItems(): string {
|
public get alignItems(): string {
|
||||||
@@ -128,53 +112,23 @@ export default class FormContainer extends ContainerBase<FormItemLayout> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getFormWidth(): string {
|
private getFormWidth(): string {
|
||||||
return this.convertSize(this._formLayout && this._formLayout.width, '');
|
return this.convertSize(this._formLayout && this._formLayout.width);
|
||||||
}
|
|
||||||
|
|
||||||
private getFormPadding(): string {
|
|
||||||
return this._formLayout && this._formLayout.padding ? this._formLayout.padding : '10px 30px 0px 30px';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFormHeight(): string {
|
private getFormHeight(): string {
|
||||||
return this.convertSize(this._formLayout && this._formLayout.height, '');
|
return this.convertSize(this._formLayout && this._formLayout.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getComponentWidth(item: FormItem): string {
|
private getComponentWidth(item: FormItem): string {
|
||||||
let itemConfig = item.config;
|
let itemConfig = item.config;
|
||||||
return (itemConfig && itemConfig.componentWidth) ? this.convertSize(itemConfig.componentWidth, '') : '';
|
return (itemConfig && itemConfig.componentWidth) ? itemConfig.componentWidth + 'px' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRowHeight(item: FormItem): string {
|
|
||||||
let itemConfig = item.config;
|
|
||||||
return (itemConfig && itemConfig.componentHeight) ? this.convertSize(itemConfig.componentHeight, '') : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
private isItemRequired(item: FormItem): boolean {
|
|
||||||
let itemConfig = item.config;
|
|
||||||
return itemConfig && itemConfig.required;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getItemInfo(item: FormItem): string {
|
|
||||||
let itemConfig = item.config;
|
|
||||||
return itemConfig && itemConfig.info;
|
|
||||||
}
|
|
||||||
|
|
||||||
private itemHasInfo(item: FormItem): boolean {
|
|
||||||
let itemConfig = item.config;
|
|
||||||
return itemConfig && itemConfig.info !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private getItemTitle(item: FormItem): string {
|
private getItemTitle(item: FormItem): string {
|
||||||
let itemConfig = item.config;
|
let itemConfig = item.config;
|
||||||
return itemConfig ? itemConfig.title : '';
|
return itemConfig ? itemConfig.title : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
private getItemTitleFontSize(item: FormItem): string {
|
|
||||||
let itemConfig = item.config;
|
|
||||||
return itemConfig && itemConfig.titleFontSize ? this.convertSize(itemConfig.titleFontSize, '11px') : '11px';
|
|
||||||
}
|
|
||||||
|
|
||||||
private getActionComponents(item: FormItem): FormItem[] {
|
private getActionComponents(item: FormItem): FormItem[] {
|
||||||
let items = this.items;
|
let items = this.items;
|
||||||
let itemConfig = item.config;
|
let itemConfig = item.config;
|
||||||
|
|||||||
@@ -37,16 +37,6 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-required {
|
|
||||||
color: red;
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-info {
|
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-component-actions {
|
.form-component-actions {
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
|
|||||||