Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9baee1c22c | ||
|
|
8cb67b4f9d | ||
|
|
0cd47bc328 | ||
|
|
07fb58d5e1 | ||
|
|
2d80d5e611 | ||
|
|
4bd63b615b | ||
|
|
335f667507 | ||
|
|
1819036d7d | ||
|
|
4f76f116ac | ||
|
|
1eba7c7d2a | ||
|
|
83234dd52c | ||
|
|
bae23b7fce | ||
|
|
3db61eaa82 | ||
|
|
5cf85a0361 | ||
|
|
ffe27f5bde | ||
|
|
78bcd9d54c | ||
|
|
1a9797f0ff | ||
|
|
0de94ff8a4 | ||
|
|
472233d9a7 | ||
|
|
f69e31b0d5 | ||
|
|
60b696cc31 | ||
|
|
549037f744 | ||
|
|
ca5e1e6133 | ||
|
|
3e3ff163db |
38
extensions/agent/client/src/agentUtils.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
|
||||||
|
export class AgentUtils {
|
||||||
|
|
||||||
|
private static _agentService: sqlops.AgentServicesProvider;
|
||||||
|
private static _connectionService: sqlops.ConnectionProvider;
|
||||||
|
private static _queryProvider: sqlops.QueryProvider;
|
||||||
|
|
||||||
|
public static async getAgentService(): Promise<sqlops.AgentServicesProvider> {
|
||||||
|
if (!AgentUtils._agentService) {
|
||||||
|
let currentConnection = await sqlops.connection.getCurrentConnection();
|
||||||
|
this._agentService = sqlops.dataprotocol.getProvider<sqlops.AgentServicesProvider>(currentConnection.providerName, sqlops.DataProviderType.AgentServicesProvider);
|
||||||
|
}
|
||||||
|
return AgentUtils._agentService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async getDatabases(ownerUri: string): Promise<string[]> {
|
||||||
|
if (!AgentUtils._connectionService) {
|
||||||
|
let currentConnection = await sqlops.connection.getCurrentConnection();
|
||||||
|
this._connectionService = sqlops.dataprotocol.getProvider<sqlops.ConnectionProvider>(currentConnection.providerName, sqlops.DataProviderType.ConnectionProvider);
|
||||||
|
}
|
||||||
|
return AgentUtils._connectionService.listDatabases(ownerUri).then(result => {
|
||||||
|
if (result && result.databaseNames && result.databaseNames.length > 0) {
|
||||||
|
return result.databaseNames;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async getQueryProvider(): Promise<sqlops.QueryProvider> {
|
||||||
|
if (!AgentUtils._queryProvider) {
|
||||||
|
let currentConnection = await sqlops.connection.getCurrentConnection();
|
||||||
|
this._queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(currentConnection.providerName, sqlops.DataProviderType.QueryProvider);
|
||||||
|
}
|
||||||
|
return this._queryProvider;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
extensions/agent/client/src/data/createAlertData.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
150
extensions/agent/client/src/data/createJobData.ts
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
extensions/agent/client/src/data/createScheduleData.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
71
extensions/agent/client/src/data/createStepData.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
29
extensions/agent/client/src/data/pickScheduleData.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
149
extensions/agent/client/src/dialogs/createAlertDialog.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
464
extensions/agent/client/src/dialogs/createJobDialog.ts
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
91
extensions/agent/client/src/dialogs/createScheduleDialog.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
421
extensions/agent/client/src/dialogs/createStepDialog.ts
Normal file
@@ -0,0 +1,421 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
92
extensions/agent/client/src/dialogs/pickScheduleDialog.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { PickScheduleData } from '../data/pickScheduleData';
|
||||||
|
|
||||||
|
export class PickScheduleDialog {
|
||||||
|
|
||||||
|
// TODO: localize
|
||||||
|
// Top level
|
||||||
|
private readonly DialogTitle: string = 'Job Schedules';
|
||||||
|
private readonly OkButtonText: string = 'OK';
|
||||||
|
private readonly CancelButtonText: string = 'Cancel';
|
||||||
|
private readonly SchedulesTabText: string = 'Schedules';
|
||||||
|
|
||||||
|
// UI Components
|
||||||
|
private dialog: sqlops.window.modelviewdialog.Dialog;
|
||||||
|
private schedulesTable: sqlops.TableComponent;
|
||||||
|
|
||||||
|
private model: PickScheduleData;
|
||||||
|
|
||||||
|
private _onSuccess: vscode.EventEmitter<PickScheduleData> = new vscode.EventEmitter<PickScheduleData>();
|
||||||
|
public readonly onSuccess: vscode.Event<PickScheduleData> = this._onSuccess.event;
|
||||||
|
|
||||||
|
constructor(ownerUri: string) {
|
||||||
|
this.model = new PickScheduleData(ownerUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async showDialog() {
|
||||||
|
await this.model.initialize();
|
||||||
|
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
|
||||||
|
this.initializeContent();
|
||||||
|
this.dialog.okButton.onClick(async () => await this.execute());
|
||||||
|
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
||||||
|
this.dialog.okButton.label = this.OkButtonText;
|
||||||
|
this.dialog.cancelButton.label = this.CancelButtonText;
|
||||||
|
|
||||||
|
sqlops.window.modelviewdialog.openDialog(this.dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeContent() {
|
||||||
|
this.dialog.registerContent(async view => {
|
||||||
|
this.schedulesTable = view.modelBuilder.table()
|
||||||
|
.withProperties({
|
||||||
|
columns: [
|
||||||
|
'Schedule Name'
|
||||||
|
],
|
||||||
|
data: [],
|
||||||
|
height: 600,
|
||||||
|
width: 400
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
let formModel = view.modelBuilder.formContainer()
|
||||||
|
.withFormItems([{
|
||||||
|
component: this.schedulesTable,
|
||||||
|
title: 'Schedules'
|
||||||
|
}]).withLayout({ width: '100%' }).component();
|
||||||
|
|
||||||
|
await view.initializeModel(formModel);
|
||||||
|
|
||||||
|
if (this.model.schedules) {
|
||||||
|
let data: any[][] = [];
|
||||||
|
for (let i = 0; i < this.model.schedules.length; ++i) {
|
||||||
|
let schedule = this.model.schedules[i];
|
||||||
|
data[i] = [ schedule.name ];
|
||||||
|
}
|
||||||
|
this.schedulesTable.data = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async execute() {
|
||||||
|
this.updateModel();
|
||||||
|
await this.model.save();
|
||||||
|
this._onSuccess.fire(this.model);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async cancel() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateModel() {
|
||||||
|
let selectedRows = this.schedulesTable.selectedRows;
|
||||||
|
if (selectedRows && selectedRows.length > 0) {
|
||||||
|
let selectedRow = selectedRows[0];
|
||||||
|
this.model.selectedSchedule = this.model.schedules[selectedRow];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,12 +6,10 @@
|
|||||||
|
|
||||||
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) {
|
||||||
let apiWrapper = new ApiWrapper();
|
controller = new MainController(context);
|
||||||
controller = new MainController(context, apiWrapper);
|
|
||||||
controller.activate();
|
controller.activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,24 +3,47 @@
|
|||||||
* 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 * as data from 'sqlops';
|
import { CreateAlertDialog } from './dialogs/createAlertDialog';
|
||||||
import { ApiWrapper } from './apiWrapper';
|
import { CreateJobDialog } from './dialogs/createJobDialog';
|
||||||
|
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, apiWrapper?: ApiWrapper) {
|
public constructor(context: vscode.ExtensionContext) {
|
||||||
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) => {
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,11 +51,4 @@ 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,4 +5,5 @@
|
|||||||
|
|
||||||
/// <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
Normal file
@@ -9,15 +9,12 @@
|
|||||||
"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"
|
||||||
@@ -29,22 +26,25 @@
|
|||||||
"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 Visual Studio Code.
|
**Notice** This is a an extension that is bundled with SQL Operations Studio.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||||
"version": "1.4.0-alpha.46",
|
"version": "1.5.0-alpha.2",
|
||||||
"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.engineEditionId)) {
|
if (e.profile.providerName.toLowerCase() === 'mssql' && !types.isUndefinedOrNull(e.serverInfo) && !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,6 +64,10 @@ 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;
|
||||||
@@ -144,6 +148,27 @@ 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');
|
||||||
@@ -169,6 +194,10 @@ 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');
|
||||||
@@ -233,4 +262,21 @@ 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,6 +135,20 @@ 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 = {
|
||||||
@@ -365,6 +379,67 @@ 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,
|
||||||
@@ -373,6 +448,7 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
createJob,
|
createJob,
|
||||||
updateJob,
|
updateJob,
|
||||||
deleteJob,
|
deleteJob,
|
||||||
|
getJobDefaults,
|
||||||
createJobStep,
|
createJobStep,
|
||||||
updateJobStep,
|
updateJobStep,
|
||||||
deleteJobStep,
|
deleteJobStep,
|
||||||
@@ -387,7 +463,11 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
getProxies,
|
getProxies,
|
||||||
createProxy,
|
createProxy,
|
||||||
updateProxy,
|
updateProxy,
|
||||||
deleteProxy
|
deleteProxy,
|
||||||
|
getJobSchedules,
|
||||||
|
createJobSchedule,
|
||||||
|
updateJobSchedule,
|
||||||
|
deleteJobSchedule
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,16 @@
|
|||||||
"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": [
|
||||||
|
|||||||
@@ -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.20",
|
"slickgrid": "github:anthonydresser/SlickGrid#2.3.22",
|
||||||
"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",
|
||||||
|
|||||||
@@ -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": "openDialog"
|
"title": "sqlservices.openDialog"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "sqlservices.openEditor",
|
"command": "sqlservices.openEditor",
|
||||||
@@ -32,6 +32,10 @@
|
|||||||
{
|
{
|
||||||
"command": "sqlservices.openEditorWithWebView2",
|
"command": "sqlservices.openEditorWithWebView2",
|
||||||
"title": "sqlservices.openEditorWithWebView2"
|
"title": "sqlservices.openEditorWithWebView2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlservices.openWizard",
|
||||||
|
"title": "sqlservices.openWizard"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dashboard.tabs": [
|
"dashboard.tabs": [
|
||||||
|
|||||||
@@ -60,9 +60,212 @@ 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');
|
||||||
@@ -80,207 +283,29 @@ 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) => {
|
||||||
let inputBox = view.modelBuilder.inputBox()
|
await this.getTabContent(view, customButton1, customButton2, 400);
|
||||||
.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 => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
import { InputBox as vsInputBox, IInputOptions, IInputBoxStyles as vsIInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
|
import { InputBox as vsInputBox, IInputOptions, IInputBoxStyles as vsIInputBoxStyles, IMessage } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
import { 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,6 +33,7 @@ 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);
|
||||||
@@ -103,4 +104,21 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -216,4 +216,19 @@
|
|||||||
|
|
||||||
.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;
|
||||||
}
|
}
|
||||||
@@ -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.getName(), '', input, queryResultsInput, undefined);
|
let queryInput: QueryInput = instantiationService.createInstance(QueryInput, '', 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,6 +21,8 @@ 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';
|
||||||
|
|
||||||
|
|||||||
@@ -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, IDashboardContainer, registerContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
|
import { IDashboardContainerRegistry, Extensions as DashboardContainerExtensions } 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,13 +136,17 @@ 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;
|
||||||
let edition = connectionInfo.serverInfo.engineEditionId;
|
if (connectionInfo.serverInfo) {
|
||||||
return config.map((item) => {
|
let edition = connectionInfo.serverInfo.engineEditionId;
|
||||||
if (item.edition === undefined) {
|
return config.map((item) => {
|
||||||
item.edition = edition;
|
if (item.edition === undefined) {
|
||||||
}
|
item.edition = edition;
|
||||||
return item;
|
}
|
||||||
});
|
return item;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -162,9 +166,11 @@ export function addContext(config: WidgetConfig[], collection: any, context: str
|
|||||||
* Returns a filtered version of the widgets passed based on edition and provider
|
* 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 { when?: string }, K extends { contextKeyService: IContextKeyService }>(config: T[], collection: K): Array<T> {
|
export function filterConfigs<T extends { provider?: string | string[], when?: string }, K extends { contextKeyService: IContextKeyService }>(config: T[], collection: K): Array<T> {
|
||||||
return config.filter((item) => {
|
return config.filter((item) => {
|
||||||
if (!item.when) {
|
if (!hasCompatibleProvider(item.provider, collection.contextKeyService)) {
|
||||||
|
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));
|
||||||
@@ -172,6 +178,21 @@ export function filterConfigs<T extends { when?: string }, K extends { contextKe
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the listed providers contain '*' indicating any provider will do, or that they are a match
|
||||||
|
* for the currently scoped 'connectionProvider' context key.
|
||||||
|
*/
|
||||||
|
function hasCompatibleProvider(provider: string | string[], contextKeyService: IContextKeyService): boolean {
|
||||||
|
let isCompatible = true;
|
||||||
|
let connectionProvider = contextKeyService.getContextKeyValue<string>(Constants.connectionProviderContextKey);
|
||||||
|
if (connectionProvider) {
|
||||||
|
let providers = (provider instanceof Array) ? provider : [provider];
|
||||||
|
let matchingProvider = providers.find((p) => p === connectionProvider || p === Constants.anyProviderName);
|
||||||
|
isCompatible = (matchingProvider !== undefined);
|
||||||
|
} // Else there's no connection context so skip the check
|
||||||
|
return isCompatible;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get registered container if it is specified as the key
|
* Get registered container if it is specified as the key
|
||||||
* @param container dashboard container
|
* @param container dashboard container
|
||||||
|
|||||||
@@ -17,11 +17,12 @@ 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 } from 'sql/parts/dashboard/common/interfaces';
|
import { DashboardTab, IConfigModifierCollection } from 'sql/parts/dashboard/common/interfaces';
|
||||||
import * as dashboardHelper from 'sql/parts/dashboard/common/dashboardHelper';
|
import * 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';
|
||||||
@@ -38,16 +39,11 @@ 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 {
|
export abstract class DashboardPage extends AngularDisposable implements IConfigModifierCollection {
|
||||||
|
|
||||||
protected tabs: Array<TabConfig> = [];
|
protected tabs: Array<TabConfig> = [];
|
||||||
|
|
||||||
@@ -137,22 +133,11 @@ export abstract class DashboardPage extends AngularDisposable {
|
|||||||
this._tabsDispose.forEach(i => i.dispose());
|
this._tabsDispose.forEach(i => i.dispose());
|
||||||
this._tabsDispose = [];
|
this._tabsDispose = [];
|
||||||
|
|
||||||
// Create home tab
|
|
||||||
let homeTab: TabConfig = {
|
|
||||||
id: 'homeTab',
|
|
||||||
publisher: undefined,
|
|
||||||
title: this.homeTabTitle,
|
|
||||||
container: { 'widgets-container': homeWidgets },
|
|
||||||
context: this.context,
|
|
||||||
originalConfig: this._originalConfig,
|
|
||||||
editable: true,
|
|
||||||
canClose: false,
|
|
||||||
actions: []
|
|
||||||
};
|
|
||||||
this.addNewTab(homeTab);
|
|
||||||
|
|
||||||
let allTabs = dashboardHelper.filterConfigs(dashboardRegistry.tabs, this);
|
let allTabs = dashboardHelper.filterConfigs(dashboardRegistry.tabs, this);
|
||||||
|
|
||||||
|
// Before separating tabs into pinned / shown, ensure that the home tab is always set up as expected
|
||||||
|
allTabs = this.setAndRemoveHomeTab(allTabs, homeWidgets);
|
||||||
|
|
||||||
// Load tab setting configs
|
// 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('.'));
|
||||||
|
|
||||||
@@ -199,6 +184,32 @@ export abstract class DashboardPage extends AngularDisposable {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setAndRemoveHomeTab(allTabs: IDashboardTab[], homeWidgets: WidgetConfig[]): IDashboardTab[] {
|
||||||
|
let homeTabConfig: TabConfig = {
|
||||||
|
id: 'homeTab',
|
||||||
|
provider: Constants.anyProviderName,
|
||||||
|
publisher: undefined,
|
||||||
|
title: this.homeTabTitle,
|
||||||
|
container: { 'widgets-container': homeWidgets },
|
||||||
|
context: this.context,
|
||||||
|
originalConfig: this._originalConfig,
|
||||||
|
editable: true,
|
||||||
|
canClose: false,
|
||||||
|
actions: []
|
||||||
|
};
|
||||||
|
|
||||||
|
let homeTabIndex = allTabs.findIndex((tab) => tab.isHomeTab === true);
|
||||||
|
if (homeTabIndex !== undefined && homeTabIndex > -1) {
|
||||||
|
// Have a tab: get its information and copy over to the home tab definition
|
||||||
|
let homeTab = allTabs.splice(homeTabIndex, 1)[0];
|
||||||
|
let tabConfig = this.initTabComponents(homeTab);
|
||||||
|
homeTabConfig.id = tabConfig.id;
|
||||||
|
homeTabConfig.container = tabConfig.container;
|
||||||
|
}
|
||||||
|
this.addNewTab(homeTabConfig);
|
||||||
|
return allTabs;
|
||||||
|
}
|
||||||
|
|
||||||
private rewriteConfig(): void {
|
private rewriteConfig(): void {
|
||||||
let writeableConfig = objects.deepClone(this._tabSettingConfigs);
|
let writeableConfig = objects.deepClone(this._tabSettingConfigs);
|
||||||
|
|
||||||
@@ -208,35 +219,12 @@ export abstract class DashboardPage extends AngularDisposable {
|
|||||||
|
|
||||||
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 => {
|
let selectedTabs = dashboardTabs.map(v => this.initTabComponents(v)).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 tabConfig = this._tabSettingConfigs.find(i => i.tabId === v.id);
|
let tabSettingConfig = this._tabSettingConfigs.find(i => i.tabId === v.id);
|
||||||
let isPinned = false;
|
let isPinned = false;
|
||||||
if (tabConfig) {
|
if (tabSettingConfig) {
|
||||||
isPinned = tabConfig.isPinned;
|
isPinned = tabSettingConfig.isPinned;
|
||||||
} else if (v.alwaysShow) {
|
} else if (v.alwaysShow) {
|
||||||
isPinned = true;
|
isPinned = true;
|
||||||
}
|
}
|
||||||
@@ -261,6 +249,30 @@ export abstract class DashboardPage extends AngularDisposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private initTabComponents(value: IDashboardTab): { id: string; title: string; container: object; alwaysShow: boolean; } {
|
||||||
|
let containerResult = dashboardHelper.getDashboardContainer(value.container);
|
||||||
|
if (!containerResult.result) {
|
||||||
|
return { id: value.id, title: value.title, container: { 'error-container': undefined }, alwaysShow: value.alwaysShow };
|
||||||
|
}
|
||||||
|
let key = Object.keys(containerResult.container)[0];
|
||||||
|
if (key === WIDGETS_CONTAINER || key === GRID_CONTAINER) {
|
||||||
|
let configs = <WidgetConfig[]>Object.values(containerResult.container)[0];
|
||||||
|
this._configModifiers.forEach(cb => {
|
||||||
|
configs = cb.apply(this, [configs, this, this.context]);
|
||||||
|
});
|
||||||
|
this._gridModifiers.forEach(cb => {
|
||||||
|
configs = cb.apply(this, [configs]);
|
||||||
|
});
|
||||||
|
if (key === WIDGETS_CONTAINER) {
|
||||||
|
return { id: value.id, title: value.title, container: { 'widgets-container': configs }, alwaysShow: value.alwaysShow };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return { id: value.id, title: value.title, container: { 'grid-container': configs }, alwaysShow: value.alwaysShow };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { id: value.id, title: value.title, container: containerResult.container, alwaysShow: value.alwaysShow };
|
||||||
|
}
|
||||||
|
|
||||||
private getContentType(tab: TabConfig): string {
|
private getContentType(tab: TabConfig): string {
|
||||||
return tab.container ? Object.keys(tab.container)[0] : '';
|
return tab.container ? Object.keys(tab.container)[0] : '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,11 @@
|
|||||||
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 { TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_ACTIVE_BORDER, TAB_INACTIVE_BACKGROUND, TAB_INACTIVE_FOREGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND, TAB_BORDER } from 'vs/workbench/common/theme';
|
import {
|
||||||
import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
|
TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_ACTIVE_BORDER, TAB_INACTIVE_BACKGROUND,
|
||||||
|
TAB_INACTIVE_FOREGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND, TAB_BORDER, EDITOR_GROUP_BORDER
|
||||||
|
} from 'vs/workbench/common/theme';
|
||||||
|
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||||
|
|
||||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||||
|
|
||||||
@@ -98,4 +101,14 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const divider = theme.getColor(EDITOR_GROUP_BORDER);
|
||||||
|
if (divider) {
|
||||||
|
collector.addRule(`
|
||||||
|
panel.dashboard-panel > .tabbedPanel > .title .tabList .tab-header {
|
||||||
|
border-right-width: 1px;
|
||||||
|
border-right-style: solid;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
@@ -7,6 +7,7 @@ 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';
|
||||||
@@ -17,9 +18,11 @@ 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 = {
|
||||||
@@ -41,6 +44,11 @@ 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',
|
||||||
@@ -49,6 +57,10 @@ 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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -67,7 +79,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, title, when, id, alwaysShow } = tab;
|
let { description, container, provider, title, when, id, alwaysShow, isHomeTab } = tab;
|
||||||
|
|
||||||
// If always show is not specified, set it to true by default.
|
// If always show is not specified, set it to true by default.
|
||||||
if (!types.isBoolean(alwaysShow)) {
|
if (!types.isBoolean(alwaysShow)) {
|
||||||
@@ -88,6 +100,13 @@ 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;
|
||||||
@@ -110,7 +129,7 @@ ExtensionsRegistry.registerExtensionPoint<IDashboardTabContrib | IDashboardTabCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
registerTab({ description, title, container, when, id, alwaysShow, publisher });
|
registerTab({ description, title, container, provider, when, id, alwaysShow, publisher, isHomeTab });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ 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',
|
||||||
@@ -50,3 +52,8 @@ export abstract class DashboardTab extends TabChild implements OnDestroy {
|
|||||||
this.dispose();
|
this.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IConfigModifierCollection {
|
||||||
|
connectionManagementService: SingleConnectionManagementService;
|
||||||
|
contextKeyService: IContextKeyService;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,28 +5,27 @@
|
|||||||
|
|
||||||
import 'vs/css!./dashboardNavSection';
|
import 'vs/css!./dashboardNavSection';
|
||||||
|
|
||||||
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, EventEmitter, OnChanges, AfterContentInit } from '@angular/core';
|
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, OnChanges, AfterContentInit } from '@angular/core';
|
||||||
|
|
||||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
import { CommonServiceInterface, SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.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 { TabComponent, TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||||
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
import { DashboardTab, IConfigModifierCollection } 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 {
|
export class DashboardNavSection extends DashboardTab implements OnDestroy, OnChanges, AfterContentInit, IConfigModifierCollection {
|
||||||
@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>();
|
||||||
@@ -38,7 +37,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>, dashboardServer: DashboardServiceInterface, context: string) => Array<WidgetConfig>> = [
|
private readonly _configModifiers: Array<(item: Array<WidgetConfig>, collection: IConfigModifierCollection, context: string) => Array<WidgetConfig>> = [
|
||||||
dashboardHelper.removeEmpty,
|
dashboardHelper.removeEmpty,
|
||||||
dashboardHelper.initExtensionConfigs,
|
dashboardHelper.initExtensionConfigs,
|
||||||
dashboardHelper.addProvider,
|
dashboardHelper.addProvider,
|
||||||
@@ -103,7 +102,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.dashboardService, this.tab.context]);
|
configs = cb.apply(this, [configs, this, this.tab.context]);
|
||||||
});
|
});
|
||||||
this._gridModifiers.forEach(cb => {
|
this._gridModifiers.forEach(cb => {
|
||||||
configs = cb.apply(this, [configs]);
|
configs = cb.apply(this, [configs]);
|
||||||
@@ -169,4 +168,12 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get connectionManagementService(): SingleConnectionManagementService {
|
||||||
|
return this.dashboardService.connectionManagementService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get contextKeyService(): IContextKeyService {
|
||||||
|
return this.dashboardService.scopedContextKeyService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ 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';
|
||||||
@@ -59,9 +62,8 @@ 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, DashboardModelViewContainer, ModelComponentWrapper, Checkbox,
|
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, AlertsViewComponent, ProxiesViewComponent, OperatorsViewComponent,
|
||||||
SelectBox,
|
DashboardModelViewContainer, ModelComponentWrapper, Checkbox, SelectBox, InputBox,];
|
||||||
InputBox,];
|
|
||||||
|
|
||||||
/* Panel */
|
/* Panel */
|
||||||
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
||||||
|
|||||||
@@ -96,16 +96,12 @@ let defaultVal = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
widget: {
|
widget: {
|
||||||
'backup-history-server-insight': {
|
'backup-history-server-insight': null
|
||||||
cacheId: '0c7cba8b-c87a-4bcc-ae54-2f40a5503a90'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
widget: {
|
widget: {
|
||||||
'all-database-size-server-insight': {
|
'all-database-size-server-insight': null
|
||||||
cacheId: '1d7cba8b-c87a-4bcc-ae54-2f40a5503a90'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -121,8 +117,7 @@ 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';
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ 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',
|
||||||
@@ -120,6 +121,8 @@ 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,7 +166,6 @@ 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,17 +4,42 @@
|
|||||||
* 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>
|
||||||
</panel>
|
<tab [title]="alertsComponentTitle" class="fullsize" [identifier]="alertsTabIdentifier"
|
||||||
|
[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,6 +33,10 @@ 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;
|
||||||
@@ -40,6 +44,9 @@ 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,7 +7,6 @@
|
|||||||
|
|
||||||
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';
|
||||||
@@ -21,6 +20,12 @@ 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,11 +7,8 @@
|
|||||||
|
|
||||||
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';
|
||||||
|
|
||||||
|
|
||||||
@@ -22,8 +19,7 @@ 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
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +29,25 @@ 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);
|
||||||
@@ -79,7 +94,8 @@ 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>;
|
||||||
@@ -89,7 +105,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,12 +125,16 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +146,10 @@ 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
src/sql/parts/jobManagement/common/media/alert.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{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>
|
||||||
|
After Width: | Height: | Size: 847 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{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>
|
||||||
|
After Width: | Height: | Size: 852 B |
@@ -31,7 +31,7 @@ jobhistory-component {
|
|||||||
border-bottom: 3px solid #444444;
|
border-bottom: 3px solid #444444;
|
||||||
}
|
}
|
||||||
|
|
||||||
#jobsDiv .jobview-grid {
|
.jobview-grid {
|
||||||
height: 94.7%;
|
height: 94.7%;
|
||||||
width : 100%;
|
width : 100%;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -49,6 +49,18 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -97,7 +109,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: 200px;
|
width: 250px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -107,18 +119,14 @@ jobhistory-component {
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.error-row {
|
.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.error-row {
|
.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,6 +190,30 @@ 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;
|
||||||
@@ -219,8 +251,8 @@ agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.od
|
|||||||
background: #444444 !important;
|
background: #444444 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.jobprevruns div.bar1, table.jobprevruns div.bar2, table.jobprevruns div.bar3,
|
table.jobprevruns div.bar0, table.jobprevruns div.bar1, table.jobprevruns div.bar2,
|
||||||
table.jobprevruns div.bar4, table.jobprevruns div.bar5 {
|
table.jobprevruns div.bar3, table.jobprevruns div.bar4, table.jobprevruns div.bar5 {
|
||||||
padding-top: 3px;
|
padding-top: 3px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
@@ -232,10 +264,54 @@ table.jobprevruns div.bar4, table.jobprevruns div.bar5 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
src/sql/parts/jobManagement/common/media/new.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{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>
|
||||||
|
After Width: | Height: | Size: 336 B |
1
src/sql/parts/jobManagement/common/media/new_inverse.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{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>
|
||||||
|
After Width: | Height: | Size: 320 B |
1
src/sql/parts/jobManagement/common/media/operator.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<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>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{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>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
1
src/sql/parts/jobManagement/common/media/proxy.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{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>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{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>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
17
src/sql/parts/jobManagement/views/alertsView.component.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!--
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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>
|
||||||
160
src/sql/parts/jobManagement/views/alertsView.component.ts
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,11 +10,15 @@ 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 JobHistoryActions {
|
export enum JobActions {
|
||||||
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");
|
||||||
@@ -30,7 +34,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, JobHistoryActions.Run).then(result => {
|
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.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({
|
||||||
@@ -65,7 +69,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, JobHistoryActions.Stop).then(result => {
|
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.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({
|
||||||
@@ -83,4 +87,30 @@ 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));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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 style="min-width: 275px">
|
<div class="prev-run-list-container" style="min-width: 275px; height: 75vh">
|
||||||
<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 { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||||
import { RunJobAction, StopJobAction } from 'sql/parts/jobManagement/views/jobHistoryActions';
|
import { RunJobAction, StopJobAction, NewStepAction } from 'sql/parts/jobManagement/views/jobActions';
|
||||||
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: AgentJobInfo = undefined;
|
@Input() public agentJobInfo: sqlops.AgentJobInfo = undefined;
|
||||||
@Input() public agentJobHistories: AgentJobHistoryInfo[] = undefined;
|
@Input() public agentJobHistories: sqlops.AgentJobHistoryInfo[] = undefined;
|
||||||
public agentJobHistoryInfo: AgentJobHistoryInfo = undefined;
|
public agentJobHistoryInfo: sqlops.AgentJobHistoryInfo = undefined;
|
||||||
|
|
||||||
private _isVisible: boolean = false;
|
private _isVisible: boolean = false;
|
||||||
private _stepRows: JobStepsViewRow[] = [];
|
private _stepRows: JobStepsViewRow[] = [];
|
||||||
@@ -56,8 +56,12 @@ 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: AgentJobInfo;
|
private _agentJobInfo: sqlops.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,
|
||||||
@@ -76,14 +80,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;
|
||||||
let serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||||
let jobCache = jobCacheObjectMap[serverName];
|
let jobCache = jobCacheObjectMap[this._serverName];
|
||||||
if (jobCache) {
|
if (jobCache) {
|
||||||
this._jobCacheObject = jobCache;
|
this._jobCacheObject = jobCache;
|
||||||
} else {
|
} else {
|
||||||
this._jobCacheObject = new JobCacheObject();
|
this._jobCacheObject = new JobCacheObject();
|
||||||
this._jobCacheObject.serverName = serverName;
|
this._jobCacheObject.serverName = this._serverName;
|
||||||
this._jobManagementService.addToCache(serverName, this._jobCacheObject);
|
this._jobManagementService.addToCache(this._serverName, this._jobCacheObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,8 +128,17 @@ 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(1024);
|
this._tree.layout(JobHistoryComponent.INITIAL_TREE_HEIGHT);
|
||||||
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() {
|
||||||
@@ -210,7 +223,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildHistoryTree(self: any, jobHistories: AgentJobHistoryInfo[]) {
|
private buildHistoryTree(self: any, jobHistories: sqlops.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));
|
||||||
@@ -237,7 +250,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
this._agentViewComponent.showHistory = false;
|
this._agentViewComponent.showHistory = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertToJobHistoryRow(historyInfo: AgentJobHistoryInfo): JobHistoryRow {
|
private convertToJobHistoryRow(historyInfo: sqlops.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);
|
||||||
@@ -263,12 +276,14 @@ 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 }
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,6 +301,10 @@ 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,6 +122,15 @@ 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;
|
||||||
@@ -221,10 +230,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,5 +9,9 @@
|
|||||||
<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,16 +44,28 @@ 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.lastRun','Last Run'), field: 'lastRun', width: 120, id: 'lastRun' },
|
name: nls.localize('jobColumns.name', 'Name'),
|
||||||
{ name: nls.localize('jobColumns.nextRun','Next Run'), field: 'nextRun', width: 120, id: 'nextRun' },
|
field: 'name',
|
||||||
{ name: nls.localize('jobColumns.enabled','Enabled'), field: 'enabled', width: 50, id: 'enabled' },
|
formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext),
|
||||||
{ name: nls.localize('jobColumns.status','Status'), field: 'currentExecutionStatus', width: 60, id: 'currentExecutionStatus' },
|
width: 150,
|
||||||
{ name: nls.localize('jobColumns.category','Category'), field: 'category', width: 120, id: 'category' },
|
id: 'name'
|
||||||
{ 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.lastRun', 'Last Run'), field: 'lastRun', width: 80, id: 'lastRun' },
|
||||||
{ name: nls.localize('jobColumns.lastRunOutcome', 'Last Run Outcome'), field: 'lastRunOutcome', width: 120, id: 'lastRunOutcome' },
|
{ name: nls.localize('jobColumns.nextRun', 'Next Run'), field: 'nextRun', width: 80, id: 'nextRun' },
|
||||||
{ name: nls.localize('jobColumns.previousRuns', 'Previous Runs'), formatter: this.renderChartsPostHistory, field: 'previousRuns', width: 80, id: 'previousRuns'}
|
{ name: nls.localize('jobColumns.enabled', 'Enabled'), field: 'enabled', width: 60, id: 'enabled' },
|
||||||
|
{ 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'
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
@@ -79,19 +91,22 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
private _serverName: string;
|
private _serverName: string;
|
||||||
private _isCloud: boolean;
|
private _isCloud: boolean;
|
||||||
private _showProgressWheel: boolean;
|
private _showProgressWheel: boolean;
|
||||||
private _tabHeight: number;
|
private filterStylingMap: { [columnName: string]: [any]; } = {};
|
||||||
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;
|
||||||
@@ -130,13 +145,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,9 +256,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]);
|
||||||
}
|
}
|
||||||
@@ -251,7 +266,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);
|
||||||
@@ -261,7 +276,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++;
|
||||||
@@ -277,7 +292,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])) {
|
||||||
@@ -291,7 +306,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++;
|
||||||
@@ -331,21 +346,20 @@ 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 currentTab = $('agentview-component #jobsDiv .jobview-grid').get(0);
|
let jobsViewToolbar = $('jobsview-component .jobs-view-toolbar').get(0);
|
||||||
if (currentTab) {
|
let statusBar = $('.part.statusbar').get(0);
|
||||||
let currentTabHeight = currentTab.clientHeight;
|
if (jobsViewToolbar && statusBar) {
|
||||||
if (currentTabHeight < self._tabHeight) {
|
let toolbarBottom = jobsViewToolbar.getBoundingClientRect().bottom;
|
||||||
$('agentview-component #jobsDiv div.ui-widget').css('height', `${currentTabHeight - 22}px`);
|
let statusTop = statusBar.getBoundingClientRect().top;
|
||||||
self._table.resizeCanvas();
|
$('agentview-component #jobsDiv .jobview-grid').css('height', statusTop - toolbarBottom);
|
||||||
} else {
|
self._table.resizeCanvas();
|
||||||
$('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) => {
|
||||||
@@ -357,49 +371,17 @@ 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((e) => {
|
$('#jobsDiv .jobview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e1) =>
|
||||||
// highlight the error row as well if a failing job row is hovered
|
this.highlightErrorRows(e1), (e2) => this.hightlightNonErrorRows(e2));
|
||||||
if (e.currentTarget.children.item(0).classList.contains('job-with-error')) {
|
|
||||||
let target = $(e.currentTarget);
|
this._table.grid.onScroll.subscribe((e) => {
|
||||||
let targetChildren = $(e.currentTarget.children);
|
$('#jobsDiv .jobview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e1) =>
|
||||||
let siblings = target.nextAll().toArray();
|
this.highlightErrorRows(e1), (e2) => this.hightlightNonErrorRows(e2));
|
||||||
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;
|
||||||
@@ -407,6 +389,47 @@ 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,
|
||||||
@@ -427,27 +450,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) {
|
||||||
@@ -477,14 +500,28 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderChartsPostHistory(row, cell, value, columnDef, dataContext) {
|
private renderChartsPostHistory(row, cell, value, columnDef, dataContext) {
|
||||||
return `<table class="jobprevruns" id="${dataContext.id}">
|
let runChart = this._jobCacheObject.getRunChart(dataContext.id);
|
||||||
|
if (runChart && runChart.length > 0) {
|
||||||
|
return `<table class="jobprevruns" id="${dataContext.id}">
|
||||||
<tr>
|
<tr>
|
||||||
<td><div class="bar1"></div></td>
|
<td>${runChart[0] ? runChart[0] : '<div></div>' }</td>
|
||||||
<td><div class="bar2"></div></td>
|
<td>${runChart[1] ? runChart[1] : '<div></div>' }</td>
|
||||||
<td><div class="bar3"></div></td>
|
<td>${runChart[2] ? runChart[2] : '<div></div>' }</td>
|
||||||
<td><div class="bar4"></div></td>
|
<td>${runChart[3] ? runChart[3] : '<div></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 {
|
||||||
@@ -557,13 +594,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);
|
||||||
@@ -575,8 +612,9 @@ 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+1}`);
|
let runGraph = $(`table#${jobId}.jobprevruns > tbody > tr > td > div.bar${i}`);
|
||||||
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';
|
||||||
@@ -585,6 +623,9 @@ 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');
|
||||||
@@ -594,16 +635,19 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -613,7 +657,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;
|
||||||
@@ -625,14 +669,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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -648,7 +692,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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -661,8 +705,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) => {
|
||||||
@@ -670,13 +714,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);
|
||||||
@@ -737,7 +781,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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -751,12 +795,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);
|
||||||
@@ -784,4 +828,13 @@ 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<!--
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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>
|
||||||
157
src/sql/parts/jobManagement/views/operatorsView.component.ts
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import '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;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/sql/parts/jobManagement/views/proxiesView.component.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!--
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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>
|
||||||
155
src/sql/parts/jobManagement/views/proxiesView.component.ts
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -207,7 +207,9 @@ export abstract class ContainerBase<T> extends ComponentBase {
|
|||||||
) {
|
) {
|
||||||
super(_changeRef);
|
super(_changeRef);
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this._validations.push(() => this.items.every(item => this.modelStore.getComponent(item.descriptor.id).valid));
|
this._validations.push(() => this.items.every(item => {
|
||||||
|
return this.modelStore.getComponent(item.descriptor.id).valid;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// IComponent container-related implementation
|
/// IComponent container-related implementation
|
||||||
|
|||||||
@@ -32,9 +32,9 @@
|
|||||||
border: 1px solid gray;
|
border: 1px solid gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
[role="gridcell"]:focus,
|
.declarative-table [role="gridcell"]:focus,
|
||||||
[role="gridcell"] *:focus,
|
.declarative-table [role="gridcell"] *:focus,
|
||||||
[role="grid"] [tabindex="0"]:focus {
|
.declarative-table [role="grid"] [tabindex="0"]:focus {
|
||||||
outline: #005a9c;
|
outline: #005a9c;
|
||||||
outline-style: dotted;
|
outline-style: dotted;
|
||||||
outline-width: 3px;
|
outline-width: 3px;
|
||||||
|
|||||||
@@ -18,9 +18,8 @@ import { IInputOptions, MessageType } from 'vs/base/browser/ui/inputbox/inputBox
|
|||||||
import { attachInputBoxStyler, attachListStyler } from 'vs/platform/theme/common/styler';
|
import { attachInputBoxStyler, attachListStyler } 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 * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import { TextAreaInput } from 'vs/editor/browser/controller/textAreaInput';
|
import { inputBackground, inputBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'modelview-inputBox',
|
selector: 'modelview-inputBox',
|
||||||
@@ -70,13 +69,13 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
|||||||
if (this._inputContainer) {
|
if (this._inputContainer) {
|
||||||
this._input = new InputBox(this._inputContainer.nativeElement, this.contextViewService, inputOptions);
|
this._input = new InputBox(this._inputContainer.nativeElement, this.contextViewService, inputOptions);
|
||||||
this.registerInput(this._input, () => !this.multiline);
|
this.registerInput(this._input, () => !this.multiline);
|
||||||
|
|
||||||
}
|
}
|
||||||
if (this._textareaContainer) {
|
if (this._textareaContainer) {
|
||||||
let textAreaInputOptions = Object.assign({}, inputOptions, { flexibleHeight: true, type: 'textarea' });
|
let textAreaInputOptions = Object.assign({}, inputOptions, { flexibleHeight: true, type: 'textarea' });
|
||||||
this._textAreaInput = new InputBox(this._textareaContainer.nativeElement, this.contextViewService, textAreaInputOptions);
|
this._textAreaInput = new InputBox(this._textareaContainer.nativeElement, this.contextViewService, textAreaInputOptions);
|
||||||
this.registerInput(this._textAreaInput, () => this.multiline);
|
this.registerInput(this._textAreaInput, () => this.multiline);
|
||||||
}
|
}
|
||||||
|
this.inputElement.hideErrors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get inputElement(): InputBox {
|
private get inputElement(): InputBox {
|
||||||
@@ -88,10 +87,17 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
|
|||||||
this._validations.push(() => !input.inputElement.validationMessage);
|
this._validations.push(() => !input.inputElement.validationMessage);
|
||||||
|
|
||||||
this._register(input);
|
this._register(input);
|
||||||
this._register(attachInputBoxStyler(input, this.themeService));
|
this._register(attachInputBoxStyler(input, this.themeService, {
|
||||||
this._register(input.onDidChange(e => {
|
inputValidationInfoBackground: inputBackground,
|
||||||
|
inputValidationInfoBorder: inputBorder,
|
||||||
|
}));
|
||||||
|
this._register(input.onDidChange(async e => {
|
||||||
if (checkOption()) {
|
if (checkOption()) {
|
||||||
this.value = input.value;
|
this.value = input.value;
|
||||||
|
await this.validate();
|
||||||
|
if (input.hideErrors) {
|
||||||
|
input.hideErrors = false;
|
||||||
|
}
|
||||||
this._onEventEmitter.fire({
|
this._onEventEmitter.fire({
|
||||||
eventType: ComponentEventType.onDidChange,
|
eventType: ComponentEventType.onDidChange,
|
||||||
args: e
|
args: e
|
||||||
|
|||||||
@@ -94,8 +94,8 @@ export class ModelComponentWrapper extends AngularDisposable implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public layout(): void {
|
public layout(): void {
|
||||||
if (this._componentInstance && this._componentInstance.layout) {
|
if (this.componentInstance && this.componentInstance.layout) {
|
||||||
this._componentInstance.layout();
|
this.componentInstance.layout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +110,12 @@ export class ModelComponentWrapper extends AngularDisposable implements OnInit {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get componentInstance(): IComponent {
|
||||||
|
if (!this._componentInstance) {
|
||||||
|
this.loadComponent();
|
||||||
|
}
|
||||||
|
return this._componentInstance;
|
||||||
|
}
|
||||||
|
|
||||||
private loadComponent(): void {
|
private loadComponent(): void {
|
||||||
if (!this.descriptor || !this.descriptor.type) {
|
if (!this.descriptor || !this.descriptor.type) {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export class ModelViewInput extends EditorInput {
|
|||||||
|
|
||||||
private createDialogPane(): void {
|
private createDialogPane(): void {
|
||||||
this._dialogPaneContainer = DOM.$('div.model-view-container');
|
this._dialogPaneContainer = DOM.$('div.model-view-container');
|
||||||
this._dialogPane = new DialogPane(this.title, this.modelViewId, () => undefined, this._instantiationService);
|
this._dialogPane = new DialogPane(this.title, this.modelViewId, () => undefined, this._instantiationService, false);
|
||||||
this._dialogPane.createBody(this._dialogPaneContainer);
|
this._dialogPane.createBody(this._dialogPaneContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import * as sqlops from 'sqlops';
|
|||||||
selector: 'modelview-content',
|
selector: 'modelview-content',
|
||||||
template: `
|
template: `
|
||||||
<div *ngIf="rootDescriptor" style="width: 100%; height: 100%;">
|
<div *ngIf="rootDescriptor" style="width: 100%; height: 100%;">
|
||||||
<model-component-wrapper [descriptor]="rootDescriptor" [modelStore]="modelStore">
|
<model-component-wrapper style="display: block; height: 100%" [descriptor]="rootDescriptor" [modelStore]="modelStore">
|
||||||
</model-component-wrapper>
|
</model-component-wrapper>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import nls = require('vs/nls');
|
|||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { IModelStore, IComponentDescriptor, IComponent, IComponentEventArgs } from './interfaces';
|
import { IModelStore, IComponentDescriptor, IComponent, IComponentEventArgs } from './interfaces';
|
||||||
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
import { IModelView } from 'sql/services/model/modelViewService';
|
import { IModelView, IModelViewEventArgs } from 'sql/services/model/modelViewService';
|
||||||
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||||
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||||
import { ModelStore } from 'sql/parts/modelComponents/modelStore';
|
import { ModelStore } from 'sql/parts/modelComponents/modelStore';
|
||||||
@@ -38,7 +38,7 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
abstract id: string;
|
abstract id: string;
|
||||||
abstract connection: sqlops.connection.Connection;
|
abstract connection: sqlops.connection.Connection;
|
||||||
abstract serverInfo: sqlops.ServerInfo;
|
abstract serverInfo: sqlops.ServerInfo;
|
||||||
private _onEventEmitter = new Emitter<any>();
|
private _onEventEmitter = new Emitter<IModelViewEventArgs>();
|
||||||
|
|
||||||
initializeModel(rootComponent: IComponentShape, validationCallback: (componentId: string) => Thenable<boolean>): void {
|
initializeModel(rootComponent: IComponentShape, validationCallback: (componentId: string) => Thenable<boolean>): void {
|
||||||
let descriptor = this.defineComponent(rootComponent);
|
let descriptor = this.defineComponent(rootComponent);
|
||||||
@@ -106,13 +106,16 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
registerEvent(componentId: string) {
|
registerEvent(componentId: string) {
|
||||||
this.queueAction(componentId, (component) => {
|
this.queueAction(componentId, (component) => {
|
||||||
this._register(component.registerEventHandler(e => {
|
this._register(component.registerEventHandler(e => {
|
||||||
e.componentId = componentId;
|
let modelViewEvent: IModelViewEventArgs = Object.assign({
|
||||||
this._onEventEmitter.fire(e);
|
componentId: componentId,
|
||||||
|
isRootComponent: componentId === this.rootDescriptor.id
|
||||||
|
}, e);
|
||||||
|
this._onEventEmitter.fire(modelViewEvent);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get onEvent(): Event<IComponentEventArgs> {
|
public get onEvent(): Event<IModelViewEventArgs> {
|
||||||
return this._onEventEmitter.event;
|
return this._onEventEmitter.event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,11 +96,11 @@ export class TreeNode {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
var currentNode: TreeNode = this;
|
var currentNode: TreeNode = this;
|
||||||
while (currentNode.nodeTypeId !== NodeType.Database && currentNode.nodeTypeId !== NodeType.Server) {
|
while (currentNode.nodeTypeId !== NodeType.Database && currentNode.nodeTypeId !== NodeType.Server && currentNode.parent) {
|
||||||
currentNode = currentNode.parent;
|
currentNode = currentNode.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentNode.nodeTypeId === NodeType.Database) {
|
if (currentNode && currentNode.nodeTypeId === NodeType.Database) {
|
||||||
return currentNode.metadata ? currentNode.metadata.name : null;
|
return currentNode.metadata ? currentNode.metadata.name : null;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ export class TreeUpdateUtils {
|
|||||||
connectedConnection.options['groupId'] = connection.groupId;
|
connectedConnection.options['groupId'] = connection.groupId;
|
||||||
connectedConnection.options['databaseDisplayName'] = connection.databaseName;
|
connectedConnection.options['databaseDisplayName'] = connection.databaseName;
|
||||||
|
|
||||||
var rootNode: TreeNode = objectExplorerService.getObjectExplorerNode(connectedConnection);
|
let rootNode: TreeNode = objectExplorerService.getObjectExplorerNode(connectedConnection);
|
||||||
if (!rootNode) {
|
if (!rootNode) {
|
||||||
objectExplorerService.updateObjectExplorerNodes(connectedConnection).then(() => {
|
objectExplorerService.updateObjectExplorerNodes(connectedConnection).then(() => {
|
||||||
rootNode = objectExplorerService.getObjectExplorerNode(connectedConnection);
|
rootNode = objectExplorerService.getObjectExplorerNode(connectedConnection);
|
||||||
@@ -207,7 +207,7 @@ export class TreeUpdateUtils {
|
|||||||
if (connection.isDisconnecting) {
|
if (connection.isDisconnecting) {
|
||||||
resolve([]);
|
resolve([]);
|
||||||
} else {
|
} else {
|
||||||
var rootNode = objectExplorerService.getObjectExplorerNode(connection);
|
let rootNode = objectExplorerService.getObjectExplorerNode(connection);
|
||||||
if (rootNode) {
|
if (rootNode) {
|
||||||
objectExplorerService.resolveTreeNodeChildren(rootNode.getSession(), rootNode).then(() => {
|
objectExplorerService.resolveTreeNodeChildren(rootNode.getSession(), rootNode).then(() => {
|
||||||
resolve(rootNode.children);
|
resolve(rootNode.children);
|
||||||
@@ -226,7 +226,7 @@ export class TreeUpdateUtils {
|
|||||||
if (objectExplorerNode && objectExplorerNode.parent) {
|
if (objectExplorerNode && objectExplorerNode.parent) {
|
||||||
// if object explorer node's parent is root, return connection profile
|
// if object explorer node's parent is root, return connection profile
|
||||||
if (!objectExplorerNode.parent.parent) {
|
if (!objectExplorerNode.parent.parent) {
|
||||||
var connectionId = objectExplorerNode.getConnectionProfile().id;
|
let connectionId = objectExplorerNode.getConnectionProfile().id;
|
||||||
|
|
||||||
// get connection profile from connection profile groups
|
// get connection profile from connection profile groups
|
||||||
let root = TreeUpdateUtils.getTreeInput(connectionManagementService);
|
let root = TreeUpdateUtils.getTreeInput(connectionManagementService);
|
||||||
@@ -268,8 +268,8 @@ export class TreeUpdateUtils {
|
|||||||
* Get connection profile with the current database
|
* Get connection profile with the current database
|
||||||
*/
|
*/
|
||||||
public static getConnectionProfile(treeNode: TreeNode): ConnectionProfile {
|
public static getConnectionProfile(treeNode: TreeNode): ConnectionProfile {
|
||||||
var connectionProfile = treeNode.getConnectionProfile();
|
let connectionProfile = treeNode.getConnectionProfile();
|
||||||
var databaseName = treeNode.getDatabaseName();
|
let databaseName = treeNode.getDatabaseName();
|
||||||
if (databaseName !== undefined && connectionProfile.databaseName !== databaseName) {
|
if (databaseName !== undefined && connectionProfile.databaseName !== databaseName) {
|
||||||
connectionProfile = connectionProfile.cloneWithDatabase(databaseName);
|
connectionProfile = connectionProfile.cloneWithDatabase(databaseName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ import { IObjectExplorerService } from '../../objectExplorer/common/objectExplor
|
|||||||
import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
|
import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
|
||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
||||||
|
import { IProfilerService} from '../service/interfaces';
|
||||||
|
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||||
|
import { KeyCode, KeyMod } from 'vs/editor/editor.api';
|
||||||
|
import { ProfilerEditor } from '../editor/profilerEditor';
|
||||||
|
|
||||||
// Contribute Global Actions
|
// Contribute Global Actions
|
||||||
const category = nls.localize('profilerCategory', "Profiler");
|
const category = nls.localize('profilerCategory', "Profiler");
|
||||||
@@ -46,3 +50,42 @@ CommandsRegistry.registerCommand({
|
|||||||
return editorService.openEditor(profilerInput, { pinned: true }, false).then(() => TPromise.as(true));
|
return editorService.openEditor(profilerInput, { pinned: true }, false).then(() => TPromise.as(true));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||||
|
id: 'profiler.newProfiler',
|
||||||
|
weight: KeybindingsRegistry.WEIGHT.builtinExtension(),
|
||||||
|
when: undefined,
|
||||||
|
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_P,
|
||||||
|
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_P },
|
||||||
|
handler: CommandsRegistry.getCommand('profiler.newProfiler').handler
|
||||||
|
});
|
||||||
|
|
||||||
|
CommandsRegistry.registerCommand({
|
||||||
|
id: 'profiler.start',
|
||||||
|
handler: (accessor: ServicesAccessor) => {
|
||||||
|
let profilerService: IProfilerService = accessor.get(IProfilerService);
|
||||||
|
let editorService: IWorkbenchEditorService = accessor.get(IWorkbenchEditorService);
|
||||||
|
|
||||||
|
let activeEditor = editorService.getActiveEditor();
|
||||||
|
if (activeEditor instanceof ProfilerEditor) {
|
||||||
|
let profilerInput = activeEditor.input;
|
||||||
|
return profilerService.startSession(profilerInput.id);
|
||||||
|
}
|
||||||
|
return TPromise.as(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CommandsRegistry.registerCommand({
|
||||||
|
id: 'profiler.stop',
|
||||||
|
handler: (accessor: ServicesAccessor) => {
|
||||||
|
let profilerService: IProfilerService = accessor.get(IProfilerService);
|
||||||
|
let editorService: IWorkbenchEditorService = accessor.get(IWorkbenchEditorService);
|
||||||
|
|
||||||
|
let activeEditor = editorService.getActiveEditor();
|
||||||
|
if (activeEditor instanceof ProfilerEditor) {
|
||||||
|
let profilerInput = activeEditor.input;
|
||||||
|
return profilerService.stopSession(profilerInput.id);
|
||||||
|
}
|
||||||
|
return TPromise.as(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -79,10 +79,7 @@ export class ProfilerStart extends Action {
|
|||||||
public run(input: ProfilerInput): TPromise<boolean> {
|
public run(input: ProfilerInput): TPromise<boolean> {
|
||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
input.data.clear();
|
input.data.clear();
|
||||||
return TPromise.wrap(this._profilerService.startSession(input.id).then(() => {
|
return TPromise.wrap(this._profilerService.startSession(input.id));
|
||||||
input.state.change({ isRunning: true, isStopped: false, isPaused: false });
|
|
||||||
return true;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,10 +128,7 @@ export class ProfilerStop extends Action {
|
|||||||
|
|
||||||
public run(input: ProfilerInput): TPromise<boolean> {
|
public run(input: ProfilerInput): TPromise<boolean> {
|
||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
return TPromise.wrap(this._profilerService.stopSession(input.id).then(() => {
|
return TPromise.wrap(this._profilerService.stopSession(input.id));
|
||||||
input.state.change({ isStopped: true, isPaused: false, isRunning: false });
|
|
||||||
return true;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,10 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onSessionStateChanged(state: ProfilerState) {
|
||||||
|
this.state.change(state);
|
||||||
|
}
|
||||||
|
|
||||||
public onMoreRows(eventMessage: sqlops.ProfilerSessionEvents) {
|
public onMoreRows(eventMessage: sqlops.ProfilerSessionEvents) {
|
||||||
if (eventMessage.eventsLost){
|
if (eventMessage.eventsLost){
|
||||||
this._notificationService.warn(nls.localize("profiler.eventsLost", "The XEvent Profiler session for {0} has lost events.", this._connection.serverName));
|
this._notificationService.warn(nls.localize("profiler.eventsLost", "The XEvent Profiler session for {0} has lost events.", this._connection.serverName));
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
|
|||||||
|
|
||||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
|
import { INewProfilerState } from '../editor/profilerState';
|
||||||
|
|
||||||
const PROFILER_SERVICE_ID = 'profilerService';
|
const PROFILER_SERVICE_ID = 'profilerService';
|
||||||
export const IProfilerService = createDecorator<IProfilerService>(PROFILER_SERVICE_ID);
|
export const IProfilerService = createDecorator<IProfilerService>(PROFILER_SERVICE_ID);
|
||||||
@@ -29,6 +30,10 @@ export interface IProfilerSession {
|
|||||||
* Called by the service when the session is closed unexpectedly
|
* Called by the service when the session is closed unexpectedly
|
||||||
*/
|
*/
|
||||||
onSessionStopped(events: sqlops.ProfilerSessionStoppedParams);
|
onSessionStopped(events: sqlops.ProfilerSessionStoppedParams);
|
||||||
|
/**
|
||||||
|
* Called by the service when the session state is changed
|
||||||
|
*/
|
||||||
|
onSessionStateChanged(newState: INewProfilerState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -96,7 +96,10 @@ export class ProfilerService implements IProfilerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public startSession(id: ProfilerSessionID): Thenable<boolean> {
|
public startSession(id: ProfilerSessionID): Thenable<boolean> {
|
||||||
return this._runAction(id, provider => provider.startSession(this._idMap.get(id)));
|
return this._runAction(id, provider => provider.startSession(this._idMap.get(id))).then(() => {
|
||||||
|
this._sessionMap.get(this._idMap.reverseGet(id)).onSessionStateChanged({ isRunning: true, isStopped: false, isPaused: false });
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public pauseSession(id: ProfilerSessionID): Thenable<boolean> {
|
public pauseSession(id: ProfilerSessionID): Thenable<boolean> {
|
||||||
@@ -104,7 +107,10 @@ export class ProfilerService implements IProfilerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public stopSession(id: ProfilerSessionID): Thenable<boolean> {
|
public stopSession(id: ProfilerSessionID): Thenable<boolean> {
|
||||||
return this._runAction(id, provider => provider.stopSession(this._idMap.get(id)));
|
return this._runAction(id, provider => provider.stopSession(this._idMap.get(id))).then(() => {
|
||||||
|
this._sessionMap.get(this._idMap.reverseGet(id)).onSessionStateChanged({ isStopped: true, isPaused: false, isRunning: false });
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _runAction<T>(id: ProfilerSessionID, action: (handler: sqlops.ProfilerProvider) => Thenable<T>): Thenable<T> {
|
private _runAction<T>(id: ProfilerSessionID, action: (handler: sqlops.ProfilerProvider) => Thenable<T>): Thenable<T> {
|
||||||
|
|||||||
@@ -306,6 +306,11 @@ let registryProperties = {
|
|||||||
'default': Constants.tabColorModeOff,
|
'default': Constants.tabColorModeOff,
|
||||||
'description': localize('tabColorMode', "Controls how to color tabs based on the server group of their active connection")
|
'description': localize('tabColorMode', "Controls how to color tabs based on the server group of their active connection")
|
||||||
},
|
},
|
||||||
|
'sql.showConnectionInfoInTitle': {
|
||||||
|
'type': 'boolean',
|
||||||
|
'description': localize('showConnectionInfoInTitle', "Controls whether to show the connection info for a tab in the title."),
|
||||||
|
'default': false
|
||||||
|
},
|
||||||
'mssql.intelliSense.enableIntelliSense': {
|
'mssql.intelliSense.enableIntelliSense': {
|
||||||
'type': 'boolean',
|
'type': 'boolean',
|
||||||
'default': true,
|
'default': true,
|
||||||
|
|||||||
@@ -4,17 +4,35 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
import { EditorInput, EditorModel, ConfirmResult, EncodingMode, IEncodingSupport } from 'vs/workbench/common/editor';
|
import { localize } from 'vs/nls';
|
||||||
import { IConnectionManagementService, IConnectableInput, INewConnectionParams, RunQueryOnConnectionMode } from 'sql/parts/connection/common/connectionManagement';
|
|
||||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
|
||||||
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
|
|
||||||
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
|
|
||||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import URI from 'vs/base/common/uri';
|
import URI from 'vs/base/common/uri';
|
||||||
import { ISelectionData, ExecutionPlanOptions } from 'sqlops';
|
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||||
|
import { EditorInput, EditorModel, ConfirmResult, EncodingMode, IEncodingSupport } from 'vs/workbench/common/editor';
|
||||||
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
|
|
||||||
|
import { IConnectionManagementService, IConnectableInput, INewConnectionParams, RunQueryOnConnectionMode } from 'sql/parts/connection/common/connectionManagement';
|
||||||
|
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
|
||||||
|
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
|
||||||
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
||||||
|
|
||||||
|
import { ISelectionData, ExecutionPlanOptions } from 'sqlops';
|
||||||
|
|
||||||
|
const MAX_SIZE = 13;
|
||||||
|
|
||||||
|
function trimTitle(title: string): string {
|
||||||
|
const length = title.length;
|
||||||
|
const diff = length - MAX_SIZE;
|
||||||
|
|
||||||
|
if (Math.sign(diff) <= 0) {
|
||||||
|
return title;
|
||||||
|
} else {
|
||||||
|
const start = (length / 2) - (diff / 2);
|
||||||
|
return title.slice(0, start) + '...' + title.slice(start + diff, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input for the QueryEditor. This input is simply a wrapper around a QueryResultsInput for the QueryResultsEditor
|
* Input for the QueryEditor. This input is simply a wrapper around a QueryResultsInput for the QueryResultsEditor
|
||||||
* and a UntitledEditorInput for the SQL File Editor.
|
* and a UntitledEditorInput for the SQL File Editor.
|
||||||
@@ -39,17 +57,16 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
|
|||||||
private _currentEventCallbacks: IDisposable[];
|
private _currentEventCallbacks: IDisposable[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _name: string,
|
|
||||||
private _description: string,
|
private _description: string,
|
||||||
private _sql: UntitledEditorInput,
|
private _sql: UntitledEditorInput,
|
||||||
private _results: QueryResultsInput,
|
private _results: QueryResultsInput,
|
||||||
private _connectionProviderName: string,
|
private _connectionProviderName: string,
|
||||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||||
@IQueryModelService private _queryModelService: IQueryModelService,
|
@IQueryModelService private _queryModelService: IQueryModelService,
|
||||||
@IQueryEditorService private _queryEditorService: IQueryEditorService
|
@IQueryEditorService private _queryEditorService: IQueryEditorService,
|
||||||
|
@IConfigurationService private _configurationService: IConfigurationService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
let self = this;
|
|
||||||
this._updateTaskbar = new Emitter<void>();
|
this._updateTaskbar = new Emitter<void>();
|
||||||
this._showQueryResultsEditor = new Emitter<void>();
|
this._showQueryResultsEditor = new Emitter<void>();
|
||||||
this._updateSelection = new Emitter<ISelectionData>();
|
this._updateSelection = new Emitter<ISelectionData>();
|
||||||
@@ -66,36 +83,44 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
|
|||||||
// Register callbacks for the Actions
|
// Register callbacks for the Actions
|
||||||
this._toDispose.push(
|
this._toDispose.push(
|
||||||
this._queryModelService.onRunQueryStart(uri => {
|
this._queryModelService.onRunQueryStart(uri => {
|
||||||
if (self.uri === uri) {
|
if (this.uri === uri) {
|
||||||
self.onRunQuery();
|
this.onRunQuery();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this._toDispose.push(
|
this._toDispose.push(
|
||||||
this._queryModelService.onRunQueryComplete(uri => {
|
this._queryModelService.onRunQueryComplete(uri => {
|
||||||
if (self.uri === uri) {
|
if (this.uri === uri) {
|
||||||
self.onQueryComplete();
|
this.onQueryComplete();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._connectionManagementService) {
|
if (this._connectionManagementService) {
|
||||||
this._toDispose.push(self._connectionManagementService.onDisconnect(result => {
|
this._toDispose.push(this._connectionManagementService.onDisconnect(result => {
|
||||||
if (result.connectionUri === self.uri) {
|
if (result.connectionUri === this.uri) {
|
||||||
self.onDisconnect();
|
this.onDisconnect();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
if (self.uri) {
|
if (this.uri) {
|
||||||
if (this._connectionProviderName) {
|
if (this._connectionProviderName) {
|
||||||
this._connectionManagementService.doChangeLanguageFlavor(self.uri, 'sql', this._connectionProviderName);
|
this._connectionManagementService.doChangeLanguageFlavor(this.uri, 'sql', this._connectionProviderName);
|
||||||
} else {
|
} else {
|
||||||
this._connectionManagementService.ensureDefaultLanguageFlavor(self.uri);
|
this._connectionManagementService.ensureDefaultLanguageFlavor(this.uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._configurationService) {
|
||||||
|
this._configurationService.onDidChangeConfiguration(e => {
|
||||||
|
if (e.affectedKeys.includes('sql.showConnectionInfoInTitle')) {
|
||||||
|
this._onDidChangeLabel.fire();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.onDisconnect();
|
this.onDisconnect();
|
||||||
this.onQueryComplete();
|
this.onQueryComplete();
|
||||||
}
|
}
|
||||||
@@ -136,11 +161,31 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
|
|||||||
public resolve(refresh?: boolean): TPromise<EditorModel> { return this._sql.resolve(); }
|
public resolve(refresh?: boolean): TPromise<EditorModel> { return this._sql.resolve(); }
|
||||||
public save(): TPromise<boolean> { return this._sql.save(); }
|
public save(): TPromise<boolean> { return this._sql.save(); }
|
||||||
public isDirty(): boolean { return this._sql.isDirty(); }
|
public isDirty(): boolean { return this._sql.isDirty(); }
|
||||||
public confirmSave(): TPromise<ConfirmResult> { return this._sql.confirmSave(); }
|
public confirmSave(): TPromise<ConfirmResult> { return this._sql.confirmSave(); }
|
||||||
public getResource(): URI { return this._sql.getResource(); }
|
public getResource(): URI { return this._sql.getResource(); }
|
||||||
public getEncoding(): string { return this._sql.getEncoding(); }
|
public getEncoding(): string { return this._sql.getEncoding(); }
|
||||||
public suggestFileName(): string { return this._sql.suggestFileName(); }
|
public suggestFileName(): string { return this._sql.suggestFileName(); }
|
||||||
public getName(): string { return this._sql.getName(); }
|
|
||||||
|
public getName(): string {
|
||||||
|
if (this._configurationService.getValue('sql.showConnectionInfoInTitle')) {
|
||||||
|
let profile = this._connectionManagementService.getConnectionProfile(this.uri);
|
||||||
|
let title = '';
|
||||||
|
if (profile) {
|
||||||
|
if (profile.userName) {
|
||||||
|
title = `${profile.serverName}.${profile.databaseName} (${profile.userName})`;
|
||||||
|
} else {
|
||||||
|
title = `${profile.serverName}.${profile.databaseName} (${profile.authenticationType})`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
title = localize('disconnected', 'disconnected');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._sql.getName() + ` - ${trimTitle(title)}`;
|
||||||
|
} else {
|
||||||
|
return this._sql.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public get hasAssociatedFilePath(): boolean { return this._sql.hasAssociatedFilePath; }
|
public get hasAssociatedFilePath(): boolean { return this._sql.hasAssociatedFilePath; }
|
||||||
|
|
||||||
public setEncoding(encoding: string, mode: EncodingMode /* ignored, we only have Encode */): void {
|
public setEncoding(encoding: string, mode: EncodingMode /* ignored, we only have Encode */): void {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export interface IQueryManagementService {
|
|||||||
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
||||||
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
||||||
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<sqlops.SimpleExecuteResult>;
|
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<sqlops.SimpleExecuteResult>;
|
||||||
|
parseSyntax(ownerUri:string, query: string): Thenable<sqlops.SyntaxParseResult>;
|
||||||
getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult>;
|
getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult>;
|
||||||
disposeQuery(ownerUri: string): Thenable<void>;
|
disposeQuery(ownerUri: string): Thenable<void>;
|
||||||
saveResults(requestParams: sqlops.SaveResultsRequestParams): Thenable<sqlops.SaveResultRequestResult>;
|
saveResults(requestParams: sqlops.SaveResultsRequestParams): Thenable<sqlops.SaveResultRequestResult>;
|
||||||
@@ -63,6 +64,7 @@ export interface IQueryRequestHandler {
|
|||||||
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
||||||
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
||||||
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<sqlops.SimpleExecuteResult>;
|
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<sqlops.SimpleExecuteResult>;
|
||||||
|
parseSyntax(ownerUri:string, query: string): Thenable<sqlops.SyntaxParseResult>;
|
||||||
getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult>;
|
getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult>;
|
||||||
disposeQuery(ownerUri: string): Thenable<void>;
|
disposeQuery(ownerUri: string): Thenable<void>;
|
||||||
saveResults(requestParams: sqlops.SaveResultsRequestParams): Thenable<sqlops.SaveResultRequestResult>;
|
saveResults(requestParams: sqlops.SaveResultsRequestParams): Thenable<sqlops.SaveResultRequestResult>;
|
||||||
@@ -197,6 +199,11 @@ export class QueryManagementService implements IQueryManagementService {
|
|||||||
return runner.runQueryAndReturn(ownerUri, queryString);
|
return runner.runQueryAndReturn(ownerUri, queryString);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public parseSyntax(ownerUri: string, query: string): Thenable<sqlops.SyntaxParseResult> {
|
||||||
|
return this._runAction(ownerUri, (runner) => {
|
||||||
|
return runner.parseSyntax(ownerUri, query);
|
||||||
|
});
|
||||||
|
}
|
||||||
public getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult> {
|
public getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult> {
|
||||||
return this._runAction(rowData.ownerUri, (runner) => {
|
return this._runAction(rowData.ownerUri, (runner) => {
|
||||||
return runner.getQueryRows(rowData);
|
return runner.getQueryRows(rowData);
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export class QueryEditorService implements IQueryEditorService {
|
|||||||
//input.resolve().then(model => this.backupFileService.backupResource(resource, model.getValue(), model.getVersionId())).done(null, errors.onUnexpectedError);
|
//input.resolve().then(model => this.backupFileService.backupResource(resource, model.getValue(), model.getVersionId())).done(null, errors.onUnexpectedError);
|
||||||
|
|
||||||
const queryResultsInput: QueryResultsInput = this._instantiationService.createInstance(QueryResultsInput, docUri.toString());
|
const queryResultsInput: QueryResultsInput = this._instantiationService.createInstance(QueryResultsInput, docUri.toString());
|
||||||
let queryInput: QueryInput = this._instantiationService.createInstance(QueryInput, fileInput.getName(), '', fileInput, queryResultsInput, connectionProviderName);
|
let queryInput: QueryInput = this._instantiationService.createInstance(QueryInput, '', fileInput, queryResultsInput, connectionProviderName);
|
||||||
|
|
||||||
this._editorService.openEditor(queryInput, { pinned: true })
|
this._editorService.openEditor(queryInput, { pinned: true })
|
||||||
.then((editor) => {
|
.then((editor) => {
|
||||||
@@ -113,7 +113,7 @@ export class QueryEditorService implements IQueryEditorService {
|
|||||||
const self = this;
|
const self = this;
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
let queryPlanInput: QueryPlanInput = self._instantiationService.createInstance(QueryPlanInput, xmlShowPlan, 'aaa', undefined);
|
let queryPlanInput: QueryPlanInput = self._instantiationService.createInstance(QueryPlanInput, xmlShowPlan, 'aaa', undefined);
|
||||||
self._editorService.openEditor(queryPlanInput, { pinned: true }, false);
|
self._editorService.openEditor(queryPlanInput, { pinned: true }, false);
|
||||||
resolve(true);
|
resolve(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -222,8 +222,7 @@ export class QueryEditorService implements IQueryEditorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let uri: URI = QueryEditorService._getEditorChangeUri(editor.input, changingToSql);
|
let uri: URI = QueryEditorService._getEditorChangeUri(editor.input, changingToSql);
|
||||||
if(uri.scheme === Schemas.untitled && (editor.input instanceof QueryInput || editor.input instanceof EditDataInput))
|
if (uri.scheme === Schemas.untitled && (editor.input instanceof QueryInput || editor.input instanceof EditDataInput)) {
|
||||||
{
|
|
||||||
QueryEditorService.notificationService.notify({
|
QueryEditorService.notificationService.notify({
|
||||||
severity: Severity.Error,
|
severity: Severity.Error,
|
||||||
message: QueryEditorService.CHANGE_UNSUPPORTED_ERROR_MESSAGE
|
message: QueryEditorService.CHANGE_UNSUPPORTED_ERROR_MESSAGE
|
||||||
@@ -258,15 +257,15 @@ export class QueryEditorService implements IQueryEditorService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close the current editor
|
// Close the current editor
|
||||||
QueryEditorService.editorService.closeEditor(position, editor.input).then(() => {
|
QueryEditorService.editorService.closeEditor(position, editor.input).then(() => {
|
||||||
|
|
||||||
// Reopen a new editor in the same position/index
|
// Reopen a new editor in the same position/index
|
||||||
QueryEditorService.editorService.openEditor(newEditorInput, options, position).then((editor) => {
|
QueryEditorService.editorService.openEditor(newEditorInput, options, position).then((editor) => {
|
||||||
resolve(QueryEditorService._onEditorOpened(editor, uri.toString(), position, options.pinned));
|
resolve(QueryEditorService._onEditorOpened(editor, uri.toString(), position, options.pinned));
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -331,10 +330,10 @@ export class QueryEditorService implements IQueryEditorService {
|
|||||||
let newEditorInput: IEditorInput = undefined;
|
let newEditorInput: IEditorInput = undefined;
|
||||||
if (changingToSql) {
|
if (changingToSql) {
|
||||||
const queryResultsInput: QueryResultsInput = QueryEditorService.instantiationService.createInstance(QueryResultsInput, uri.toString());
|
const queryResultsInput: QueryResultsInput = QueryEditorService.instantiationService.createInstance(QueryResultsInput, uri.toString());
|
||||||
let queryInput: QueryInput = QueryEditorService.instantiationService.createInstance(QueryInput, input.getName(), '', input, queryResultsInput, undefined);
|
let queryInput: QueryInput = QueryEditorService.instantiationService.createInstance(QueryInput, '', input, queryResultsInput, undefined);
|
||||||
newEditorInput = queryInput;
|
newEditorInput = queryInput;
|
||||||
} else {
|
} else {
|
||||||
let uriCopy: URI = URI.from( { scheme: uri.scheme, authority: uri.authority, path: uri.path, query: uri.query, fragment: uri.fragment } );
|
let uriCopy: URI = URI.from({ scheme: uri.scheme, authority: uri.authority, path: uri.path, query: uri.query, fragment: uri.fragment });
|
||||||
newEditorInput = QueryEditorService.instantiationService.createInstance(FileEditorInput, uriCopy, undefined);
|
newEditorInput = QueryEditorService.instantiationService.createInstance(FileEditorInput, uriCopy, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +349,7 @@ export class QueryEditorService implements IQueryEditorService {
|
|||||||
// It is assumed that if we got here, !changingToSql is logically equivalent to changingFromSql
|
// It is assumed that if we got here, !changingToSql is logically equivalent to changingFromSql
|
||||||
let changingFromSql = !changingToSql;
|
let changingFromSql = !changingToSql;
|
||||||
if (input instanceof QueryInput && changingFromSql) {
|
if (input instanceof QueryInput && changingFromSql) {
|
||||||
let queryInput: QueryInput = <QueryInput> input;
|
let queryInput: QueryInput = <QueryInput>input;
|
||||||
uriSource = queryInput.sql;
|
uriSource = queryInput.sql;
|
||||||
}
|
}
|
||||||
return getSupportedInputResource(uriSource);
|
return getSupportedInputResource(uriSource);
|
||||||
@@ -378,7 +377,7 @@ export class QueryEditorService implements IQueryEditorService {
|
|||||||
|
|
||||||
// Grab and returns the IModel that will be used to resolve the sqlLanguageModeCheck promise.
|
// Grab and returns the IModel that will be used to resolve the sqlLanguageModeCheck promise.
|
||||||
let control = editor.getControl();
|
let control = editor.getControl();
|
||||||
let codeEditor: CodeEditor = <CodeEditor> control;
|
let codeEditor: CodeEditor = <CodeEditor>control;
|
||||||
let newModel = codeEditor ? codeEditor.getModel() : undefined;
|
let newModel = codeEditor ? codeEditor.getModel() : undefined;
|
||||||
return newModel;
|
return newModel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||||||
import { IConfigurationRegistry, Extensions as ConfigurationExtension } from 'vs/platform/configuration/common/configurationRegistry';
|
import { IConfigurationRegistry, Extensions as ConfigurationExtension } from 'vs/platform/configuration/common/configurationRegistry';
|
||||||
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import { deepClone } from 'vs/base/common/objects';
|
|
||||||
import { IExtensionPointUser, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
import { IExtensionPointUser, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||||
|
|
||||||
import { ProviderProperties } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component';
|
import { ProviderProperties } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component';
|
||||||
@@ -22,11 +21,13 @@ export const Extensions = {
|
|||||||
export interface IDashboardTab {
|
export interface IDashboardTab {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
provider: string | string[];
|
||||||
publisher: string;
|
publisher: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
container?: object;
|
container?: object;
|
||||||
when?: string;
|
when?: string;
|
||||||
alwaysShow?: boolean;
|
alwaysShow?: boolean;
|
||||||
|
isHomeTab?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IDashboardRegistry {
|
export interface IDashboardRegistry {
|
||||||
|
|||||||
@@ -26,10 +26,11 @@ import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
|||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
|
|
||||||
/* Model-backed components */
|
|
||||||
let extensionComponents = Registry.as<IComponentRegistry>(Extensions.ComponentContribution).getAllCtors();
|
|
||||||
|
|
||||||
export const DialogModule = (params, selector: string, instantiationService: IInstantiationService): any => {
|
export const DialogModule = (params, selector: string, instantiationService: IInstantiationService): any => {
|
||||||
|
|
||||||
|
/* Model-backed components */
|
||||||
|
let extensionComponents = Registry.as<IComponentRegistry>(Extensions.ComponentContribution).getAllCtors();
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
Checkbox,
|
Checkbox,
|
||||||
|
|||||||
@@ -6,29 +6,41 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import 'vs/css!./media/dialogModal';
|
import 'vs/css!./media/dialogModal';
|
||||||
import { Component, AfterContentInit, ViewChild, Input, Inject, forwardRef, ElementRef } from '@angular/core';
|
import { Component, ViewChild, Inject, forwardRef, ElementRef, AfterViewInit } from '@angular/core';
|
||||||
import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
|
import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
|
||||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
|
import { DialogPane } from 'sql/platform/dialog/dialogPane';
|
||||||
|
import { ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { ComponentEventType } from '../../parts/modelComponents/interfaces';
|
|
||||||
|
|
||||||
export interface DialogComponentParams extends IBootstrapParams {
|
export interface DialogComponentParams extends IBootstrapParams {
|
||||||
modelViewId: string;
|
modelViewId: string;
|
||||||
validityChangedCallback: (valid: boolean) => void;
|
validityChangedCallback: (valid: boolean) => void;
|
||||||
onLayoutRequested: Event<string>;
|
onLayoutRequested: Event<string>;
|
||||||
|
dialogPane: DialogPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'dialog-modelview-container',
|
selector: 'dialog-modelview-container',
|
||||||
providers: [],
|
providers: [],
|
||||||
template: `
|
template: `
|
||||||
<modelview-content [modelViewId]="modelViewId">
|
<div class="dialogContainer" *ngIf="_dialogPane && _dialogPane.displayPageTitle">
|
||||||
|
<div class="dialogModal-wizardHeader" *ngIf="_dialogPane && _dialogPane.displayPageTitle">
|
||||||
|
<div *ngIf="_dialogPane.pageNumber" class="wizardPageNumber">Step {{_dialogPane.pageNumber}}</div>
|
||||||
|
<h1 class="wizardPageTitle">{{_dialogPane.title}}</h1>
|
||||||
|
<div *ngIf="_dialogPane.description">{{_dialogPane.description}}</div>
|
||||||
|
</div>
|
||||||
|
<modelview-content [modelViewId]="modelViewId">
|
||||||
|
</modelview-content>
|
||||||
|
</div>
|
||||||
|
<modelview-content [modelViewId]="modelViewId" *ngIf="!_dialogPane || !_dialogPane.displayPageTitle">
|
||||||
</modelview-content>
|
</modelview-content>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class DialogContainer implements AfterContentInit {
|
export class DialogContainer implements AfterViewInit {
|
||||||
private _onResize = new Emitter<void>();
|
private _onResize = new Emitter<void>();
|
||||||
public readonly onResize: Event<void> = this._onResize.event;
|
public readonly onResize: Event<void> = this._onResize.event;
|
||||||
|
private _dialogPane: DialogPane;
|
||||||
|
|
||||||
public modelViewId: string;
|
public modelViewId: string;
|
||||||
@ViewChild(ModelViewContent) private _modelViewContent: ModelViewContent;
|
@ViewChild(ModelViewContent) private _modelViewContent: ModelViewContent;
|
||||||
@@ -41,11 +53,12 @@ export class DialogContainer implements AfterContentInit {
|
|||||||
this.layout();
|
this.layout();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this._dialogPane = this._params.dialogPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this._modelViewContent.onEvent(event => {
|
this._modelViewContent.onEvent(event => {
|
||||||
if (event.eventType === ComponentEventType.validityChanged) {
|
if (event.isRootComponent && event.eventType === ComponentEventType.validityChanged) {
|
||||||
this._params.validityChangedCallback(event.args);
|
this._params.validityChangedCallback(event.args);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ export class DialogModal extends Modal {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this._dialogPane = new DialogPane(this._dialog.title, this._dialog.content,
|
this._dialogPane = new DialogPane(this._dialog.title, this._dialog.content,
|
||||||
valid => this._dialog.notifyValidityChanged(valid), this._instantiationService);
|
valid => this._dialog.notifyValidityChanged(valid), this._instantiationService, false);
|
||||||
this._dialogPane.createBody(body);
|
this._dialogPane.createBody(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,11 @@ import 'vs/css!./media/dialogModal';
|
|||||||
import { NgModuleRef } from '@angular/core';
|
import { NgModuleRef } from '@angular/core';
|
||||||
|
|
||||||
import { IModalDialogStyles } from 'sql/base/browser/ui/modal/modal';
|
import { IModalDialogStyles } from 'sql/base/browser/ui/modal/modal';
|
||||||
import { Dialog, DialogTab } from 'sql/platform/dialog/dialogTypes';
|
import { DialogTab } from 'sql/platform/dialog/dialogTypes';
|
||||||
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
|
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
|
||||||
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
|
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
|
||||||
import { DialogModule } from 'sql/platform/dialog/dialog.module';
|
import { DialogModule } from 'sql/platform/dialog/dialog.module';
|
||||||
import { DialogComponentParams } from 'sql/platform/dialog/dialogContainer.component';
|
import { DialogComponentParams } from 'sql/platform/dialog/dialogContainer.component';
|
||||||
import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes';
|
|
||||||
|
|
||||||
import * as DOM from 'vs/base/browser/dom';
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
import { Builder } from 'vs/base/browser/builder';
|
import { Builder } from 'vs/base/browser/builder';
|
||||||
@@ -31,24 +30,21 @@ export class DialogPane extends Disposable implements IThemable {
|
|||||||
// Validation
|
// Validation
|
||||||
private _modelViewValidityMap = new Map<string, boolean>();
|
private _modelViewValidityMap = new Map<string, boolean>();
|
||||||
|
|
||||||
// HTML Elements
|
|
||||||
private _body: HTMLElement;
|
private _body: HTMLElement;
|
||||||
private _tabBar: HTMLElement;
|
|
||||||
private _tabs: HTMLElement[];
|
|
||||||
private _tabContent: HTMLElement[];
|
|
||||||
private _selectedTabIndex: number = 0; //TODO: can be an option
|
private _selectedTabIndex: number = 0; //TODO: can be an option
|
||||||
private _onTabChange = new Emitter<string>();
|
private _onTabChange = new Emitter<string>();
|
||||||
private _selectedTabContent: string;
|
private _selectedTabContent: string;
|
||||||
|
public pageNumber?: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _title: string,
|
public title: string,
|
||||||
private _content: string | DialogTab[],
|
private _content: string | DialogTab[],
|
||||||
private _validityChangedCallback: (valid: boolean) => void,
|
private _validityChangedCallback: (valid: boolean) => void,
|
||||||
private _instantiationService: IInstantiationService
|
private _instantiationService: IInstantiationService,
|
||||||
|
public displayPageTitle: boolean,
|
||||||
|
public description?: string,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._tabs = [];
|
|
||||||
this._tabContent = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public createBody(container: HTMLElement): HTMLElement {
|
public createBody(container: HTMLElement): HTMLElement {
|
||||||
@@ -73,7 +69,7 @@ export class DialogPane extends Disposable implements IThemable {
|
|||||||
});
|
});
|
||||||
this._tabbedPanel.pushTab({
|
this._tabbedPanel.pushTab({
|
||||||
title: tab.title,
|
title: tab.title,
|
||||||
identifier: 'dialogPane.' + this._title + '.' + tabIndex,
|
identifier: 'dialogPane.' + this.title + '.' + tabIndex,
|
||||||
view: {
|
view: {
|
||||||
render: (container) => {
|
render: (container) => {
|
||||||
if (tabContainer.parentElement === this._body) {
|
if (tabContainer.parentElement === this._body) {
|
||||||
@@ -93,12 +89,12 @@ export class DialogPane extends Disposable implements IThemable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getTabDimension(): DOM.Dimension {
|
private getTabDimension(): DOM.Dimension {
|
||||||
return new DOM.Dimension(DOM.getContentWidth(this._body), DOM.getContentHeight(this._body));
|
return new DOM.Dimension(DOM.getContentWidth(this._body) - 5, DOM.getContentHeight(this._body) - 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public layout(): void {
|
public layout(): void {
|
||||||
if (this._tabbedPanel) {
|
if (this._tabbedPanel) {
|
||||||
this._tabbedPanel.layout(new DOM.Dimension(DOM.getContentWidth(this._body), DOM.getContentHeight(this._body)));
|
this._tabbedPanel.layout(this.getTabDimension());
|
||||||
this._onTabChange.fire(this._selectedTabContent);
|
this._onTabChange.fire(this._selectedTabContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +115,8 @@ export class DialogPane extends Disposable implements IThemable {
|
|||||||
tab.notifyValidityChanged(valid);
|
tab.notifyValidityChanged(valid);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLayoutRequested: this._onTabChange.event
|
onLayoutRequested: this._onTabChange.event,
|
||||||
|
dialogPane: this
|
||||||
} as DialogComponentParams,
|
} as DialogComponentParams,
|
||||||
undefined,
|
undefined,
|
||||||
(moduleRef) => {
|
(moduleRef) => {
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ export class DialogButton implements sqlops.window.modelviewdialog.Button {
|
|||||||
export class WizardPage extends DialogTab {
|
export class WizardPage extends DialogTab {
|
||||||
public customButtons: DialogButton[];
|
public customButtons: DialogButton[];
|
||||||
private _enabled: boolean;
|
private _enabled: boolean;
|
||||||
|
private _description: string;
|
||||||
private _onUpdate: Emitter<void> = new Emitter<void>();
|
private _onUpdate: Emitter<void> = new Emitter<void>();
|
||||||
public readonly onUpdate: Event<void> = this._onUpdate.event;
|
public readonly onUpdate: Event<void> = this._onUpdate.event;
|
||||||
|
|
||||||
@@ -150,6 +151,15 @@ export class WizardPage extends DialogTab {
|
|||||||
this._enabled = enabled;
|
this._enabled = enabled;
|
||||||
this._onUpdate.fire();
|
this._onUpdate.fire();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get description(): string {
|
||||||
|
return this._description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set description(description: string) {
|
||||||
|
this._description = description;
|
||||||
|
this._onUpdate.fire();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Wizard {
|
export class Wizard {
|
||||||
@@ -171,6 +181,7 @@ export class Wizard {
|
|||||||
private _onMessageChange = new Emitter<DialogMessage>();
|
private _onMessageChange = new Emitter<DialogMessage>();
|
||||||
public readonly onMessageChange = this._onMessageChange.event;
|
public readonly onMessageChange = this._onMessageChange.event;
|
||||||
private _message: DialogMessage;
|
private _message: DialogMessage;
|
||||||
|
public displayPageTitles: boolean;
|
||||||
|
|
||||||
constructor(public title: string) { }
|
constructor(public title: string) { }
|
||||||
|
|
||||||
|
|||||||
@@ -30,3 +30,19 @@
|
|||||||
.footer-button.dialogModal-hidden {
|
.footer-button.dialogModal-hidden {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialogModal-wizardHeader {
|
||||||
|
padding: 10px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogModal-wizardHeader h1 {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column
|
||||||
|
}
|
||||||
|
|||||||
@@ -133,18 +133,28 @@ export class WizardModal extends Modal {
|
|||||||
});
|
});
|
||||||
this._wizard.onPageAdded(page => {
|
this._wizard.onPageAdded(page => {
|
||||||
this.registerPage(page);
|
this.registerPage(page);
|
||||||
|
this.updatePageNumbers();
|
||||||
this.showPage(this._wizard.currentPage, false);
|
this.showPage(this._wizard.currentPage, false);
|
||||||
});
|
});
|
||||||
this._wizard.onPageRemoved(page => {
|
this._wizard.onPageRemoved(page => {
|
||||||
let dialogPane = this._dialogPanes.get(page);
|
let dialogPane = this._dialogPanes.get(page);
|
||||||
this._dialogPanes.delete(page);
|
this._dialogPanes.delete(page);
|
||||||
|
this.updatePageNumbers();
|
||||||
this.showPage(this._wizard.currentPage, false);
|
this.showPage(this._wizard.currentPage, false);
|
||||||
dialogPane.dispose();
|
dialogPane.dispose();
|
||||||
});
|
});
|
||||||
|
this.updatePageNumbers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updatePageNumbers(): void {
|
||||||
|
this._wizard.pages.forEach((page, index) => {
|
||||||
|
let dialogPane = this._dialogPanes.get(page);
|
||||||
|
dialogPane.pageNumber = index + 1;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerPage(page: WizardPage): void {
|
private registerPage(page: WizardPage): void {
|
||||||
let dialogPane = new DialogPane(page.title, page.content, valid => page.notifyValidityChanged(valid), this._instantiationService);
|
let dialogPane = new DialogPane(page.title, page.content, valid => page.notifyValidityChanged(valid), this._instantiationService, this._wizard.displayPageTitles, page.description);
|
||||||
dialogPane.createBody(this._body);
|
dialogPane.createBody(this._body);
|
||||||
this._dialogPanes.set(page, dialogPane);
|
this._dialogPanes.set(page, dialogPane);
|
||||||
page.onUpdate(() => this.setButtonsForPage(this._wizard.currentPage));
|
page.onUpdate(() => this.setButtonsForPage(this._wizard.currentPage));
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export class MetadataService implements IMetadataService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTableInfo(connectionUri: string, metadata: sqlops.ObjectMetadata): Thenable<sqlops.ColumnMetadata[]> {
|
public getTableInfo(connectionUri: string, metadata: sqlops.ObjectMetadata): Thenable<sqlops.ColumnMetadata[]> {
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { IComponentEventArgs } from 'sql/parts/modelComponents/interfaces';
|
||||||
|
import { Event } from 'vs/base/common/event';
|
||||||
|
|
||||||
export interface IView {
|
export interface IView {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
@@ -14,6 +15,10 @@ export interface IView {
|
|||||||
readonly serverInfo: sqlops.ServerInfo;
|
readonly serverInfo: sqlops.ServerInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IModelViewEventArgs extends IComponentEventArgs {
|
||||||
|
isRootComponent: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IModelView extends IView {
|
export interface IModelView extends IView {
|
||||||
initializeModel(rootComponent: IComponentShape, validationCallback?: (componentId: string) => Thenable<boolean>): void;
|
initializeModel(rootComponent: IComponentShape, validationCallback?: (componentId: string) => Thenable<boolean>): void;
|
||||||
clearContainer(componentId: string): void;
|
clearContainer(componentId: string): void;
|
||||||
@@ -21,7 +26,7 @@ export interface IModelView extends IView {
|
|||||||
setLayout(componentId: string, layout: any): void;
|
setLayout(componentId: string, layout: any): void;
|
||||||
setProperties(componentId: string, properties: { [key: string]: any }): void;
|
setProperties(componentId: string, properties: { [key: string]: any }): void;
|
||||||
registerEvent(componentId: string);
|
registerEvent(componentId: string);
|
||||||
onEvent: Event<any>;
|
onEvent: Event<IModelViewEventArgs>;
|
||||||
validate(componentId: string): Thenable<boolean>;
|
validate(componentId: string): Thenable<boolean>;
|
||||||
readonly onDestroy: Event<void>;
|
readonly onDestroy: Event<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
152
src/sql/sqlops.d.ts
vendored
@@ -635,6 +635,7 @@ declare module 'sqlops' {
|
|||||||
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
||||||
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
||||||
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<SimpleExecuteResult>;
|
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<SimpleExecuteResult>;
|
||||||
|
parseSyntax(ownerUri: string, query: string): Thenable<SyntaxParseResult>;
|
||||||
getQueryRows(rowData: QueryExecuteSubsetParams): Thenable<QueryExecuteSubsetResult>;
|
getQueryRows(rowData: QueryExecuteSubsetParams): Thenable<QueryExecuteSubsetResult>;
|
||||||
disposeQuery(ownerUri: string): Thenable<void>;
|
disposeQuery(ownerUri: string): Thenable<void>;
|
||||||
saveResults(requestParams: SaveResultsRequestParams): Thenable<SaveResultRequestResult>;
|
saveResults(requestParams: SaveResultsRequestParams): Thenable<SaveResultRequestResult>;
|
||||||
@@ -769,6 +770,11 @@ declare module 'sqlops' {
|
|||||||
rows: DbCellValue[][];
|
rows: DbCellValue[][];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SyntaxParseResult {
|
||||||
|
parseable: boolean;
|
||||||
|
errorMessages: string[];
|
||||||
|
}
|
||||||
|
|
||||||
// Query Batch Notification -----------------------------------------------------------------------
|
// Query Batch Notification -----------------------------------------------------------------------
|
||||||
export interface QueryExecuteBatchNotificationParams {
|
export interface QueryExecuteBatchNotificationParams {
|
||||||
batchSummary: BatchSummary;
|
batchSummary: BatchSummary;
|
||||||
@@ -1054,8 +1060,44 @@ declare module 'sqlops' {
|
|||||||
wmiEvent = 4
|
wmiEvent = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum JobCompletionActionCondition {
|
||||||
|
Never = 0,
|
||||||
|
OnSuccess = 1,
|
||||||
|
OnFailure = 2,
|
||||||
|
Always = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FrequencyTypes {
|
||||||
|
Unknown ,
|
||||||
|
OneTime = 1 << 1,
|
||||||
|
Daily = 1 << 2,
|
||||||
|
Weekly = 1 << 3,
|
||||||
|
Monthly = 1 << 4,
|
||||||
|
MonthlyRelative = 1 << 5,
|
||||||
|
AutoStart = 1 << 6,
|
||||||
|
OnIdle = 1 << 7
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FrequencySubDayTypes {
|
||||||
|
Unknown = 0,
|
||||||
|
Once = 1,
|
||||||
|
Second = 2,
|
||||||
|
Minute = 4,
|
||||||
|
Hour = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FrequencyRelativeIntervals {
|
||||||
|
First = 1,
|
||||||
|
Second = 2,
|
||||||
|
Third = 4,
|
||||||
|
Fourth = 8,
|
||||||
|
Last = 16
|
||||||
|
}
|
||||||
|
|
||||||
export interface AgentJobInfo {
|
export interface AgentJobInfo {
|
||||||
name: string;
|
name: string;
|
||||||
|
owner: string;
|
||||||
|
description: string;
|
||||||
currentExecutionStatus: number;
|
currentExecutionStatus: number;
|
||||||
lastRunOutcome: number;
|
lastRunOutcome: number;
|
||||||
currentExecutionStep: string;
|
currentExecutionStep: string;
|
||||||
@@ -1070,9 +1112,38 @@ declare module 'sqlops' {
|
|||||||
lastRun: string;
|
lastRun: string;
|
||||||
nextRun: string;
|
nextRun: string;
|
||||||
jobId: string;
|
jobId: string;
|
||||||
|
EmailLevel: JobCompletionActionCondition;
|
||||||
|
PageLevel: JobCompletionActionCondition;
|
||||||
|
EventLogLevel: JobCompletionActionCondition;
|
||||||
|
DeleteLevel: JobCompletionActionCondition;
|
||||||
|
OperatorToEmail: string;
|
||||||
|
OperatorToPage: string;
|
||||||
|
JobSteps: AgentJobStepInfo[];
|
||||||
|
JobSchedules: AgentJobScheduleInfo[];
|
||||||
|
Alerts: AgentAlertInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobStepInfo {
|
export interface AgentJobScheduleInfo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
jobName: string;
|
||||||
|
isEnabled: boolean;
|
||||||
|
frequencyTypes: FrequencyTypes;
|
||||||
|
frequencySubDayTypes: FrequencySubDayTypes;
|
||||||
|
frequencySubDayInterval: number;
|
||||||
|
frequencyRelativeIntervals; FrequencyRelativeIntervals;
|
||||||
|
frequencyRecurrenceFactor: number;
|
||||||
|
frequencyInterval: number;
|
||||||
|
dateCreated: string;
|
||||||
|
activeStartTimeOfDay: string;
|
||||||
|
activeStartDate: string;
|
||||||
|
activeEndTimeOfDay: string;
|
||||||
|
jobCount: number;
|
||||||
|
activeEndDate: string;
|
||||||
|
scheduleUid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentJobStep {
|
||||||
jobId: string;
|
jobId: string;
|
||||||
stepId: string;
|
stepId: string;
|
||||||
stepName: string;
|
stepName: string;
|
||||||
@@ -1081,6 +1152,33 @@ declare module 'sqlops' {
|
|||||||
runStatus: number;
|
runStatus: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AgentJobStepInfo {
|
||||||
|
jobId: string;
|
||||||
|
jobName: string;
|
||||||
|
script: string;
|
||||||
|
scriptName: string;
|
||||||
|
stepName: string;
|
||||||
|
subSystem: string;
|
||||||
|
id: number;
|
||||||
|
failureAction: string;
|
||||||
|
successAction: string;
|
||||||
|
failStepId: number;
|
||||||
|
successStepId: number;
|
||||||
|
command: string;
|
||||||
|
commandExecutionSuccessCode: number;
|
||||||
|
databaseName: string;
|
||||||
|
databaseUserName: string;
|
||||||
|
server: string;
|
||||||
|
outputFileName: string;
|
||||||
|
appendToLogFile: boolean;
|
||||||
|
appendToStepHist: boolean;
|
||||||
|
writeLogToTable: boolean;
|
||||||
|
appendLogToTable: boolean;
|
||||||
|
retryAttempts: number;
|
||||||
|
retryInterval: number;
|
||||||
|
proxyName: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AgentJobHistoryInfo {
|
export interface AgentJobHistoryInfo {
|
||||||
instanceId: number;
|
instanceId: number;
|
||||||
sqlMessageId: string;
|
sqlMessageId: string;
|
||||||
@@ -1098,7 +1196,7 @@ declare module 'sqlops' {
|
|||||||
operatorPaged: string;
|
operatorPaged: string;
|
||||||
retriesAttempted: string;
|
retriesAttempted: string;
|
||||||
server: string;
|
server: string;
|
||||||
steps: AgentJobStepInfo[];
|
steps: AgentJobStep[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentProxyInfo {
|
export interface AgentProxyInfo {
|
||||||
@@ -1113,6 +1211,7 @@ declare module 'sqlops' {
|
|||||||
|
|
||||||
export interface AgentAlertInfo {
|
export interface AgentAlertInfo {
|
||||||
id: number;
|
id: number;
|
||||||
|
name: string;
|
||||||
delayBetweenResponses: number;
|
delayBetweenResponses: number;
|
||||||
eventDescriptionKeyword: string;
|
eventDescriptionKeyword: string;
|
||||||
eventSource: string;
|
eventSource: string;
|
||||||
@@ -1173,15 +1272,26 @@ declare module 'sqlops' {
|
|||||||
job: AgentJobInfo;
|
job: AgentJobInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateAgentJobResult extends ResultStatus {
|
export interface UpdateAgentJobResult extends ResultStatus {
|
||||||
job: AgentJobInfo;
|
job: AgentJobInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AgentJobCategory
|
||||||
|
{
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentJobDefaultsResult extends ResultStatus {
|
||||||
|
owner: string;
|
||||||
|
categories: AgentJobCategory[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface CreateAgentJobStepResult extends ResultStatus {
|
export interface CreateAgentJobStepResult extends ResultStatus {
|
||||||
step: AgentJobStepInfo;
|
step: AgentJobStepInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateAgentJobStepResult extends ResultStatus {
|
export interface UpdateAgentJobStepResult extends ResultStatus {
|
||||||
step: AgentJobStepInfo;
|
step: AgentJobStepInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1189,7 +1299,7 @@ declare module 'sqlops' {
|
|||||||
step: AgentJobStepInfo;
|
step: AgentJobStepInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateAgentProxyResult extends ResultStatus {
|
export interface UpdateAgentProxyResult extends ResultStatus {
|
||||||
step: AgentJobStepInfo;
|
step: AgentJobStepInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1201,7 +1311,7 @@ declare module 'sqlops' {
|
|||||||
alert: AgentJobStepInfo;
|
alert: AgentJobStepInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateAgentAlertResult extends ResultStatus {
|
export interface UpdateAgentAlertResult extends ResultStatus {
|
||||||
alert: AgentJobStepInfo;
|
alert: AgentJobStepInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1213,20 +1323,32 @@ declare module 'sqlops' {
|
|||||||
operator: AgentOperatorInfo;
|
operator: AgentOperatorInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateAgentOperatorResult extends ResultStatus {
|
export interface UpdateAgentOperatorResult extends ResultStatus {
|
||||||
operator: AgentOperatorInfo;
|
operator: AgentOperatorInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentProxiesResult extends ResultStatus {
|
export interface AgentProxiesResult extends ResultStatus {
|
||||||
operators: AgentOperatorInfo[];
|
proxies: AgentProxyInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateAgentProxyResult extends ResultStatus {
|
export interface CreateAgentProxyResult extends ResultStatus {
|
||||||
operator: AgentOperatorInfo;
|
proxy: AgentProxyInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateAgentProxyResult extends ResultStatus {
|
export interface UpdateAgentProxyResult extends ResultStatus {
|
||||||
operator: AgentOperatorInfo;
|
proxy: AgentProxyInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentJobSchedulesResult extends ResultStatus {
|
||||||
|
schedules: AgentJobScheduleInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAgentJobScheduleResult extends ResultStatus {
|
||||||
|
schedule: AgentJobScheduleInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAgentJobScheduleResult extends ResultStatus {
|
||||||
|
schedule: AgentJobScheduleInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentServicesProvider extends DataProvider {
|
export interface AgentServicesProvider extends DataProvider {
|
||||||
@@ -1237,6 +1359,7 @@ declare module 'sqlops' {
|
|||||||
createJob(ownerUri: string, jobInfo: AgentJobInfo): Thenable<CreateAgentJobResult>;
|
createJob(ownerUri: string, jobInfo: AgentJobInfo): Thenable<CreateAgentJobResult>;
|
||||||
updateJob(ownerUri: string, originalJobName: string, jobInfo: AgentJobInfo): Thenable<UpdateAgentJobResult>;
|
updateJob(ownerUri: string, originalJobName: string, jobInfo: AgentJobInfo): Thenable<UpdateAgentJobResult>;
|
||||||
deleteJob(ownerUri: string, jobInfo: AgentJobInfo): Thenable<ResultStatus>;
|
deleteJob(ownerUri: string, jobInfo: AgentJobInfo): Thenable<ResultStatus>;
|
||||||
|
getJobDefaults(ownerUri: string): Thenable<AgentJobDefaultsResult>;
|
||||||
|
|
||||||
// Job Step management methods
|
// Job Step management methods
|
||||||
createJobStep(ownerUri: string, jobInfo: AgentJobStepInfo): Thenable<CreateAgentJobStepResult>;
|
createJobStep(ownerUri: string, jobInfo: AgentJobStepInfo): Thenable<CreateAgentJobStepResult>;
|
||||||
@@ -1260,6 +1383,13 @@ declare module 'sqlops' {
|
|||||||
createProxy(ownerUri: string, proxyInfo: AgentProxyInfo): Thenable<CreateAgentOperatorResult>;
|
createProxy(ownerUri: string, proxyInfo: AgentProxyInfo): Thenable<CreateAgentOperatorResult>;
|
||||||
updateProxy(ownerUri: string, originalProxyName: string, proxyInfo: AgentProxyInfo): Thenable<UpdateAgentOperatorResult>;
|
updateProxy(ownerUri: string, originalProxyName: string, proxyInfo: AgentProxyInfo): Thenable<UpdateAgentOperatorResult>;
|
||||||
deleteProxy(ownerUri: string, proxyInfo: AgentProxyInfo): Thenable<ResultStatus>;
|
deleteProxy(ownerUri: string, proxyInfo: AgentProxyInfo): Thenable<ResultStatus>;
|
||||||
|
|
||||||
|
|
||||||
|
// Job Schedule management methods
|
||||||
|
getJobSchedules(ownerUri: string): Thenable<AgentJobSchedulesResult>;
|
||||||
|
createJobSchedule(ownerUri: string, scheduleInfo: AgentJobScheduleInfo): Thenable<CreateAgentJobScheduleResult>;
|
||||||
|
updateJobSchedule(ownerUri: string, originalScheduleName: string, scheduleInfo: AgentJobScheduleInfo): Thenable<UpdateAgentJobScheduleResult>;
|
||||||
|
deleteJobSchedule(ownerUri: string, scheduleInfo: AgentJobScheduleInfo): Thenable<ResultStatus>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Task service interfaces ----------------------------------------------------------------------------
|
// Task service interfaces ----------------------------------------------------------------------------
|
||||||
|
|||||||
27
src/sql/sqlops.proposed.d.ts
vendored
@@ -133,6 +133,7 @@ declare module 'sqlops' {
|
|||||||
component: Component;
|
component: Component;
|
||||||
title: string;
|
title: string;
|
||||||
actions?: Component[];
|
actions?: Component[];
|
||||||
|
required?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolbarComponent {
|
export interface ToolbarComponent {
|
||||||
@@ -240,7 +241,6 @@ declare module 'sqlops' {
|
|||||||
componentWidth?: number | string;
|
componentWidth?: number | string;
|
||||||
componentHeight?: number | string;
|
componentHeight?: number | string;
|
||||||
titleFontSize?: number | string;
|
titleFontSize?: number | string;
|
||||||
required?: boolean;
|
|
||||||
info?: string;
|
info?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +301,7 @@ declare module 'sqlops' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum CardType {
|
export enum CardType {
|
||||||
VerticalButton = 'VerticalButton',
|
VerticalButton = 'VerticalButton',
|
||||||
Details = 'Details'
|
Details = 'Details'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,8 +314,16 @@ declare module 'sqlops' {
|
|||||||
value?: string;
|
value?: string;
|
||||||
actions?: ActionDescriptor[];
|
actions?: ActionDescriptor[];
|
||||||
status?: StatusIndicator;
|
status?: StatusIndicator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the card is selected
|
||||||
|
*/
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
cardType: CardType;
|
|
||||||
|
/**
|
||||||
|
* Card Type, default: Details
|
||||||
|
*/
|
||||||
|
cardType?: CardType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InputBoxInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'text' | 'time' | 'url' | 'week';
|
export type InputBoxInputType = 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'text' | 'time' | 'url' | 'week';
|
||||||
@@ -748,13 +756,18 @@ declare module 'sqlops' {
|
|||||||
* able to advance to it. Defaults to true.
|
* able to advance to it. Defaults to true.
|
||||||
*/
|
*/
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional description for the page. If provided it will be displayed underneath the page title.
|
||||||
|
*/
|
||||||
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Wizard {
|
export interface Wizard {
|
||||||
/**
|
/**
|
||||||
* The title of the wizard
|
* The title of the wizard
|
||||||
*/
|
*/
|
||||||
title: string,
|
title: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The wizard's pages. Pages can be added/removed while the dialog is open by using
|
* The wizard's pages. Pages can be added/removed while the dialog is open by using
|
||||||
@@ -800,6 +813,12 @@ declare module 'sqlops' {
|
|||||||
*/
|
*/
|
||||||
customButtons: Button[];
|
customButtons: Button[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When set to false page titles and descriptions will not be displayed at the top
|
||||||
|
* of each wizard page. The default is true.
|
||||||
|
*/
|
||||||
|
displayPageTitles: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event fired when the wizard's page changes, containing information about the
|
* Event fired when the wizard's page changes, containing information about the
|
||||||
* previous page and the new page
|
* previous page and the new page
|
||||||
|
|||||||
@@ -63,8 +63,7 @@ export enum ScriptOperation {
|
|||||||
Alter = 6
|
Alter = 6
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum WeekDays
|
export enum WeekDays {
|
||||||
{
|
|
||||||
sunday = 1,
|
sunday = 1,
|
||||||
monday = 2,
|
monday = 2,
|
||||||
tuesday = 4,
|
tuesday = 4,
|
||||||
@@ -77,8 +76,7 @@ export enum WeekDays
|
|||||||
everyDay = 127
|
everyDay = 127
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum NotifyMethods
|
export enum NotifyMethods {
|
||||||
{
|
|
||||||
none = 0,
|
none = 0,
|
||||||
notifyEmail = 1,
|
notifyEmail = 1,
|
||||||
pager = 2,
|
pager = 2,
|
||||||
@@ -86,14 +84,47 @@ export enum NotifyMethods
|
|||||||
notifyAll = 7
|
notifyAll = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AlertType
|
export enum JobCompletionActionCondition {
|
||||||
{
|
Never = 0,
|
||||||
|
OnSuccess = 1,
|
||||||
|
OnFailure = 2,
|
||||||
|
Always = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AlertType {
|
||||||
sqlServerEvent = 1,
|
sqlServerEvent = 1,
|
||||||
sqlServerPerformanceCondition = 2,
|
sqlServerPerformanceCondition = 2,
|
||||||
nonSqlServerEvent = 3,
|
nonSqlServerEvent = 3,
|
||||||
wmiEvent = 4
|
wmiEvent = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum FrequencyTypes {
|
||||||
|
Unknown ,
|
||||||
|
OneTime = 1 << 1,
|
||||||
|
Daily = 1 << 2,
|
||||||
|
Weekly = 1 << 3,
|
||||||
|
Monthly = 1 << 4,
|
||||||
|
MonthlyRelative = 1 << 5,
|
||||||
|
AutoStart = 1 << 6,
|
||||||
|
OnIdle = 1 << 7
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FrequencySubDayTypes {
|
||||||
|
Unknown = 0,
|
||||||
|
Once = 1,
|
||||||
|
Second = 2,
|
||||||
|
Minute = 4,
|
||||||
|
Hour = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FrequencyRelativeIntervals {
|
||||||
|
First = 1,
|
||||||
|
Second = 2,
|
||||||
|
Third = 4,
|
||||||
|
Fourth = 8,
|
||||||
|
Last = 16
|
||||||
|
}
|
||||||
|
|
||||||
export enum ModelComponentTypes {
|
export enum ModelComponentTypes {
|
||||||
NavContainer,
|
NavContainer,
|
||||||
FlexContainer,
|
FlexContainer,
|
||||||
@@ -168,6 +199,7 @@ export interface IModelViewWizardPageDetails {
|
|||||||
content: string;
|
content: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
customButtons: number[];
|
customButtons: number[];
|
||||||
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IModelViewWizardDetails {
|
export interface IModelViewWizardDetails {
|
||||||
@@ -181,6 +213,7 @@ export interface IModelViewWizardDetails {
|
|||||||
backButton: number;
|
backButton: number;
|
||||||
customButtons: number[];
|
customButtons: number[];
|
||||||
message: DialogMessage;
|
message: DialogMessage;
|
||||||
|
displayPageTitles: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MessageLevel {
|
export enum MessageLevel {
|
||||||
|
|||||||