mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-22 11:01:37 -05:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d63905056 | ||
|
|
cb224fbc74 | ||
|
|
067846099b | ||
|
|
0e14908360 | ||
|
|
27735dd68b | ||
|
|
a8eaf28884 | ||
|
|
2c007115f7 | ||
|
|
ac47fb84a8 | ||
|
|
7ba14a3925 | ||
|
|
67514ccc5f | ||
|
|
6ee3886ecf | ||
|
|
76282ed1ef | ||
|
|
bfa9e8c495 | ||
|
|
425eecf692 | ||
|
|
ac1f7542a9 | ||
|
|
225d168fdd | ||
|
|
e073b2cf42 | ||
|
|
1cb366d822 | ||
|
|
c9c8e30ced | ||
|
|
c50941ac7d |
@@ -68,7 +68,7 @@ Type: filesandordirs; Name: "{app}\_"
|
|||||||
[Tasks]
|
[Tasks]
|
||||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce; Check: IsNotUpdate
|
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce; Check: IsNotUpdate
|
||||||
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
|
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
|
||||||
Name: "associatewithfiles"; Description: "{cm:AssociateWithFiles,{#NameShort}}"; GroupDescription: "{cm:Other}"; Flags: unchecked
|
Name: "associatewithfiles"; Description: "{cm:AssociateWithFiles,{#NameLong}}"; GroupDescription: "{cm:Other}"; Flags: unchecked
|
||||||
Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}"
|
Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}"
|
||||||
Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent
|
Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,6 @@
|
|||||||
"outputChannels": [
|
"outputChannels": [
|
||||||
"sqlagent"
|
"sqlagent"
|
||||||
],
|
],
|
||||||
"commands": [
|
|
||||||
{
|
|
||||||
"command": "agent.openNewStepDialog",
|
|
||||||
"title": "agent.openNewStepDialog"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dashboard.tabs": [
|
"dashboard.tabs": [
|
||||||
{
|
{
|
||||||
"id": "data-management-agent",
|
"id": "data-management-agent",
|
||||||
|
|||||||
@@ -35,4 +35,5 @@ export class AgentUtils {
|
|||||||
}
|
}
|
||||||
return this._queryProvider;
|
return this._queryProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -136,8 +136,13 @@ export class JobData implements IAgentDialogData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public addJobSchedule(schedule: sqlops.AgentJobScheduleInfo) {
|
public addJobSchedule(schedule: sqlops.AgentJobScheduleInfo) {
|
||||||
let existingSchedule = this.jobSchedules.find(item => item.name === schedule.name);
|
if (this.jobSchedules) {
|
||||||
if (!existingSchedule) {
|
let existingSchedule = this.jobSchedules.find(item => item.name === schedule.name);
|
||||||
|
if (!existingSchedule) {
|
||||||
|
this.jobSchedules.push(schedule);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.jobSchedules = [];
|
||||||
this.jobSchedules.push(schedule);
|
this.jobSchedules.push(schedule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,20 +4,22 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
import { AgentUtils } from '../agentUtils';
|
import { AgentUtils } from '../agentUtils';
|
||||||
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
|
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
|
||||||
import { JobData } from './jobData';
|
import { JobData } from './jobData';
|
||||||
import * as nls from 'vscode-nls';
|
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export class JobStepData implements IAgentDialogData {
|
export class JobStepData implements IAgentDialogData {
|
||||||
|
|
||||||
// Error Messages
|
// Error Messages
|
||||||
private readonly CreateStepErrorMessage_JobNameIsEmpty = localize('stepData.jobNameRequired', 'Job name must be provided');
|
private static readonly CreateStepErrorMessage_JobNameIsEmpty = localize('stepData.jobNameRequired', 'Job name must be provided');
|
||||||
private readonly CreateStepErrorMessage_StepNameIsEmpty = localize('stepData.stepNameRequired', 'Step name must be provided');
|
private static readonly CreateStepErrorMessage_StepNameIsEmpty = localize('stepData.stepNameRequired', 'Step name must be provided');
|
||||||
|
|
||||||
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
|
public dialogMode: AgentDialogMode;
|
||||||
public ownerUri: string;
|
public ownerUri: string;
|
||||||
public jobId: string;
|
public jobId: string;
|
||||||
public jobName: string;
|
public jobName: string;
|
||||||
@@ -28,8 +30,8 @@ export class JobStepData implements IAgentDialogData {
|
|||||||
public id: number;
|
public id: number;
|
||||||
public failureAction: string;
|
public failureAction: string;
|
||||||
public successAction: string;
|
public successAction: string;
|
||||||
public failStepId: number;
|
|
||||||
public successStepId: number;
|
public successStepId: number;
|
||||||
|
public failStepId: number;
|
||||||
public command: string;
|
public command: string;
|
||||||
public commandExecutionSuccessCode: number;
|
public commandExecutionSuccessCode: number;
|
||||||
public databaseName: string;
|
public databaseName: string;
|
||||||
@@ -43,10 +45,12 @@ export class JobStepData implements IAgentDialogData {
|
|||||||
public retryAttempts: number;
|
public retryAttempts: number;
|
||||||
public retryInterval: number;
|
public retryInterval: number;
|
||||||
public proxyName: string;
|
public proxyName: string;
|
||||||
|
private jobModel: JobData;
|
||||||
|
|
||||||
constructor(ownerUri:string, jobModel?: JobData) {
|
constructor(ownerUri:string, jobModel?: JobData) {
|
||||||
this.ownerUri = ownerUri;
|
this.ownerUri = ownerUri;
|
||||||
this.jobName = jobModel.name;
|
this.jobName = jobModel.name;
|
||||||
|
this.jobModel = jobModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async initialize() {
|
public async initialize() {
|
||||||
@@ -54,47 +58,35 @@ export class JobStepData implements IAgentDialogData {
|
|||||||
|
|
||||||
public async save() {
|
public async save() {
|
||||||
let agentService = await AgentUtils.getAgentService();
|
let agentService = await AgentUtils.getAgentService();
|
||||||
agentService.createJobStep(this.ownerUri, {
|
let result: any;
|
||||||
jobId: this.jobId,
|
if (this.dialogMode === AgentDialogMode.CREATE) {
|
||||||
jobName: this.jobName,
|
if (this.jobModel && this.jobModel.dialogMode === AgentDialogMode.CREATE) {
|
||||||
script: this.script,
|
// create job -> create step
|
||||||
scriptName: this.scriptName,
|
Promise.resolve(this);
|
||||||
stepName: this.stepName,
|
return;
|
||||||
subSystem: this.subSystem,
|
} else {
|
||||||
id: this.id,
|
// edit job -> create step
|
||||||
failureAction: this.failureAction,
|
result = await agentService.createJobStep(this.ownerUri, JobStepData.convertToAgentJobStepInfo(this));
|
||||||
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 => {
|
|
||||||
if (result && result.success) {
|
|
||||||
console.info(result);
|
|
||||||
}
|
}
|
||||||
});
|
} else if (this.jobModel && this.jobModel.dialogMode === AgentDialogMode.EDIT) {
|
||||||
|
// edit job -> edit step
|
||||||
|
result = await agentService.updateJobStep(this.ownerUri, this.stepName, JobStepData.convertToAgentJobStepInfo(this));
|
||||||
|
}
|
||||||
|
if (!result || !result.success) {
|
||||||
|
vscode.window.showErrorMessage(
|
||||||
|
localize('jobStepData.saveErrorMessage', "Step update failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public validate(): { valid: boolean, errorMessages: string[] } {
|
public validate(): { valid: boolean, errorMessages: string[] } {
|
||||||
let validationErrors: string[] = [];
|
let validationErrors: string[] = [];
|
||||||
|
|
||||||
if (!(this.stepName && this.stepName.trim())) {
|
if (!(this.stepName && this.stepName.trim())) {
|
||||||
validationErrors.push(this.CreateStepErrorMessage_StepNameIsEmpty);
|
validationErrors.push(JobStepData.CreateStepErrorMessage_StepNameIsEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(this.jobName && this.jobName.trim())) {
|
if (!(this.jobName && this.jobName.trim())) {
|
||||||
validationErrors.push(this.CreateStepErrorMessage_JobNameIsEmpty);
|
validationErrors.push(JobStepData.CreateStepErrorMessage_JobNameIsEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -102,4 +94,66 @@ export class JobStepData implements IAgentDialogData {
|
|||||||
errorMessages: validationErrors
|
errorMessages: validationErrors
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static convertToJobStepData(jobStepInfo: sqlops.AgentJobStepInfo, jobData: JobData) {
|
||||||
|
let stepData = new JobStepData(jobData.ownerUri, jobData);
|
||||||
|
stepData.ownerUri = jobData.ownerUri;
|
||||||
|
stepData.jobId = jobStepInfo.jobId;
|
||||||
|
stepData.jobName = jobStepInfo.jobName;
|
||||||
|
stepData.script = jobStepInfo.script;
|
||||||
|
stepData.scriptName = jobStepInfo.scriptName,
|
||||||
|
stepData.stepName = jobStepInfo.stepName,
|
||||||
|
stepData.subSystem = jobStepInfo.subSystem,
|
||||||
|
stepData.id = jobStepInfo.id,
|
||||||
|
stepData.failureAction = jobStepInfo.failureAction,
|
||||||
|
stepData.successAction = jobStepInfo.successAction,
|
||||||
|
stepData.failStepId = jobStepInfo.failStepId,
|
||||||
|
stepData.successStepId = jobStepInfo.successStepId,
|
||||||
|
stepData.command = jobStepInfo.command,
|
||||||
|
stepData.commandExecutionSuccessCode = jobStepInfo.commandExecutionSuccessCode,
|
||||||
|
stepData.databaseName = jobStepInfo.databaseName,
|
||||||
|
stepData.databaseUserName = jobStepInfo.databaseUserName,
|
||||||
|
stepData.server = jobStepInfo.server,
|
||||||
|
stepData.outputFileName = jobStepInfo.outputFileName,
|
||||||
|
stepData.appendToLogFile = jobStepInfo.appendToLogFile,
|
||||||
|
stepData.appendToStepHist = jobStepInfo.appendToStepHist,
|
||||||
|
stepData.writeLogToTable = jobStepInfo.writeLogToTable,
|
||||||
|
stepData.appendLogToTable = jobStepInfo.appendLogToTable,
|
||||||
|
stepData.retryAttempts = jobStepInfo.retryAttempts,
|
||||||
|
stepData.retryInterval = jobStepInfo.retryInterval,
|
||||||
|
stepData.proxyName = jobStepInfo.proxyName;
|
||||||
|
stepData.dialogMode = AgentDialogMode.EDIT;
|
||||||
|
return stepData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static convertToAgentJobStepInfo(jobStepData: JobStepData): sqlops.AgentJobStepInfo {
|
||||||
|
let result: sqlops.AgentJobStepInfo = {
|
||||||
|
jobId: jobStepData.jobId,
|
||||||
|
jobName: jobStepData.jobName,
|
||||||
|
script: jobStepData.script,
|
||||||
|
scriptName: jobStepData.scriptName,
|
||||||
|
stepName: jobStepData.stepName,
|
||||||
|
subSystem: jobStepData.subSystem,
|
||||||
|
id: jobStepData.id,
|
||||||
|
failureAction: jobStepData.failureAction,
|
||||||
|
successAction: jobStepData.successAction,
|
||||||
|
failStepId: jobStepData.failStepId,
|
||||||
|
successStepId: jobStepData.successStepId,
|
||||||
|
command: jobStepData.command,
|
||||||
|
commandExecutionSuccessCode: jobStepData.commandExecutionSuccessCode,
|
||||||
|
databaseName: jobStepData.databaseName,
|
||||||
|
databaseUserName: jobStepData.databaseUserName,
|
||||||
|
server: jobStepData.server,
|
||||||
|
outputFileName: jobStepData.outputFileName,
|
||||||
|
appendToLogFile: jobStepData.appendToLogFile,
|
||||||
|
appendToStepHist: jobStepData.appendToStepHist,
|
||||||
|
writeLogToTable: jobStepData.writeLogToTable,
|
||||||
|
appendLogToTable: jobStepData.appendLogToTable,
|
||||||
|
retryAttempts: jobStepData.retryAttempts,
|
||||||
|
retryInterval: jobStepData.retryInterval,
|
||||||
|
proxyName: jobStepData.proxyName
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -13,9 +13,11 @@ export class PickScheduleData implements IAgentDialogData {
|
|||||||
public ownerUri: string;
|
public ownerUri: string;
|
||||||
public schedules: sqlops.AgentJobScheduleInfo[];
|
public schedules: sqlops.AgentJobScheduleInfo[];
|
||||||
public selectedSchedule: sqlops.AgentJobScheduleInfo;
|
public selectedSchedule: sqlops.AgentJobScheduleInfo;
|
||||||
|
private jobName: string;
|
||||||
|
|
||||||
constructor(ownerUri:string) {
|
constructor(ownerUri:string, jobName: string) {
|
||||||
this.ownerUri = ownerUri;
|
this.ownerUri = ownerUri;
|
||||||
|
this.jobName = jobName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async initialize() {
|
public async initialize() {
|
||||||
@@ -27,5 +29,8 @@ export class PickScheduleData implements IAgentDialogData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async save() {
|
public async save() {
|
||||||
|
let agentService = await AgentUtils.getAgentService();
|
||||||
|
this.selectedSchedule.jobName = this.jobName;
|
||||||
|
let result = await agentService.createJobSchedule(this.ownerUri, this.selectedSchedule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,12 +108,14 @@ export class JobDialog extends AgentDialog<JobData> {
|
|||||||
// Alert tab controls
|
// Alert tab controls
|
||||||
private alertsTable: sqlops.TableComponent;
|
private alertsTable: sqlops.TableComponent;
|
||||||
private newAlertButton: sqlops.ButtonComponent;
|
private newAlertButton: sqlops.ButtonComponent;
|
||||||
|
private isEdit: boolean = false;
|
||||||
|
|
||||||
constructor(ownerUri: string, jobInfo: sqlops.AgentJobInfo = undefined) {
|
constructor(ownerUri: string, jobInfo: sqlops.AgentJobInfo = undefined) {
|
||||||
super(
|
super(
|
||||||
ownerUri,
|
ownerUri,
|
||||||
new JobData(ownerUri, jobInfo),
|
new JobData(ownerUri, jobInfo),
|
||||||
jobInfo ? JobDialog.EditDialogTitle : JobDialog.CreateDialogTitle);
|
jobInfo ? JobDialog.EditDialogTitle : JobDialog.CreateDialogTitle);
|
||||||
|
this.isEdit = jobInfo ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async initializeDialog() {
|
protected async initializeDialog() {
|
||||||
@@ -235,13 +237,17 @@ export class JobDialog extends AgentDialog<JobData> {
|
|||||||
width: 80
|
width: 80
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
let stepDialog = new JobStepDialog(this.model.ownerUri, '' , data.length + 1, this.model);
|
let stepDialog = new JobStepDialog(this.model.ownerUri, '' , this.model);
|
||||||
stepDialog.onSuccess((step) => {
|
stepDialog.onSuccess((step) => {
|
||||||
|
if (!this.model.jobSteps) {
|
||||||
|
this.model.jobSteps = [];
|
||||||
|
}
|
||||||
this.model.jobSteps.push(step);
|
this.model.jobSteps.push(step);
|
||||||
this.stepsTable.data = this.convertStepsToData(this.model.jobSteps);
|
this.stepsTable.data = this.convertStepsToData(this.model.jobSteps);
|
||||||
});
|
});
|
||||||
this.newStepButton.onDidClick((e)=>{
|
this.newStepButton.onDidClick((e)=>{
|
||||||
if (this.nameTextBox.value && this.nameTextBox.value.length > 0) {
|
if (this.nameTextBox.value && this.nameTextBox.value.length > 0) {
|
||||||
|
stepDialog.jobName = this.nameTextBox.value;
|
||||||
stepDialog.openDialog();
|
stepDialog.openDialog();
|
||||||
} else {
|
} else {
|
||||||
this.dialog.message = { text: this.BlankJobNameErrorText };
|
this.dialog.message = { text: this.BlankJobNameErrorText };
|
||||||
@@ -267,17 +273,15 @@ export class JobDialog extends AgentDialog<JobData> {
|
|||||||
// one step selection
|
// one step selection
|
||||||
if (this.stepsTable.selectedRows.length === 1) {
|
if (this.stepsTable.selectedRows.length === 1) {
|
||||||
let rowNumber = this.stepsTable.selectedRows[0];
|
let rowNumber = this.stepsTable.selectedRows[0];
|
||||||
let stepData = steps[rowNumber];
|
let stepData = this.model.jobSteps[rowNumber];
|
||||||
this.deleteStepButton.enabled = true;
|
this.deleteStepButton.enabled = true;
|
||||||
this.editStepButton.enabled = true;
|
this.editStepButton.enabled = true;
|
||||||
this.editStepButton.onDidClick((e) => {
|
this.editStepButton.onDidClick(() => {
|
||||||
// implement edit steps
|
let stepDialog = new JobStepDialog(this.model.ownerUri, '' , this.model, stepData);
|
||||||
|
stepDialog.openDialog();
|
||||||
// let stepDialog = new JobStepDialog(this.model.ownerUri, this.nameTextBox.value, '' , 1, this.model);
|
|
||||||
// stepDialog.openNewStepDialog();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.deleteStepButton.onDidClick((e) => {
|
this.deleteStepButton.onDidClick(() => {
|
||||||
AgentUtils.getAgentService().then((agentService) => {
|
AgentUtils.getAgentService().then((agentService) => {
|
||||||
let steps = this.model.jobSteps ? this.model.jobSteps : [];
|
let steps = this.model.jobSteps ? this.model.jobSteps : [];
|
||||||
agentService.deleteJobStep(this.ownerUri, stepData).then((result) => {
|
agentService.deleteJobStep(this.ownerUri, stepData).then((result) => {
|
||||||
@@ -371,12 +375,12 @@ export class JobDialog extends AgentDialog<JobData> {
|
|||||||
label: this.PickScheduleButtonString,
|
label: this.PickScheduleButtonString,
|
||||||
width: 80
|
width: 80
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.pickScheduleButton.onDidClick((e)=>{
|
this.pickScheduleButton.onDidClick((e)=>{
|
||||||
let pickScheduleDialog = new PickScheduleDialog(this.model.ownerUri);
|
let pickScheduleDialog = new PickScheduleDialog(this.model.ownerUri, this.model.name);
|
||||||
pickScheduleDialog.onSuccess((dialogModel) => {
|
pickScheduleDialog.onSuccess((dialogModel) => {
|
||||||
let selectedSchedule = dialogModel.selectedSchedule;
|
let selectedSchedule = dialogModel.selectedSchedule;
|
||||||
if (selectedSchedule) {
|
if (selectedSchedule) {
|
||||||
|
selectedSchedule.jobName = this.model.name;
|
||||||
this.model.addJobSchedule(selectedSchedule);
|
this.model.addJobSchedule(selectedSchedule);
|
||||||
this.populateScheduleTable();
|
this.populateScheduleTable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { JobStepData } from '../data/jobStepData';
|
|||||||
import { AgentUtils } from '../agentUtils';
|
import { AgentUtils } from '../agentUtils';
|
||||||
import { JobData } from '../data/jobData';
|
import { JobData } from '../data/jobData';
|
||||||
import { AgentDialog } from './agentDialog';
|
import { AgentDialog } from './agentDialog';
|
||||||
|
import { AgentDialogMode } from '../interfaces';
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
@@ -19,7 +20,8 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
|
|||||||
// TODO: localize
|
// TODO: localize
|
||||||
// Top level
|
// Top level
|
||||||
//
|
//
|
||||||
private readonly DialogTitle: string = localize('jobStepDialog.newJobStep', 'New Job Step');
|
private static readonly NewDialogTitle: string = localize('jobStepDialog.newJobStep', 'New Job Step');
|
||||||
|
private static readonly EditDialogTitle: string = localize('jobStepDialog.editJobStep', 'Edit Job Step');
|
||||||
private readonly FileBrowserDialogTitle: string = localize('jobStepDialog.fileBrowserTitle', 'Locate Database Files - ');
|
private readonly FileBrowserDialogTitle: string = localize('jobStepDialog.fileBrowserTitle', 'Locate Database Files - ');
|
||||||
private readonly OkButtonText: string = localize('jobStepDialog.ok', 'OK');
|
private readonly OkButtonText: string = localize('jobStepDialog.ok', 'OK');
|
||||||
private readonly CancelButtonText: string = localize('jobStepDialog.cancel', 'Cancel');
|
private readonly CancelButtonText: string = localize('jobStepDialog.cancel', 'Cancel');
|
||||||
@@ -102,24 +104,32 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
|
|||||||
// Checkbox
|
// Checkbox
|
||||||
private appendToExistingFileCheckbox: sqlops.CheckBoxComponent;
|
private appendToExistingFileCheckbox: sqlops.CheckBoxComponent;
|
||||||
private logToTableCheckbox: sqlops.CheckBoxComponent;
|
private logToTableCheckbox: sqlops.CheckBoxComponent;
|
||||||
|
private logStepOutputHistoryCheckbox: sqlops.CheckBoxComponent;
|
||||||
|
|
||||||
private fileBrowserTree: sqlops.FileBrowserTreeComponent;
|
private fileBrowserTree: sqlops.FileBrowserTreeComponent;
|
||||||
private jobModel: JobData;
|
private jobModel: JobData;
|
||||||
private jobName: string;
|
public jobName: string;
|
||||||
private server: string;
|
private server: string;
|
||||||
private stepId: number;
|
private stepId: number;
|
||||||
|
private isEdit: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
ownerUri: string,
|
ownerUri: string,
|
||||||
server: string,
|
server: string,
|
||||||
stepId: number,
|
jobModel: JobData,
|
||||||
jobModel?: JobData
|
jobStepInfo?: sqlops.AgentJobStepInfo,
|
||||||
) {
|
) {
|
||||||
super(ownerUri, new JobStepData(ownerUri, jobModel), 'New Step');
|
super(ownerUri,
|
||||||
this.stepId = stepId;
|
jobStepInfo ? JobStepData.convertToJobStepData(jobStepInfo, jobModel) : new JobStepData(ownerUri, jobModel),
|
||||||
this.jobName = jobModel.name;
|
jobStepInfo ? JobStepDialog.EditDialogTitle : JobStepDialog.NewDialogTitle);
|
||||||
this.server = server;
|
this.stepId = jobStepInfo ?
|
||||||
|
jobStepInfo.id : jobModel.jobSteps ?
|
||||||
|
jobModel.jobSteps.length + 1 : 1;
|
||||||
|
this.isEdit = jobStepInfo ? true : false;
|
||||||
|
this.model.dialogMode = this.isEdit ? AgentDialogMode.EDIT : AgentDialogMode.CREATE;
|
||||||
this.jobModel = jobModel;
|
this.jobModel = jobModel;
|
||||||
|
this.jobName = this.jobName ? this.jobName : this.jobModel.name;
|
||||||
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeUIComponents() {
|
private initializeUIComponents() {
|
||||||
@@ -254,6 +264,14 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
|
|||||||
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
||||||
formWrapper.loading = false;
|
formWrapper.loading = false;
|
||||||
await view.initializeModel(formWrapper);
|
await view.initializeModel(formWrapper);
|
||||||
|
|
||||||
|
// Load values for edit scenario
|
||||||
|
if (this.isEdit) {
|
||||||
|
this.nameTextBox.value = this.model.stepName;
|
||||||
|
this.typeDropdown.value = this.model.subSystem;
|
||||||
|
this.databaseDropdown.value = this.model.databaseName;
|
||||||
|
this.commandTextBox.value = this.model.command;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +308,7 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
|
|||||||
let logToTableContainer = view.modelBuilder.flexContainer()
|
let logToTableContainer = view.modelBuilder.flexContainer()
|
||||||
.withLayout({ flexFlow: 'row', justifyContent: 'space-between', width: 300 })
|
.withLayout({ flexFlow: 'row', justifyContent: 'space-between', width: 300 })
|
||||||
.withItems([this.logToTableCheckbox]).component();
|
.withItems([this.logToTableCheckbox]).component();
|
||||||
let logStepOutputHistoryCheckbox = view.modelBuilder.checkBox()
|
this.logStepOutputHistoryCheckbox = view.modelBuilder.checkBox()
|
||||||
.withProperties({ label: this.IncludeStepOutputHistoryLabel }).component();
|
.withProperties({ label: this.IncludeStepOutputHistoryLabel }).component();
|
||||||
this.userInputBox = view.modelBuilder.inputBox()
|
this.userInputBox = view.modelBuilder.inputBox()
|
||||||
.withProperties({ inputType: 'text', width: '100%' }).component();
|
.withProperties({ inputType: 'text', width: '100%' }).component();
|
||||||
@@ -315,7 +333,7 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
|
|||||||
component: appendCheckboxContainer,
|
component: appendCheckboxContainer,
|
||||||
title: ' '
|
title: ' '
|
||||||
}, {
|
}, {
|
||||||
component: logStepOutputHistoryCheckbox,
|
component: this.logStepOutputHistoryCheckbox,
|
||||||
title: ''
|
title: ''
|
||||||
}, {
|
}, {
|
||||||
component: this.userInputBox,
|
component: this.userInputBox,
|
||||||
@@ -326,7 +344,19 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
|
|||||||
|
|
||||||
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
||||||
formWrapper.loading = false;
|
formWrapper.loading = false;
|
||||||
view.initializeModel(formWrapper);
|
await view.initializeModel(formWrapper);
|
||||||
|
|
||||||
|
if (this.isEdit) {
|
||||||
|
this.successActionDropdown.value = this.model.successAction;
|
||||||
|
this.retryAttemptsBox.value = this.model.retryAttempts.toString();
|
||||||
|
this.retryIntervalBox.value = this.model.retryInterval.toString();
|
||||||
|
this.failureActionDropdown.value = this.model.failureAction;
|
||||||
|
this.outputFileNameBox.value = this.model.outputFileName;
|
||||||
|
this.appendToExistingFileCheckbox.checked = this.model.appendToLogFile;
|
||||||
|
this.logToTableCheckbox.checked = this.model.appendLogToTable;
|
||||||
|
this.logStepOutputHistoryCheckbox.checked = this.model.appendToStepHist;
|
||||||
|
this.userInputBox.value = this.model.databaseUserName;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +509,6 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
|
|||||||
this.model.jobName = this.jobName;
|
this.model.jobName = this.jobName;
|
||||||
this.model.id = this.stepId;
|
this.model.id = this.stepId;
|
||||||
this.model.server = this.server;
|
this.model.server = this.server;
|
||||||
this.model.stepName = this.nameTextBox.value;
|
|
||||||
this.model.subSystem = this.typeDropdown.value as string;
|
this.model.subSystem = this.typeDropdown.value as string;
|
||||||
this.model.databaseName = this.databaseDropdown.value as string;
|
this.model.databaseName = this.databaseDropdown.value as string;
|
||||||
this.model.script = this.commandTextBox.value;
|
this.model.script = this.commandTextBox.value;
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ export class PickScheduleDialog {
|
|||||||
private _onSuccess: vscode.EventEmitter<PickScheduleData> = new vscode.EventEmitter<PickScheduleData>();
|
private _onSuccess: vscode.EventEmitter<PickScheduleData> = new vscode.EventEmitter<PickScheduleData>();
|
||||||
public readonly onSuccess: vscode.Event<PickScheduleData> = this._onSuccess.event;
|
public readonly onSuccess: vscode.Event<PickScheduleData> = this._onSuccess.event;
|
||||||
|
|
||||||
constructor(ownerUri: string) {
|
constructor(ownerUri: string, jobName: string) {
|
||||||
this.model = new PickScheduleData(ownerUri);
|
this.model = new PickScheduleData(ownerUri, jobName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async showDialog() {
|
public async showDialog() {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { OperatorDialog } from './dialogs/operatorDialog';
|
|||||||
import { ProxyDialog } from './dialogs/proxyDialog';
|
import { ProxyDialog } from './dialogs/proxyDialog';
|
||||||
import { JobStepDialog } from './dialogs/jobStepDialog';
|
import { JobStepDialog } from './dialogs/jobStepDialog';
|
||||||
import { PickScheduleDialog } from './dialogs/pickScheduleDialog';
|
import { PickScheduleDialog } from './dialogs/pickScheduleDialog';
|
||||||
|
import { JobData } from './data/jobData';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
@@ -40,12 +41,12 @@ export class MainController {
|
|||||||
let dialog = new JobDialog(ownerUri, jobInfo);
|
let dialog = new JobDialog(ownerUri, jobInfo);
|
||||||
dialog.openDialog();
|
dialog.openDialog();
|
||||||
});
|
});
|
||||||
vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, server: string, stepId: number) => {
|
vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, server: string, jobData: JobData, jobStepInfo: sqlops.AgentJobStepInfo) => {
|
||||||
let dialog = new JobStepDialog(ownerUri, server, stepId);
|
let dialog = new JobStepDialog(ownerUri, server, jobData, jobStepInfo);
|
||||||
dialog.openDialog();
|
dialog.openDialog();
|
||||||
});
|
});
|
||||||
vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string) => {
|
vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string, jobName: string) => {
|
||||||
let dialog = new PickScheduleDialog(ownerUri);
|
let dialog = new PickScheduleDialog(ownerUri, jobName);
|
||||||
dialog.showDialog();
|
dialog.showDialog();
|
||||||
});
|
});
|
||||||
vscode.commands.registerCommand('agent.openAlertDialog', (ownerUri: string, alertInfo: sqlops.AgentAlertInfo, jobs: string[]) => {
|
vscode.commands.registerCommand('agent.openAlertDialog', (ownerUri: string, alertInfo: sqlops.AgentAlertInfo, jobs: string[]) => {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export class TestAgentService implements sqlops.AgentServicesProvider {
|
|||||||
getJobs(ownerUri: string): Thenable<sqlops.AgentJobsResult> {
|
getJobs(ownerUri: string): Thenable<sqlops.AgentJobsResult> {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
getJobHistory(ownerUri: string, jobId: string): Thenable<sqlops.AgentJobHistoryResult> {
|
getJobHistory(ownerUri: string, jobId: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult> {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
jobAction(ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
|
jobAction(ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
|
||||||
|
|||||||
@@ -83,9 +83,8 @@
|
|||||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7",
|
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7",
|
||||||
"opener": "^1.4.3",
|
"opener": "^1.4.3",
|
||||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
|
"service-downloader": "github:anthonydresser/service-downloader#0.1.5",
|
||||||
"vscode-extension-telemetry": "^0.0.5",
|
"vscode-extension-telemetry": "0.0.18",
|
||||||
"vscode-nls": "^3.2.1"
|
"vscode-nls": "^3.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ agent-base@4, agent-base@^4.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
es6-promisify "^5.0.0"
|
es6-promisify "^5.0.0"
|
||||||
|
|
||||||
applicationinsights@0.15.6:
|
applicationinsights@1.0.1:
|
||||||
version "0.15.6"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-0.15.6.tgz#201a0682c0704fe4bdd9a92d0b2cbe34d2ae5972"
|
resolved "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.1.tgz#53446b830fe8d5d619eee2a278b31d3d25030927"
|
||||||
|
dependencies:
|
||||||
|
diagnostic-channel "0.2.0"
|
||||||
|
diagnostic-channel-publishers "0.2.1"
|
||||||
|
zone.js "0.7.6"
|
||||||
|
|
||||||
base64-js@0.0.8:
|
base64-js@0.0.8:
|
||||||
version "0.0.8"
|
version "0.0.8"
|
||||||
@@ -120,6 +124,16 @@ decompress@^4.2.0:
|
|||||||
pify "^2.3.0"
|
pify "^2.3.0"
|
||||||
strip-dirs "^2.0.0"
|
strip-dirs "^2.0.0"
|
||||||
|
|
||||||
|
diagnostic-channel-publishers@0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
|
||||||
|
|
||||||
|
diagnostic-channel@0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17"
|
||||||
|
dependencies:
|
||||||
|
semver "^5.3.0"
|
||||||
|
|
||||||
end-of-stream@^1.0.0:
|
end-of-stream@^1.0.0:
|
||||||
version "1.4.1"
|
version "1.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
|
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
|
||||||
@@ -297,6 +311,10 @@ seek-bzip@^1.0.5:
|
|||||||
dependencies:
|
dependencies:
|
||||||
commander "~2.8.1"
|
commander "~2.8.1"
|
||||||
|
|
||||||
|
semver@^5.3.0:
|
||||||
|
version "5.6.0"
|
||||||
|
resolved "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||||
|
|
||||||
"service-downloader@github:anthonydresser/service-downloader#0.1.5":
|
"service-downloader@github:anthonydresser/service-downloader#0.1.5":
|
||||||
version "0.1.5"
|
version "0.1.5"
|
||||||
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/6ebb0465573cc140e461a22f334260f55ef45546"
|
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/6ebb0465573cc140e461a22f334260f55ef45546"
|
||||||
@@ -357,12 +375,11 @@ util-deprecate@~1.0.1:
|
|||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
|
||||||
vscode-extension-telemetry@^0.0.5:
|
vscode-extension-telemetry@0.0.18:
|
||||||
version "0.0.5"
|
version "0.0.18"
|
||||||
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.5.tgz#21e2abb4cbce3326e469ddbb322123b3702f3f85"
|
resolved "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327"
|
||||||
dependencies:
|
dependencies:
|
||||||
applicationinsights "0.15.6"
|
applicationinsights "1.0.1"
|
||||||
winreg "0.0.13"
|
|
||||||
|
|
||||||
vscode-jsonrpc@3.5.0:
|
vscode-jsonrpc@3.5.0:
|
||||||
version "3.5.0"
|
version "3.5.0"
|
||||||
@@ -389,10 +406,6 @@ vscode-nls@^3.2.1:
|
|||||||
version "3.2.4"
|
version "3.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||||
|
|
||||||
winreg@0.0.13:
|
|
||||||
version "0.0.13"
|
|
||||||
resolved "https://registry.yarnpkg.com/winreg/-/winreg-0.0.13.tgz#76bfe02e1dd0c9c8275fb9fdf17a9f36846e3483"
|
|
||||||
|
|
||||||
wrappy@1:
|
wrappy@1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
@@ -407,3 +420,7 @@ yauzl@^2.4.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
buffer-crc32 "~0.2.3"
|
buffer-crc32 "~0.2.3"
|
||||||
fd-slicer "~1.1.0"
|
fd-slicer "~1.1.0"
|
||||||
|
|
||||||
|
zone.js@0.7.6:
|
||||||
|
version "0.7.6"
|
||||||
|
resolved "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||||
"version": "1.5.0-alpha.43",
|
"version": "1.5.0-alpha.48",
|
||||||
"downloadFileNames": {
|
"downloadFileNames": {
|
||||||
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
"Windows_86": "win-x86-netcoreapp2.2.zip",
|
||||||
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
"Windows_64": "win-x64-netcoreapp2.2.zip",
|
||||||
"OSX": "osx-x64-netcoreapp2.1.tar.gz",
|
"OSX": "osx-x64-netcoreapp2.2.tar.gz",
|
||||||
"CentOS_7": "rhel-x64-netcoreapp2.1.tar.gz",
|
"CentOS_7": "rhel-x64-netcoreapp2.2.tar.gz",
|
||||||
"Debian_8": "rhel-x64-netcoreapp2.1.tar.gz",
|
"Debian_8": "rhel-x64-netcoreapp2.2.tar.gz",
|
||||||
"Fedora_23": "rhel-x64-netcoreapp2.1.tar.gz",
|
"Fedora_23": "rhel-x64-netcoreapp2.2.tar.gz",
|
||||||
"OpenSUSE_13_2": "rhel-x64-netcoreapp2.1.tar.gz",
|
"OpenSUSE_13_2": "rhel-x64-netcoreapp2.2.tar.gz",
|
||||||
"RHEL_7": "rhel-x64-netcoreapp2.1.tar.gz",
|
"RHEL_7": "rhel-x64-netcoreapp2.2.tar.gz",
|
||||||
"SLES_12_2": "rhel-x64-netcoreapp2.1.tar.gz",
|
"SLES_12_2": "rhel-x64-netcoreapp2.2.tar.gz",
|
||||||
"Ubuntu_14": "rhel-x64-netcoreapp2.1.tar.gz",
|
"Ubuntu_14": "rhel-x64-netcoreapp2.2.tar.gz",
|
||||||
"Ubuntu_16": "rhel-x64-netcoreapp2.1.tar.gz"
|
"Ubuntu_16": "rhel-x64-netcoreapp2.2.tar.gz"
|
||||||
},
|
},
|
||||||
"installDirectory": "../sqltoolsservice/{#platform#}/{#version#}",
|
"installDirectory": "../sqltoolsservice/{#platform#}/{#version#}",
|
||||||
"executableFiles": ["MicrosoftSqlToolsServiceLayer.exe", "MicrosoftSqlToolsServiceLayer"]
|
"executableFiles": ["MicrosoftSqlToolsServiceLayer.exe", "MicrosoftSqlToolsServiceLayer"]
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export interface AgentJobsParams {
|
|||||||
export interface AgentJobHistoryParams {
|
export interface AgentJobHistoryParams {
|
||||||
ownerUri: string;
|
ownerUri: string;
|
||||||
jobId: string;
|
jobId: string;
|
||||||
|
jobName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobActionParams {
|
export interface AgentJobActionParams {
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let getJobHistory = (ownerUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> => {
|
let getJobHistory = (ownerUri: string, jobID: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult> => {
|
||||||
let params: contracts.AgentJobHistoryParams = { ownerUri: ownerUri, jobId: jobID };
|
let params: contracts.AgentJobHistoryParams = { ownerUri: ownerUri, jobId: jobID, jobName: jobName };
|
||||||
|
|
||||||
return client.sendRequest(contracts.AgentJobHistoryRequest.type, params).then(
|
return client.sendRequest(contracts.AgentJobHistoryRequest.type, params).then(
|
||||||
r => r,
|
r => r,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "azuredatastudio",
|
"name": "azuredatastudio",
|
||||||
"version": "1.1.2",
|
"version": "1.1.4",
|
||||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Microsoft Corporation"
|
"name": "Microsoft Corporation"
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
"Microsoft.import",
|
"Microsoft.import",
|
||||||
"Microsoft.profiler",
|
"Microsoft.profiler",
|
||||||
"Microsoft.server-report",
|
"Microsoft.server-report",
|
||||||
|
"Microsoft.sql-vnext",
|
||||||
"Microsoft.whoisactive",
|
"Microsoft.whoisactive",
|
||||||
"Redgate.sql-search"
|
"Redgate.sql-search"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
import { IThemable } from 'vs/platform/theme/common/styler';
|
import { IThemable } from 'vs/platform/theme/common/styler';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { Dimension, EventType } from 'vs/base/browser/dom';
|
import { Dimension, EventType, $, addDisposableListener } from 'vs/base/browser/dom';
|
||||||
import { $, Builder } from 'vs/base/browser/builder';
|
import { $ as quickBuilder } from 'vs/base/browser/builder';
|
||||||
import { IAction } from 'vs/base/common/actions';
|
import { IAction } from 'vs/base/common/actions';
|
||||||
import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
@@ -34,8 +34,8 @@ export interface IPanelTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IInternalPanelTab extends IPanelTab {
|
interface IInternalPanelTab extends IPanelTab {
|
||||||
header: Builder;
|
header: HTMLElement;
|
||||||
label: Builder;
|
label: HTMLElement;
|
||||||
dispose(): void;
|
dispose(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,10 +49,10 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
private _tabMap = new Map<PanelTabIdentifier, IInternalPanelTab>();
|
private _tabMap = new Map<PanelTabIdentifier, IInternalPanelTab>();
|
||||||
private _shownTab: PanelTabIdentifier;
|
private _shownTab: PanelTabIdentifier;
|
||||||
public readonly headersize = 35;
|
public readonly headersize = 35;
|
||||||
private $header: Builder;
|
private header: HTMLElement;
|
||||||
private $tabList: Builder;
|
private tabList: HTMLElement;
|
||||||
private $body: Builder;
|
private body: HTMLElement;
|
||||||
private $parent: Builder;
|
private parent: HTMLElement;
|
||||||
private _actionbar: ActionBar;
|
private _actionbar: ActionBar;
|
||||||
private _currentDimensions: Dimension;
|
private _currentDimensions: Dimension;
|
||||||
private _collapsed = false;
|
private _collapsed = false;
|
||||||
@@ -63,28 +63,35 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
|
|
||||||
private tabHistory: string[] = [];
|
private tabHistory: string[] = [];
|
||||||
|
|
||||||
constructor(private container: HTMLElement, private options: IPanelOptions = defaultOptions) {
|
constructor(container: HTMLElement, private options: IPanelOptions = defaultOptions) {
|
||||||
super();
|
super();
|
||||||
this.$parent = this._register($('.tabbedPanel'));
|
this.parent = $('.tabbedPanel');
|
||||||
this.$parent.appendTo(container);
|
container.appendChild(this.parent);
|
||||||
this.$header = $('.composite.title');
|
this.header = $('.composite.title');
|
||||||
this.$tabList = $('.tabList');
|
this.tabList = $('.tabList');
|
||||||
this.$tabList.attr('role', 'tablist');
|
this.tabList.setAttribute('role', 'tablist');
|
||||||
this.$tabList.style('height', this.headersize + 'px');
|
this.tabList.style.height = this.headersize + 'px';
|
||||||
this.$header.append(this.$tabList);
|
this.header.appendChild(this.tabList);
|
||||||
let actionbarcontainer = $('.title-actions');
|
let actionbarcontainer = $('.title-actions');
|
||||||
this._actionbar = new ActionBar(actionbarcontainer.getHTMLElement());
|
this._actionbar = new ActionBar(actionbarcontainer);
|
||||||
this.$header.append(actionbarcontainer);
|
this.header.appendChild(actionbarcontainer);
|
||||||
if (options.showHeaderWhenSingleView) {
|
if (options.showHeaderWhenSingleView) {
|
||||||
this._headerVisible = true;
|
this._headerVisible = true;
|
||||||
this.$parent.append(this.$header);
|
this.parent.appendChild(this.header);
|
||||||
} else {
|
} else {
|
||||||
this._headerVisible = false;
|
this._headerVisible = false;
|
||||||
}
|
}
|
||||||
this.$body = $('.tabBody');
|
this.body = $('.tabBody');
|
||||||
this.$body.attr('role', 'tabpanel');
|
this.body.setAttribute('role', 'tabpanel');
|
||||||
this.$body.attr('tabindex', '0');
|
this.body.setAttribute('tabindex', '0');
|
||||||
this.$parent.append(this.$body);
|
this.parent.appendChild(this.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
this.header.remove();
|
||||||
|
this.tabList.remove();
|
||||||
|
this.body.remove();
|
||||||
|
this.parent.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public contains(tab: IPanelTab): boolean {
|
public contains(tab: IPanelTab): boolean {
|
||||||
@@ -99,7 +106,7 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
this.showTab(tab.identifier);
|
this.showTab(tab.identifier);
|
||||||
}
|
}
|
||||||
if (this._tabMap.size > 1 && !this._headerVisible) {
|
if (this._tabMap.size > 1 && !this._headerVisible) {
|
||||||
this.$parent.append(this.$header, 0);
|
this.parent.insertBefore(this.header, this.parent.firstChild);
|
||||||
this._headerVisible = true;
|
this._headerVisible = true;
|
||||||
this.layout(this._currentDimensions);
|
this.layout(this._currentDimensions);
|
||||||
}
|
}
|
||||||
@@ -116,30 +123,27 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
|
|
||||||
private _createTab(tab: IInternalPanelTab): void {
|
private _createTab(tab: IInternalPanelTab): void {
|
||||||
let tabHeaderElement = $('.tab-header');
|
let tabHeaderElement = $('.tab-header');
|
||||||
tabHeaderElement.attr('tabindex', '0');
|
tabHeaderElement.setAttribute('tabindex', '0');
|
||||||
tabHeaderElement.attr('role', 'tab');
|
tabHeaderElement.setAttribute('role', 'tab');
|
||||||
tabHeaderElement.attr('aria-selected', 'false');
|
tabHeaderElement.setAttribute('aria-selected', 'false');
|
||||||
tabHeaderElement.attr('aria-controls', tab.identifier);
|
tabHeaderElement.setAttribute('aria-controls', tab.identifier);
|
||||||
let tabElement = $('.tab');
|
let tabElement = $('.tab');
|
||||||
tabHeaderElement.append(tabElement);
|
tabHeaderElement.appendChild(tabElement);
|
||||||
let tabLabel = $('a.tabLabel');
|
let tabLabel = $('a.tabLabel');
|
||||||
tabLabel.safeInnerHtml(tab.title);
|
tabLabel.innerText = tab.title;
|
||||||
tabElement.append(tabLabel);
|
tabElement.appendChild(tabLabel);
|
||||||
tabHeaderElement.on(EventType.CLICK, e => this.showTab(tab.identifier));
|
addDisposableListener(tabHeaderElement, EventType.CLICK, e => this.showTab(tab.identifier));
|
||||||
tabHeaderElement.on(EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
addDisposableListener(tabHeaderElement, EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||||
let event = new StandardKeyboardEvent(e);
|
let event = new StandardKeyboardEvent(e);
|
||||||
if (event.equals(KeyCode.Enter)) {
|
if (event.equals(KeyCode.Enter)) {
|
||||||
this.showTab(tab.identifier);
|
this.showTab(tab.identifier);
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.$tabList.append(tabHeaderElement);
|
this.tabList.appendChild(tabHeaderElement);
|
||||||
tab.header = tabHeaderElement;
|
tab.header = tabHeaderElement;
|
||||||
tab.label = tabLabel;
|
tab.label = tabLabel;
|
||||||
tab.dispose = () => {
|
tab.dispose = () => { };
|
||||||
tab.header.dispose();
|
|
||||||
tab.label.dispose();
|
|
||||||
};
|
|
||||||
this._register(tab);
|
this._register(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,19 +153,20 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this._shownTab) {
|
if (this._shownTab) {
|
||||||
this._tabMap.get(this._shownTab).label.removeClass('active');
|
this._tabMap.get(this._shownTab).label.classList.remove('active');
|
||||||
this._tabMap.get(this._shownTab).header.removeClass('active').attr('aria-selected', 'false');
|
this._tabMap.get(this._shownTab).header.classList.remove('active');
|
||||||
|
this._tabMap.get(this._shownTab).header.setAttribute('aria-selected', 'false');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._shownTab = id;
|
this._shownTab = id;
|
||||||
this.tabHistory.push(id);
|
this.tabHistory.push(id);
|
||||||
this.$body.clearChildren();
|
quickBuilder(this.body).empty();
|
||||||
let tab = this._tabMap.get(this._shownTab);
|
let tab = this._tabMap.get(this._shownTab);
|
||||||
this.$body.attr('aria-labelledby', tab.identifier);
|
this.body.setAttribute('aria-labelledby', tab.identifier);
|
||||||
tab.label.addClass('active');
|
tab.label.classList.add('active');
|
||||||
tab.header.addClass('active');
|
tab.header.classList.add('active');
|
||||||
tab.header.attr('aria-selected', 'true');
|
tab.header.setAttribute('aria-selected', 'true');
|
||||||
tab.view.render(this.$body.getHTMLElement());
|
tab.view.render(this.body);
|
||||||
this._onTabChange.fire(id);
|
this._onTabChange.fire(id);
|
||||||
if (this._currentDimensions) {
|
if (this._currentDimensions) {
|
||||||
this._layoutCurrentTab(new Dimension(this._currentDimensions.width, this._currentDimensions.height - this.headersize));
|
this._layoutCurrentTab(new Dimension(this._currentDimensions.width, this._currentDimensions.height - this.headersize));
|
||||||
@@ -170,11 +175,11 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
|
|
||||||
public removeTab(tab: PanelTabIdentifier) {
|
public removeTab(tab: PanelTabIdentifier) {
|
||||||
let actualTab = this._tabMap.get(tab);
|
let actualTab = this._tabMap.get(tab);
|
||||||
actualTab.header.destroy();
|
quickBuilder(actualTab.header).destroy();
|
||||||
if (actualTab.view.remove) {
|
if (actualTab.view.remove) {
|
||||||
actualTab.view.remove();
|
actualTab.view.remove();
|
||||||
}
|
}
|
||||||
this._tabMap.get(tab).header.destroy();
|
quickBuilder(this._tabMap.get(tab).header).destroy();
|
||||||
this._tabMap.delete(tab);
|
this._tabMap.delete(tab);
|
||||||
if (this._shownTab === tab) {
|
if (this._shownTab === tab) {
|
||||||
this._shownTab = undefined;
|
this._shownTab = undefined;
|
||||||
@@ -192,7 +197,7 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.options.showHeaderWhenSingleView && this._tabMap.size === 1 && this._headerVisible) {
|
if (!this.options.showHeaderWhenSingleView && this._tabMap.size === 1 && this._headerVisible) {
|
||||||
this.$header.offDOM();
|
this.header.remove();
|
||||||
this._headerVisible = false;
|
this._headerVisible = false;
|
||||||
this.layout(this._currentDimensions);
|
this.layout(this._currentDimensions);
|
||||||
}
|
}
|
||||||
@@ -205,12 +210,12 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
public layout(dimension: Dimension): void {
|
public layout(dimension: Dimension): void {
|
||||||
if (dimension) {
|
if (dimension) {
|
||||||
this._currentDimensions = dimension;
|
this._currentDimensions = dimension;
|
||||||
this.$parent.style('height', dimension.height + 'px');
|
this.parent.style.height = dimension.height + 'px';
|
||||||
this.$parent.style('width', dimension.width + 'px');
|
this.parent.style.height = dimension.width + 'px';
|
||||||
this.$header.style('width', dimension.width + 'px');
|
this.header.style.width = dimension.width + 'px';
|
||||||
this.$body.style('width', dimension.width + 'px');
|
this.body.style.width = dimension.width + 'px';
|
||||||
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
|
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
|
||||||
this.$body.style('height', bodyHeight + 'px');
|
this.body.style.height = bodyHeight + 'px';
|
||||||
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
|
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,9 +237,9 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
|
|
||||||
this._collapsed = val === false ? false : true;
|
this._collapsed = val === false ? false : true;
|
||||||
if (this.collapsed) {
|
if (this.collapsed) {
|
||||||
this.$body.offDOM();
|
this.body.remove();
|
||||||
} else {
|
} else {
|
||||||
this.$parent.append(this.$body);
|
this.parent.appendChild(this.body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,9 @@ import * as TelemetryUtils from 'sql/common/telemetryUtilities';
|
|||||||
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||||
import { memoize, unmemoize } from 'sql/base/common/decorators';
|
import { memoize, unmemoize } from 'sql/base/common/decorators';
|
||||||
import { mixin } from 'sql/base/common/objects';
|
import { mixin } from 'sql/base/common/objects';
|
||||||
import { LegendPosition, DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
import { LegendPosition, ChartType, defaultChartConfig, IChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||||
|
|
||||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||||
import { Color } from 'vs/base/common/color';
|
|
||||||
import * as types from 'vs/base/common/types';
|
import * as types from 'vs/base/common/types';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
@@ -22,51 +21,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
|||||||
|
|
||||||
declare var Chart: any;
|
declare var Chart: any;
|
||||||
|
|
||||||
export function customMixin(destination: any, source: any, overwrite?: boolean): any {
|
|
||||||
if (types.isObject(source)) {
|
|
||||||
mixin(destination, source, overwrite, customMixin);
|
|
||||||
} else if (types.isArray(source)) {
|
|
||||||
for (let i = 0; i < source.length; i++) {
|
|
||||||
if (destination[i]) {
|
|
||||||
mixin(destination[i], source[i], overwrite, customMixin);
|
|
||||||
} else {
|
|
||||||
destination[i] = source[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
destination = source;
|
|
||||||
}
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IDataSet {
|
|
||||||
data: Array<number>;
|
|
||||||
label?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IPointDataSet {
|
|
||||||
data: Array<{ x: number | string, y: number }>;
|
|
||||||
label?: string;
|
|
||||||
fill: boolean;
|
|
||||||
backgroundColor?: Color;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IChartConfig {
|
|
||||||
colorMap?: { [column: string]: string };
|
|
||||||
labelFirstColumn?: boolean;
|
|
||||||
legendPosition?: LegendPosition;
|
|
||||||
dataDirection?: DataDirection;
|
|
||||||
columnsAsLabels?: boolean;
|
|
||||||
showTopNData?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultChartConfig: IChartConfig = {
|
|
||||||
labelFirstColumn: true,
|
|
||||||
columnsAsLabels: true,
|
|
||||||
legendPosition: LegendPosition.Top,
|
|
||||||
dataDirection: DataDirection.Vertical
|
|
||||||
};
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: ` <div style="display: block; width: 100%; height: 100%; position: relative">
|
template: ` <div style="display: block; width: 100%; height: 100%; position: relative">
|
||||||
<canvas #canvas *ngIf="_isDataAvailable && _hasInit"
|
<canvas #canvas *ngIf="_isDataAvailable && _hasInit"
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { Color } from 'vs/base/common/color';
|
||||||
|
import * as types from 'vs/base/common/types';
|
||||||
|
|
||||||
|
import { mixin } from 'sql/base/common/objects';
|
||||||
|
|
||||||
export enum ChartType {
|
export enum ChartType {
|
||||||
Bar = 'bar',
|
Bar = 'bar',
|
||||||
Doughnut = 'doughnut',
|
Doughnut = 'doughnut',
|
||||||
@@ -29,4 +34,49 @@ export enum LegendPosition {
|
|||||||
export enum DataType {
|
export enum DataType {
|
||||||
Number = 'number',
|
Number = 'number',
|
||||||
Point = 'point'
|
Point = 'point'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function customMixin(destination: any, source: any, overwrite?: boolean): any {
|
||||||
|
if (types.isObject(source)) {
|
||||||
|
mixin(destination, source, overwrite, customMixin);
|
||||||
|
} else if (types.isArray(source)) {
|
||||||
|
for (let i = 0; i < source.length; i++) {
|
||||||
|
if (destination[i]) {
|
||||||
|
mixin(destination[i], source[i], overwrite, customMixin);
|
||||||
|
} else {
|
||||||
|
destination[i] = source[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
destination = source;
|
||||||
|
}
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDataSet {
|
||||||
|
data: Array<number>;
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPointDataSet {
|
||||||
|
data: Array<{ x: number | string, y: number }>;
|
||||||
|
label?: string;
|
||||||
|
fill: boolean;
|
||||||
|
backgroundColor?: Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IChartConfig {
|
||||||
|
colorMap?: { [column: string]: string };
|
||||||
|
labelFirstColumn?: boolean;
|
||||||
|
legendPosition?: LegendPosition;
|
||||||
|
dataDirection?: DataDirection;
|
||||||
|
columnsAsLabels?: boolean;
|
||||||
|
showTopNData?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultChartConfig: IChartConfig = {
|
||||||
|
labelFirstColumn: true,
|
||||||
|
columnsAsLabels: true,
|
||||||
|
legendPosition: LegendPosition.Top,
|
||||||
|
dataDirection: DataDirection.Vertical
|
||||||
|
};
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { ChartInsight, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
import { ChartInsight } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||||
import { mixin } from 'sql/base/common/objects';
|
import { mixin } from 'sql/base/common/objects';
|
||||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
import { ChartType, IChartConfig, customMixin } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||||
|
|
||||||
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||||
|
|||||||
@@ -5,11 +5,10 @@
|
|||||||
|
|
||||||
import { mixin } from 'vs/base/common/objects';
|
import { mixin } from 'vs/base/common/objects';
|
||||||
|
|
||||||
import { defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
|
||||||
import BarChart, { IBarChartConfig } from './barChart.component';
|
import BarChart, { IBarChartConfig } from './barChart.component';
|
||||||
import { memoize, unmemoize } from 'sql/base/common/decorators';
|
import { memoize, unmemoize } from 'sql/base/common/decorators';
|
||||||
import { clone } from 'sql/base/common/objects';
|
import { clone } from 'sql/base/common/objects';
|
||||||
import { ChartType, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
import { ChartType, DataType, defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||||
|
|
||||||
export interface ILineConfig extends IBarChartConfig {
|
export interface ILineConfig extends IBarChartConfig {
|
||||||
dataType?: DataType;
|
dataType?: DataType;
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
|
||||||
import LineChart, { ILineConfig } from './lineChart.component';
|
import LineChart, { ILineConfig } from './lineChart.component';
|
||||||
import { clone } from 'sql/base/common/objects';
|
import { clone } from 'sql/base/common/objects';
|
||||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
import { ChartType, defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||||
|
|
||||||
import { mixin } from 'vs/base/common/objects';
|
import { mixin } from 'vs/base/common/objects';
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { defaultChartConfig, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
|
||||||
import LineChart, { ILineConfig } from './lineChart.component';
|
import LineChart, { ILineConfig } from './lineChart.component';
|
||||||
import { clone } from 'sql/base/common/objects';
|
import { clone } from 'sql/base/common/objects';
|
||||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
import { ChartType, defaultChartConfig, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||||
|
|
||||||
import { mixin } from 'vs/base/common/objects';
|
import { mixin } from 'vs/base/common/objects';
|
||||||
import { Color } from 'vs/base/common/color';
|
import { Color } from 'vs/base/common/color';
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export interface IJobManagementService {
|
|||||||
fireOnDidChange(): void;
|
fireOnDidChange(): void;
|
||||||
|
|
||||||
getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult>;
|
getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult>;
|
||||||
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult>;
|
getJobHistory(connectionUri: string, jobID: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult>;
|
||||||
deleteJob(connectionUri: string, job: sqlops.AgentJobInfo): Thenable<sqlops.ResultStatus>;
|
deleteJob(connectionUri: string, job: sqlops.AgentJobInfo): Thenable<sqlops.ResultStatus>;
|
||||||
|
|
||||||
deleteJobStep(connectionUri: string, step: sqlops.AgentJobStepInfo): Thenable<sqlops.ResultStatus>;
|
deleteJobStep(connectionUri: string, step: sqlops.AgentJobStepInfo): Thenable<sqlops.ResultStatus>;
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ export class JobManagementService implements IJobManagementService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
|
public getJobHistory(connectionUri: string, jobID: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult> {
|
||||||
return this._runAction(connectionUri, (runner) => {
|
return this._runAction(connectionUri, (runner) => {
|
||||||
return runner.getJobHistory(connectionUri, jobID);
|
return runner.getJobHistory(connectionUri, jobID, jobName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +142,9 @@ 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 _jobSteps: { [jobID: string]: sqlops.AgentJobStepInfo[]; } = {};
|
||||||
|
private _jobAlerts: { [jobID: string]: sqlops.AgentAlertInfo[]; } = {};
|
||||||
|
private _jobSchedules: { [jobID: string]: sqlops.AgentJobScheduleInfo[]; } = {};
|
||||||
private _runCharts: { [jobID: string]: string[]; } = {};
|
private _runCharts: { [jobID: string]: string[]; } = {};
|
||||||
private _prevJobID: string;
|
private _prevJobID: string;
|
||||||
private _serverName: string;
|
private _serverName: string;
|
||||||
@@ -176,6 +179,18 @@ export class JobCacheObject {
|
|||||||
return this._runCharts[jobID];
|
return this._runCharts[jobID];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getJobSteps(jobID: string): sqlops.AgentJobStepInfo[] {
|
||||||
|
return this._jobSteps[jobID];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getJobAlerts(jobID: string): sqlops.AgentAlertInfo[] {
|
||||||
|
return this._jobAlerts[jobID];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getJobSchedules(jobID: string): sqlops.AgentJobScheduleInfo[] {
|
||||||
|
return this._jobSchedules[jobID];
|
||||||
|
}
|
||||||
|
|
||||||
/* Setters */
|
/* Setters */
|
||||||
public set jobs(value: sqlops.AgentJobInfo[]) {
|
public set jobs(value: sqlops.AgentJobInfo[]) {
|
||||||
this._jobs = value;
|
this._jobs = value;
|
||||||
@@ -204,4 +219,16 @@ export class JobCacheObject {
|
|||||||
public set dataView(value: Slick.Data.DataView<any>) {
|
public set dataView(value: Slick.Data.DataView<any>) {
|
||||||
this._dataView = value;
|
this._dataView = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setJobSteps(jobID: string, value: sqlops.AgentJobStepInfo[]) {
|
||||||
|
this._jobSteps[jobID] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setJobAlerts(jobID: string, value: sqlops.AgentAlertInfo[]) {
|
||||||
|
this._jobAlerts[jobID] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setJobSchedules(jobID: string, value: sqlops.AgentJobScheduleInfo[]) {
|
||||||
|
this._jobSchedules[jobID] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -150,16 +150,17 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
|
|||||||
private loadHistory() {
|
private loadHistory() {
|
||||||
const self = this;
|
const self = this;
|
||||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||||
this._jobManagementService.getJobHistory(ownerUri, this._agentViewComponent.jobId).then((result) => {
|
let jobName = this._agentViewComponent.agentJobInfo.name;
|
||||||
if (result && result.jobs) {
|
this._jobManagementService.getJobHistory(ownerUri, this._agentViewComponent.jobId, jobName).then((result) => {
|
||||||
if (result.jobs.length > 0) {
|
if (result && result.histories) {
|
||||||
|
if (result.histories.length > 0) {
|
||||||
self._showPreviousRuns = true;
|
self._showPreviousRuns = true;
|
||||||
self.buildHistoryTree(self, result.jobs);
|
self.buildHistoryTree(self, result.histories);
|
||||||
if (self._agentViewComponent.showHistory) {
|
if (self._agentViewComponent.showHistory) {
|
||||||
self._cd.detectChanges();
|
self._cd.detectChanges();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, result.jobs);
|
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, result.histories);
|
||||||
self._showPreviousRuns = false;
|
self._showPreviousRuns = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export abstract class JobManagementView extends TabChild implements AfterContent
|
|||||||
if (!this.isInitialized) {
|
if (!this.isInitialized) {
|
||||||
this._showProgressWheel = true;
|
this._showProgressWheel = true;
|
||||||
this.onFirstVisible();
|
this.onFirstVisible();
|
||||||
|
this.layout();
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
}
|
}
|
||||||
} else if (this.isVisible === true && this._parentComponent.refresh === true) {
|
} else if (this.isVisible === true && this._parentComponent.refresh === true) {
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||||||
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
|
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
|
||||||
import { escape } from 'sql/base/common/strings';
|
import { escape } from 'sql/base/common/strings';
|
||||||
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
import { tableBackground, cellBackground, tableHoverBackground, jobsHeadingBackground, cellBorderColor } from 'sql/common/theme/colors';
|
import { tableBackground, cellBackground, cellBorderColor } from 'sql/common/theme/colors';
|
||||||
import { JobStepsViewRow } from 'sql/parts/jobManagement/views/jobStepsViewTree';
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -87,7 +86,10 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
|
|||||||
private sortingStylingMap: { [columnName: string]: any; } = {};
|
private sortingStylingMap: { [columnName: string]: any; } = {};
|
||||||
|
|
||||||
public jobs: sqlops.AgentJobInfo[];
|
public jobs: sqlops.AgentJobInfo[];
|
||||||
public jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = Object.create(null);
|
private jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = Object.create(null);
|
||||||
|
private jobSteps: { [jobId: string]: sqlops.AgentJobStepInfo[]; } = Object.create(null);
|
||||||
|
private jobAlerts: { [jobId: string]: sqlops.AgentAlertInfo[]; } = Object.create(null);
|
||||||
|
private jobSchedules: { [jobId: string]: sqlops.AgentJobScheduleInfo[]; } = Object.create(null);
|
||||||
public contextAction = NewJobAction;
|
public contextAction = NewJobAction;
|
||||||
|
|
||||||
@ViewChild('jobsgrid') _gridEl: ElementRef;
|
@ViewChild('jobsgrid') _gridEl: ElementRef;
|
||||||
@@ -579,10 +581,14 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
|
|||||||
private async curateJobHistory(jobs: sqlops.AgentJobInfo[], ownerUri: string) {
|
private async curateJobHistory(jobs: sqlops.AgentJobInfo[], ownerUri: string) {
|
||||||
const self = this;
|
const self = this;
|
||||||
jobs.forEach(async (job) => {
|
jobs.forEach(async (job) => {
|
||||||
await this._jobManagementService.getJobHistory(ownerUri, job.jobId).then((result) => {
|
await this._jobManagementService.getJobHistory(ownerUri, job.jobId, job.name).then((result) => {
|
||||||
if (result && result.jobs) {
|
if (result) {
|
||||||
self.jobHistories[job.jobId] = result.jobs;
|
self.jobSteps[job.jobId] = result.steps ? result.steps : [];
|
||||||
self._jobCacheObject.setJobHistory(job.jobId, result.jobs);
|
self.jobAlerts[job.jobId] = result.alerts ? result.alerts : [];
|
||||||
|
self.jobSchedules[job.jobId] = result.schedules ? result.schedules : [];
|
||||||
|
self.jobHistories[job.jobId] = result.histories ? result.histories : [];
|
||||||
|
self._jobCacheObject.setJobSteps(job.jobId, self.jobSteps[job.jobId]);
|
||||||
|
self._jobCacheObject.setJobHistory(job.jobId, self.jobHistories[job.jobId]);
|
||||||
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
||||||
let previousRuns: sqlops.AgentJobHistoryInfo[];
|
let previousRuns: sqlops.AgentJobHistoryInfo[];
|
||||||
if (jobHistories.length >= 5) {
|
if (jobHistories.length >= 5) {
|
||||||
@@ -592,7 +598,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
|
|||||||
}
|
}
|
||||||
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[jobHistories.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;
|
||||||
@@ -909,29 +915,22 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
|
|||||||
jobId = data.getItem(rowIndex - 1).jobId;
|
jobId = data.getItem(rowIndex - 1).jobId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let job: sqlops.AgentJobInfo[] = this.jobs.filter(job => {
|
let job: sqlops.AgentJobInfo[] = this.jobs.filter(job => {
|
||||||
return job.jobId === jobId;
|
return job.jobId === jobId;
|
||||||
});
|
});
|
||||||
let jobHistories = this.jobHistories[jobId];
|
|
||||||
let steps: sqlops.AgentJobStep[] = undefined;
|
// add steps
|
||||||
let schedules: sqlops.AgentJobScheduleInfo[] = undefined;
|
if (this.jobSteps && this.jobSteps[jobId]) {
|
||||||
let alerts: sqlops.AgentAlertInfo[] = undefined;
|
let steps = this.jobSteps[jobId];
|
||||||
|
job[0].JobSteps = steps;
|
||||||
|
}
|
||||||
|
let jobHistories = this.jobHistories[job[0].jobId];
|
||||||
|
let schedules: sqlops.AgentJobScheduleInfo[] = this.jobSchedules[job[0].jobId];
|
||||||
|
let alerts: sqlops.AgentAlertInfo[] = this.jobAlerts[job[0].jobId];
|
||||||
if (jobHistories && jobHistories[jobHistories.length-1]) {
|
if (jobHistories && jobHistories[jobHistories.length-1]) {
|
||||||
// add steps
|
|
||||||
steps = jobHistories[jobHistories.length-1].steps;
|
|
||||||
if (steps && steps.length > 0) {
|
|
||||||
if (!job[0].JobSteps) {
|
|
||||||
job[0].JobSteps = [];
|
|
||||||
}
|
|
||||||
if (job[0].JobSteps.length !== steps.length) {
|
|
||||||
job[0].JobSteps = [];
|
|
||||||
steps.forEach(step => {
|
|
||||||
job[0].JobSteps.push(step.stepDetails);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add schedules
|
// add schedules
|
||||||
schedules = jobHistories[jobHistories.length-1].schedules;
|
|
||||||
if (schedules && schedules.length > 0) {
|
if (schedules && schedules.length > 0) {
|
||||||
if (!job[0].JobSchedules) {
|
if (!job[0].JobSchedules) {
|
||||||
job[0].JobSchedules = [];
|
job[0].JobSchedules = [];
|
||||||
@@ -944,7 +943,6 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add alerts
|
// add alerts
|
||||||
alerts = jobHistories[jobHistories.length-1].alerts;
|
|
||||||
if (!job[0].Alerts) {
|
if (!job[0].Alerts) {
|
||||||
job[0].Alerts = [];
|
job[0].Alerts = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export default class EditorComponent extends ComponentBase implements IComponent
|
|||||||
private _languageMode: string;
|
private _languageMode: string;
|
||||||
private _uri: string;
|
private _uri: string;
|
||||||
private _isAutoResizable: boolean;
|
private _isAutoResizable: boolean;
|
||||||
|
private _minimumHeight: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||||
@@ -79,6 +80,9 @@ export default class EditorComponent extends ComponentBase implements IComponent
|
|||||||
this._register(this._editorModel.onDidChangeContent(e => {
|
this._register(this._editorModel.onDidChangeContent(e => {
|
||||||
this.content = this._editorModel.getValue();
|
this.content = this._editorModel.getValue();
|
||||||
if (this._isAutoResizable) {
|
if (this._isAutoResizable) {
|
||||||
|
if (this._minimumHeight) {
|
||||||
|
this._editor.setMinimumHeight(this._minimumHeight);
|
||||||
|
}
|
||||||
this._editor.setHeightToScrollHeight();
|
this._editor.setHeightToScrollHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +113,8 @@ export default class EditorComponent extends ComponentBase implements IComponent
|
|||||||
|
|
||||||
let height: number = this.convertSizeToNumber(this.height);
|
let height: number = this.convertSizeToNumber(this.height);
|
||||||
if (this._isAutoResizable) {
|
if (this._isAutoResizable) {
|
||||||
height = this._editor.scrollHeight;
|
this._editor.setHeightToScrollHeight();
|
||||||
|
height = Math.max(this._editor.scrollHeight, this._minimumHeight ? this._minimumHeight : 0);
|
||||||
}
|
}
|
||||||
this._editor.layout(new DOM.Dimension(
|
this._editor.layout(new DOM.Dimension(
|
||||||
width && width > 0 ? width : DOM.getContentWidth(this._el.nativeElement),
|
width && width > 0 ? width : DOM.getContentWidth(this._el.nativeElement),
|
||||||
@@ -152,6 +157,7 @@ export default class EditorComponent extends ComponentBase implements IComponent
|
|||||||
// Intentionally always updating editorUri as it's wiped out by parent setProperties call.
|
// Intentionally always updating editorUri as it's wiped out by parent setProperties call.
|
||||||
this.editorUri = this._uri;
|
this.editorUri = this._uri;
|
||||||
this._isAutoResizable = this.isAutoResizable;
|
this._isAutoResizable = this.isAutoResizable;
|
||||||
|
this._minimumHeight = this.minimumHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSS-bound properties
|
// CSS-bound properties
|
||||||
@@ -179,6 +185,14 @@ export default class EditorComponent extends ComponentBase implements IComponent
|
|||||||
this.setPropertyFromUI<sqlops.EditorProperties, boolean>((properties, isAutoResizable) => { properties.isAutoResizable = isAutoResizable; }, newValue);
|
this.setPropertyFromUI<sqlops.EditorProperties, boolean>((properties, isAutoResizable) => { properties.isAutoResizable = isAutoResizable; }, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get minimumHeight(): number {
|
||||||
|
return this.getPropertyOrDefault<sqlops.EditorProperties, number>((props) => props.minimumHeight, this._editor.minimumHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set minimumHeight(newValue: number) {
|
||||||
|
this.setPropertyFromUI<sqlops.EditorProperties, number>((properties, minimumHeight) => { properties.minimumHeight = minimumHeight; }, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
public get editorUri(): string {
|
public get editorUri(): string {
|
||||||
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.editorUri, '');
|
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.editorUri, '');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export class QueryTextEditor extends BaseTextEditor {
|
|||||||
public static ID = 'modelview.editors.textEditor';
|
public static ID = 'modelview.editors.textEditor';
|
||||||
private _dimension: DOM.Dimension;
|
private _dimension: DOM.Dimension;
|
||||||
private _config: editorCommon.IConfiguration;
|
private _config: editorCommon.IConfiguration;
|
||||||
|
private _minHeight: number;
|
||||||
constructor(
|
constructor(
|
||||||
@ITelemetryService telemetryService: ITelemetryService,
|
@ITelemetryService telemetryService: ITelemetryService,
|
||||||
@IInstantiationService instantiationService: IInstantiationService,
|
@IInstantiationService instantiationService: IInstantiationService,
|
||||||
@@ -116,6 +117,11 @@ export class QueryTextEditor extends BaseTextEditor {
|
|||||||
this._config = new Configuration(undefined, editorWidget.getDomNode());
|
this._config = new Configuration(undefined, editorWidget.getDomNode());
|
||||||
}
|
}
|
||||||
let editorHeightUsingLines = this._config.editor.lineHeight * editorWidget.getModel().getLineCount();
|
let editorHeightUsingLines = this._config.editor.lineHeight * editorWidget.getModel().getLineCount();
|
||||||
this.setHeight(editorHeightUsingLines);
|
let editorHeightUsingMinHeight = Math.max(editorHeightUsingLines, this._minHeight);
|
||||||
|
this.setHeight(editorHeightUsingMinHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setMinimumHeight(height: number) : void {
|
||||||
|
this._minHeight = height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ export class ActiveConnectionsFilterAction extends Action {
|
|||||||
public static LABEL = localize('activeConnections', 'Show Active Connections');
|
public static LABEL = localize('activeConnections', 'Show Active Connections');
|
||||||
private static enabledClass = 'active-connections-action';
|
private static enabledClass = 'active-connections-action';
|
||||||
private static disabledClass = 'icon server-page';
|
private static disabledClass = 'icon server-page';
|
||||||
private static clearAllLabel = localize('clearAll', 'Clear All');
|
private static showAllConnectionsLabel = localize('showAllConnections', 'Show All Connections');
|
||||||
private _isSet: boolean;
|
private _isSet: boolean;
|
||||||
public static readonly ACTIVE = 'active';
|
public static readonly ACTIVE = 'active';
|
||||||
public get isSet(): boolean {
|
public get isSet(): boolean {
|
||||||
@@ -249,7 +249,7 @@ export class ActiveConnectionsFilterAction extends Action {
|
|||||||
// show active connections in the tree
|
// show active connections in the tree
|
||||||
this.view.showFilteredTree(ActiveConnectionsFilterAction.ACTIVE);
|
this.view.showFilteredTree(ActiveConnectionsFilterAction.ACTIVE);
|
||||||
this.isSet = true;
|
this.isSet = true;
|
||||||
this.label = ActiveConnectionsFilterAction.clearAllLabel;
|
this.label = ActiveConnectionsFilterAction.showAllConnectionsLabel;
|
||||||
} else {
|
} else {
|
||||||
// show full tree
|
// show full tree
|
||||||
this.view.refreshTree();
|
this.view.refreshTree();
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export enum ControlType {
|
|||||||
combo,
|
combo,
|
||||||
numberInput,
|
numberInput,
|
||||||
input,
|
input,
|
||||||
checkbox
|
checkbox,
|
||||||
|
dateInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IChartOption {
|
export interface IChartOption {
|
||||||
@@ -115,6 +116,20 @@ const xAxisMaxInput: IChartOption = {
|
|||||||
default: undefined
|
default: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const xAxisMinDateInput: IChartOption = {
|
||||||
|
label: localize('xAxisMinDate', 'X Axis Minimum Date'),
|
||||||
|
type: ControlType.dateInput,
|
||||||
|
configEntry: 'xAxisMin',
|
||||||
|
default: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const xAxisMaxDateInput: IChartOption = {
|
||||||
|
label: localize('xAxisMaxDate', 'X Axis Maximum Date'),
|
||||||
|
type: ControlType.dateInput,
|
||||||
|
configEntry: 'xAxisMax',
|
||||||
|
default: undefined
|
||||||
|
};
|
||||||
|
|
||||||
const dataTypeInput: IChartOption = {
|
const dataTypeInput: IChartOption = {
|
||||||
label: localize('dataTypeLabel', 'Data Type'),
|
label: localize('dataTypeLabel', 'Data Type'),
|
||||||
type: ControlType.combo,
|
type: ControlType.combo,
|
||||||
@@ -150,7 +165,11 @@ export const ChartOptions: IChartOptions = {
|
|||||||
[ChartType.TimeSeries]: [
|
[ChartType.TimeSeries]: [
|
||||||
legendInput,
|
legendInput,
|
||||||
yAxisLabelInput,
|
yAxisLabelInput,
|
||||||
xAxisLabelInput
|
yAxisMinInput,
|
||||||
|
yAxisMaxInput,
|
||||||
|
xAxisLabelInput,
|
||||||
|
xAxisMinDateInput,
|
||||||
|
xAxisMaxDateInput,
|
||||||
],
|
],
|
||||||
[ChartType.Bar]: [
|
[ChartType.Bar]: [
|
||||||
dataDirectionOption,
|
dataDirectionOption,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class ChartTab implements IPanelTab {
|
|||||||
public readonly identifier = 'ChartTab';
|
public readonly identifier = 'ChartTab';
|
||||||
public readonly view: ChartView;
|
public readonly view: ChartView;
|
||||||
|
|
||||||
constructor(@IInstantiationService instantiationService: IInstantiationService) {
|
constructor( @IInstantiationService instantiationService: IInstantiationService) {
|
||||||
this.view = instantiationService.createInstance(ChartView);
|
this.view = instantiationService.createInstance(ChartView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,11 @@ export class ChartTab implements IPanelTab {
|
|||||||
this.view.queryRunner = runner;
|
this.view.queryRunner = runner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public chart(dataId: { batchId: number, resultId: number}): void {
|
public chart(dataId: { batchId: number, resultId: number }): void {
|
||||||
this.view.chart(dataId);
|
this.view.chart(dataId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
this.view.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
|
|||||||
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||||
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||||
import { Builder } from 'vs/base/browser/builder';
|
import { Builder } from 'vs/base/browser/builder';
|
||||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { attachSelectBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
import { attachSelectBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
@@ -43,7 +43,7 @@ declare class Proxy {
|
|||||||
|
|
||||||
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
|
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
|
||||||
|
|
||||||
export class ChartView implements IPanelView {
|
export class ChartView extends Disposable implements IPanelView {
|
||||||
private insight: Insight;
|
private insight: Insight;
|
||||||
private _queryRunner: QueryRunner;
|
private _queryRunner: QueryRunner;
|
||||||
private _data: IInsightData;
|
private _data: IInsightData;
|
||||||
@@ -82,6 +82,7 @@ export class ChartView implements IPanelView {
|
|||||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||||
@IContextMenuService contextMenuService: IContextMenuService
|
@IContextMenuService contextMenuService: IContextMenuService
|
||||||
) {
|
) {
|
||||||
|
super();
|
||||||
this.taskbarContainer = $('div.taskbar-container');
|
this.taskbarContainer = $('div.taskbar-container');
|
||||||
this.taskbar = new Taskbar(this.taskbarContainer, contextMenuService);
|
this.taskbar = new Taskbar(this.taskbarContainer, contextMenuService);
|
||||||
this.optionsControl = $('div.options-container');
|
this.optionsControl = $('div.options-container');
|
||||||
@@ -324,6 +325,24 @@ export class ChartView implements IPanelView {
|
|||||||
};
|
};
|
||||||
this.optionDisposables.push(attachInputBoxStyler(numberInput, this._themeService));
|
this.optionDisposables.push(attachInputBoxStyler(numberInput, this._themeService));
|
||||||
break;
|
break;
|
||||||
|
case ControlType.dateInput:
|
||||||
|
let dateInput = new InputBox(optionContainer, this._contextViewService, { type: 'date' });
|
||||||
|
dateInput.value = value || '';
|
||||||
|
dateInput.onDidChange(e => {
|
||||||
|
if (this.options[option.configEntry] !== e) {
|
||||||
|
this.options[option.configEntry] = e;
|
||||||
|
if (this.insight) {
|
||||||
|
this.insight.options = this.options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setFunc = (val: string) => {
|
||||||
|
if (!isUndefinedOrNull(val)) {
|
||||||
|
dateInput.value = val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.optionDisposables.push(attachInputBoxStyler(dateInput, this._themeService));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
this.optionMap[option.configEntry] = { element: optionContainer, set: setFunc };
|
this.optionMap[option.configEntry] = { element: optionContainer, set: setFunc };
|
||||||
container.appendChild(optionContainer);
|
container.appendChild(optionContainer);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import { Chart as ChartJs } from 'chart.js';
|
import { Chart as ChartJs } from 'chart.js';
|
||||||
|
|
||||||
import { mixin } from 'vs/base/common/objects';
|
import { mixin } from 'sql/base/common/objects';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||||
import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry';
|
import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry';
|
||||||
@@ -15,10 +15,28 @@ import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
|||||||
|
|
||||||
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||||
import { IInsightOptions, IInsight } from './interfaces';
|
import { IInsightOptions, IInsight } from './interfaces';
|
||||||
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
import { ChartType, DataDirection, LegendPosition, DataType, IPointDataSet, customMixin } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||||
|
|
||||||
const noneLineGraphs = [ChartType.Doughnut, ChartType.Pie];
|
const noneLineGraphs = [ChartType.Doughnut, ChartType.Pie];
|
||||||
|
|
||||||
|
const timeSeriesScales = {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
display: true,
|
||||||
|
ticks: {
|
||||||
|
autoSkip: false,
|
||||||
|
maxRotation: 45,
|
||||||
|
minRotation: 45
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
|
||||||
|
yAxes: [{
|
||||||
|
display: true,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const defaultOptions: IInsightOptions = {
|
const defaultOptions: IInsightOptions = {
|
||||||
type: ChartType.Bar,
|
type: ChartType.Bar,
|
||||||
dataDirection: DataDirection.Horizontal
|
dataDirection: DataDirection.Horizontal
|
||||||
@@ -30,6 +48,8 @@ export class Graph implements IInsight {
|
|||||||
private chartjs: ChartJs;
|
private chartjs: ChartJs;
|
||||||
private _data: IInsightData;
|
private _data: IInsightData;
|
||||||
|
|
||||||
|
private originalType: ChartType;
|
||||||
|
|
||||||
public static readonly types = [ChartType.Bar, ChartType.Doughnut, ChartType.HorizontalBar, ChartType.Line, ChartType.Pie, ChartType.Scatter, ChartType.TimeSeries];
|
public static readonly types = [ChartType.Bar, ChartType.Doughnut, ChartType.HorizontalBar, ChartType.Line, ChartType.Pie, ChartType.Scatter, ChartType.TimeSeries];
|
||||||
public readonly types = Graph.types;
|
public readonly types = Graph.types;
|
||||||
|
|
||||||
@@ -83,37 +103,51 @@ export class Graph implements IInsight {
|
|||||||
labels = data.rows.map(row => row[0]);
|
labels = data.rows.map(row => row[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.options.dataDirection === DataDirection.Horizontal) {
|
if (this.originalType === ChartType.TimeSeries) {
|
||||||
if (this.options.labelFirstColumn) {
|
let dataSetMap: { [label: string]: IPointDataSet } = {};
|
||||||
chartData = data.rows.map((row) => {
|
this._data.rows.map(row => {
|
||||||
return {
|
if (row && row.length >= 3) {
|
||||||
data: row.map(item => Number(item)).slice(1),
|
let legend = row[0];
|
||||||
label: row[0]
|
if (!dataSetMap[legend]) {
|
||||||
};
|
dataSetMap[legend] = { label: legend, data: [], fill: false };
|
||||||
});
|
}
|
||||||
} else {
|
dataSetMap[legend].data.push({ x: row[1], y: Number(row[2]) });
|
||||||
chartData = data.rows.map((row, i) => {
|
}
|
||||||
return {
|
});
|
||||||
data: row.map(item => Number(item)),
|
chartData = Object.values(dataSetMap);
|
||||||
label: localize('series', 'Series {0}', i)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (this.options.columnsAsLabels) {
|
if (this.options.dataDirection === DataDirection.Horizontal) {
|
||||||
chartData = data.rows[0].slice(1).map((row, i) => {
|
if (this.options.labelFirstColumn) {
|
||||||
return {
|
chartData = data.rows.map((row) => {
|
||||||
data: data.rows.map(row => Number(row[i + 1])),
|
return {
|
||||||
label: data.columns[i + 1]
|
data: row.map(item => Number(item)).slice(1),
|
||||||
};
|
label: row[0]
|
||||||
});
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
chartData = data.rows.map((row, i) => {
|
||||||
|
return {
|
||||||
|
data: row.map(item => Number(item)),
|
||||||
|
label: localize('series', 'Series {0}', i)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
chartData = data.rows[0].slice(1).map((row, i) => {
|
if (this.options.columnsAsLabels) {
|
||||||
return {
|
chartData = data.rows[0].slice(1).map((row, i) => {
|
||||||
data: data.rows.map(row => Number(row[i + 1])),
|
return {
|
||||||
label: localize('series', 'Series {0}', i + 1)
|
data: data.rows.map(row => Number(row[i + 1])),
|
||||||
};
|
label: data.columns[i + 1]
|
||||||
});
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
chartData = data.rows[0].slice(1).map((row, i) => {
|
||||||
|
return {
|
||||||
|
data: data.rows.map(row => Number(row[i + 1])),
|
||||||
|
label: localize('series', 'Series {0}', i + 1)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,6 +221,35 @@ export class Graph implements IInsight {
|
|||||||
color: gridLines
|
color: gridLines
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
if (this.originalType === ChartType.TimeSeries) {
|
||||||
|
retval = mixin(retval, timeSeriesScales, true, customMixin);
|
||||||
|
if (options.xAxisMax) {
|
||||||
|
retval = mixin(retval, {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
max: options.xAxisMax
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}, true, customMixin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.xAxisMin) {
|
||||||
|
retval = mixin(retval, {
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
min: options.xAxisMin
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}, true, customMixin);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retval.legend = <ChartJs.ChartLegendOptions>{
|
retval.legend = <ChartJs.ChartLegendOptions>{
|
||||||
@@ -208,6 +271,12 @@ export class Graph implements IInsight {
|
|||||||
|
|
||||||
public set options(options: IInsightOptions) {
|
public set options(options: IInsightOptions) {
|
||||||
this._options = options;
|
this._options = options;
|
||||||
|
this.originalType = options.type as ChartType;
|
||||||
|
if (this.options.type === ChartType.TimeSeries) {
|
||||||
|
this.options.type = ChartType.Line;
|
||||||
|
this.options.dataType = DataType.Point;
|
||||||
|
this.options.dataDirection = DataDirection.Horizontal;
|
||||||
|
}
|
||||||
this.data = this._data;
|
this.data = this._data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import { Graph } from './graphInsight';
|
import { Graph } from './graphInsight';
|
||||||
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||||
import { DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
import { DataDirection, ChartType, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||||
import { ImageInsight } from './imageInsight';
|
import { ImageInsight } from './imageInsight';
|
||||||
import { TableInsight } from './tableInsight';
|
import { TableInsight } from './tableInsight';
|
||||||
import { IInsightOptions, IInsight, InsightType, IInsightCtor } from './interfaces';
|
import { IInsightOptions, IInsight, InsightType, IInsightCtor } from './interfaces';
|
||||||
@@ -16,6 +16,7 @@ import { CountInsight } from './countInsight';
|
|||||||
import { Builder } from 'vs/base/browser/builder';
|
import { Builder } from 'vs/base/browser/builder';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { Dimension } from 'vs/base/browser/dom';
|
import { Dimension } from 'vs/base/browser/dom';
|
||||||
|
import { deepClone } from 'vs/base/common/objects';
|
||||||
|
|
||||||
const defaultOptions: IInsightOptions = {
|
const defaultOptions: IInsightOptions = {
|
||||||
type: ChartType.Bar,
|
type: ChartType.Bar,
|
||||||
@@ -47,13 +48,13 @@ export class Insight {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public set options(val: IInsightOptions) {
|
public set options(val: IInsightOptions) {
|
||||||
this._options = val;
|
this._options = deepClone(val);
|
||||||
if (this.insight) {
|
if (this.insight) {
|
||||||
// check to see if we need to change the insight type
|
// check to see if we need to change the insight type
|
||||||
if (!this.insight.types.includes(val.type)) {
|
if (!this.insight.types.includes(this.options.type)) {
|
||||||
this.buildInsight();
|
this.buildInsight();
|
||||||
} else {
|
} else {
|
||||||
this.insight.options = val;
|
this.insight.options = this.options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugi
|
|||||||
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
|
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
|
import * as pretty from 'pretty-data';
|
||||||
|
|
||||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||||
@@ -477,7 +478,27 @@ class GridTable<T> extends Disposable implements IView {
|
|||||||
if (column && (column.isXml || column.isJson)) {
|
if (column && (column.isXml || column.isJson)) {
|
||||||
this.runner.getQueryRows(event.cell.row, 1, this.resultSet.batchId, this.resultSet.id).then(d => {
|
this.runner.getQueryRows(event.cell.row, 1, this.resultSet.batchId, this.resultSet.id).then(d => {
|
||||||
let value = d.resultSubset.rows[0][event.cell.cell - 1];
|
let value = d.resultSubset.rows[0][event.cell.cell - 1];
|
||||||
let input = this.untitledEditorService.createOrGet(undefined, column.isXml ? 'xml' : 'json', value.displayValue);
|
let content = value.displayValue;
|
||||||
|
if (column.isXml) {
|
||||||
|
try {
|
||||||
|
content = pretty.pd.xml(content);
|
||||||
|
} catch (e) {
|
||||||
|
// If Xml fails to parse, fall back on original Xml content
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let jsonContent: string = undefined;
|
||||||
|
try {
|
||||||
|
jsonContent = JSON.parse(content);
|
||||||
|
} catch (e) {
|
||||||
|
// If Json fails to parse, fall back on original Json content
|
||||||
|
}
|
||||||
|
if (jsonContent) {
|
||||||
|
// If Json content was valid and parsed, pretty print content to a string
|
||||||
|
content = JSON.stringify(jsonContent, undefined, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let input = this.untitledEditorService.createOrGet(undefined, column.isXml ? 'xml' : 'json', content);
|
||||||
this.editorService.openEditor(input);
|
this.editorService.openEditor(input);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ import { PanelViewlet } from 'vs/workbench/browser/parts/views/panelViewlet';
|
|||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import * as DOM from 'vs/base/browser/dom';
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
import { once, anyEvent } from 'vs/base/common/event';
|
import { once, anyEvent } from 'vs/base/common/event';
|
||||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
class ResultsView implements IPanelView {
|
class ResultsView extends Disposable implements IPanelView {
|
||||||
private panelViewlet: PanelViewlet;
|
private panelViewlet: PanelViewlet;
|
||||||
private gridPanel: GridPanel;
|
private gridPanel: GridPanel;
|
||||||
private messagePanel: MessagePanel;
|
private messagePanel: MessagePanel;
|
||||||
@@ -30,10 +30,10 @@ class ResultsView implements IPanelView {
|
|||||||
private _state: ResultsViewState;
|
private _state: ResultsViewState;
|
||||||
|
|
||||||
constructor(private instantiationService: IInstantiationService) {
|
constructor(private instantiationService: IInstantiationService) {
|
||||||
|
super();
|
||||||
this.panelViewlet = this.instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
|
this.panelViewlet = this._register(this.instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false }));
|
||||||
this.gridPanel = this.instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' });
|
this.gridPanel = this._register(this.instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' }));
|
||||||
this.messagePanel = this.instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' });
|
this.messagePanel = this._register(this.instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' }));
|
||||||
this.gridPanel.render();
|
this.gridPanel.render();
|
||||||
this.messagePanel.render();
|
this.messagePanel.render();
|
||||||
this.panelViewlet.create(this.container).then(() => {
|
this.panelViewlet.create(this.container).then(() => {
|
||||||
@@ -76,7 +76,7 @@ class ResultsView implements IPanelView {
|
|||||||
this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize);
|
this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize);
|
||||||
}
|
}
|
||||||
this.panelViewlet.resizePanel(this.gridPanel, panelSize);
|
this.panelViewlet.resizePanel(this.gridPanel, panelSize);
|
||||||
})
|
});
|
||||||
// once the user changes the sash we should stop trying to resize the grid
|
// once the user changes the sash we should stop trying to resize the grid
|
||||||
once(this.panelViewlet.onDidSashChange)(e => {
|
once(this.panelViewlet.onDidSashChange)(e => {
|
||||||
this.needsGridResize = false;
|
this.needsGridResize = false;
|
||||||
@@ -147,9 +147,13 @@ class ResultsTab implements IPanelTab {
|
|||||||
public set queryRunner(runner: QueryRunner) {
|
public set queryRunner(runner: QueryRunner) {
|
||||||
this.view.queryRunner = runner;
|
this.view.queryRunner = runner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
dispose(this.view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueryResultsView {
|
export class QueryResultsView extends Disposable {
|
||||||
private _panelView: TabbedPanel;
|
private _panelView: TabbedPanel;
|
||||||
private _input: QueryResultsInput;
|
private _input: QueryResultsInput;
|
||||||
private resultsTab: ResultsTab;
|
private resultsTab: ResultsTab;
|
||||||
@@ -163,16 +167,17 @@ export class QueryResultsView {
|
|||||||
@IInstantiationService instantiationService: IInstantiationService,
|
@IInstantiationService instantiationService: IInstantiationService,
|
||||||
@IQueryModelService private queryModelService: IQueryModelService
|
@IQueryModelService private queryModelService: IQueryModelService
|
||||||
) {
|
) {
|
||||||
this.resultsTab = new ResultsTab(instantiationService);
|
super();
|
||||||
this.chartTab = new ChartTab(instantiationService);
|
this.resultsTab = this._register(new ResultsTab(instantiationService));
|
||||||
this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false });
|
this.chartTab = this._register(new ChartTab(instantiationService));
|
||||||
this.qpTab = new QueryPlanTab();
|
this._panelView = this._register(new TabbedPanel(container, { showHeaderWhenSingleView: false }));
|
||||||
|
this.qpTab = this._register(new QueryPlanTab());
|
||||||
this._panelView.pushTab(this.resultsTab);
|
this._panelView.pushTab(this.resultsTab);
|
||||||
this._panelView.onTabChange(e => {
|
this._register(this._panelView.onTabChange(e => {
|
||||||
if (this.input) {
|
if (this.input) {
|
||||||
this.input.state.activeTab = e;
|
this.input.state.activeTab = e;
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
public style() {
|
public style() {
|
||||||
@@ -203,12 +208,14 @@ export class QueryResultsView {
|
|||||||
if (!this._panelView.contains(this.qpTab)) {
|
if (!this._panelView.contains(this.qpTab)) {
|
||||||
this._panelView.pushTab(this.qpTab);
|
this._panelView.pushTab(this.qpTab);
|
||||||
}
|
}
|
||||||
} else if (queryRunner.isQueryPlan) {
|
|
||||||
let disp = queryRunner.onResultSet(() => {
|
|
||||||
this.showPlan(queryRunner.planXml);
|
|
||||||
disp.dispose();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
this.runnerDisposables.push(queryRunner.onQueryEnd(() => {
|
||||||
|
if (queryRunner.isQueryPlan) {
|
||||||
|
queryRunner.planXml.then(e => {
|
||||||
|
this.showPlan(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
if (this.input.state.activeTab) {
|
if (this.input.state.activeTab) {
|
||||||
this._panelView.showTab(this.input.state.activeTab);
|
this._panelView.showTab(this.input.state.activeTab);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { Emitter, Event } from 'vs/base/common/event';
|
|||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
|
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
|
||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
|
import { Deferred } from 'sql/base/common/promise';
|
||||||
|
|
||||||
export interface IEditSessionReadyEvent {
|
export interface IEditSessionReadyEvent {
|
||||||
ownerUri: string;
|
ownerUri: string;
|
||||||
@@ -69,11 +70,11 @@ export default class QueryRunner {
|
|||||||
private _hasCompleted: boolean = false;
|
private _hasCompleted: boolean = false;
|
||||||
private _batchSets: sqlops.BatchSummary[] = [];
|
private _batchSets: sqlops.BatchSummary[] = [];
|
||||||
private _eventEmitter = new EventEmitter();
|
private _eventEmitter = new EventEmitter();
|
||||||
private _isQueryPlan: boolean;
|
|
||||||
|
|
||||||
|
private _isQueryPlan: boolean;
|
||||||
public get isQueryPlan(): boolean { return this._isQueryPlan; }
|
public get isQueryPlan(): boolean { return this._isQueryPlan; }
|
||||||
private _planXml: string;
|
private _planXml = new Deferred<string>();
|
||||||
public get planXml(): string { return this._planXml; }
|
public get planXml(): Thenable<string> { return this._planXml.promise; }
|
||||||
|
|
||||||
private _onMessage = new Emitter<sqlops.IResultMessage>();
|
private _onMessage = new Emitter<sqlops.IResultMessage>();
|
||||||
private _debouncedMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => {
|
private _debouncedMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => {
|
||||||
@@ -183,6 +184,7 @@ export default class QueryRunner {
|
|||||||
this._echoedResultSet.clear();
|
this._echoedResultSet.clear();
|
||||||
this._debouncedMessage.clear();
|
this._debouncedMessage.clear();
|
||||||
this._debouncedResultSet.clear();
|
this._debouncedResultSet.clear();
|
||||||
|
this._planXml = new Deferred<string>();
|
||||||
let ownerUri = this.uri;
|
let ownerUri = this.uri;
|
||||||
this._batchSets = [];
|
this._batchSets = [];
|
||||||
this._hasCompleted = false;
|
this._hasCompleted = false;
|
||||||
@@ -342,7 +344,11 @@ export default class QueryRunner {
|
|||||||
}
|
}
|
||||||
// handle getting queryPlanxml if we need too
|
// handle getting queryPlanxml if we need too
|
||||||
if (this.isQueryPlan) {
|
if (this.isQueryPlan) {
|
||||||
this.getQueryRows(0, 1, 0, 0).then(e => this._planXml = e.resultSubset.rows[0][0].displayValue);
|
// check if this result has show plan, this needs work, it won't work for any other provider
|
||||||
|
let hasShowPlan = !!result.resultSetSummary.columnInfo.find(e => e.columnName === 'Microsoft SQL Server 2005 XML Showplan');
|
||||||
|
if (hasShowPlan) {
|
||||||
|
this.getQueryRows(0, 1, result.resultSetSummary.batchId, result.resultSetSummary.id).then(e => this._planXml.resolve(e.resultSubset.rows[0][0].displayValue));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (batchSet) {
|
if (batchSet) {
|
||||||
// Store the result set in the batch and emit that a result set has completed
|
// Store the result set in the batch and emit that a result set has completed
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import { IPanelView, IPanelTab } from 'sql/base/browser/ui/panel/panel';
|
|||||||
|
|
||||||
import { Dimension } from 'vs/base/browser/dom';
|
import { Dimension } from 'vs/base/browser/dom';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import * as UUID from 'vs/base/common/uuid';
|
|
||||||
import { Builder } from 'vs/base/browser/builder';
|
import { Builder } from 'vs/base/browser/builder';
|
||||||
|
import { dispose, Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
export class QueryPlanState {
|
export class QueryPlanState {
|
||||||
xml: string;
|
xml: string;
|
||||||
@@ -25,6 +25,10 @@ export class QueryPlanTab implements IPanelTab {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.view = new QueryPlanView();
|
this.view = new QueryPlanView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public dispose() {
|
||||||
|
dispose(this.view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueryPlanView implements IPanelView {
|
export class QueryPlanView implements IPanelView {
|
||||||
@@ -41,10 +45,18 @@ export class QueryPlanView implements IPanelView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
container.appendChild(this.container);
|
container.appendChild(this.container);
|
||||||
container.style.overflow = 'scroll';
|
this.container.style.overflow = 'scroll';
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.container.remove();
|
||||||
|
this.qp = undefined;
|
||||||
|
this.container = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public layout(dimension: Dimension): void {
|
public layout(dimension: Dimension): void {
|
||||||
|
this.container.style.width = dimension.width + 'px';
|
||||||
|
this.container.style.height = dimension.height + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
public showPlan(xml: string) {
|
public showPlan(xml: string) {
|
||||||
|
|||||||
19
src/sql/sqlops.d.ts
vendored
19
src/sql/sqlops.d.ts
vendored
@@ -1266,6 +1266,16 @@ declare module 'sqlops' {
|
|||||||
Last = 16
|
Last = 16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum JobExecutionStatus {
|
||||||
|
Executing = 1,
|
||||||
|
WaitingForWorkerThread = 2,
|
||||||
|
BetweenRetries = 3,
|
||||||
|
Idle = 4,
|
||||||
|
Suspended = 5,
|
||||||
|
WaitingForStepToFinish = 6,
|
||||||
|
PerformingCompletionAction = 7
|
||||||
|
}
|
||||||
|
|
||||||
export interface AgentJobInfo {
|
export interface AgentJobInfo {
|
||||||
name: string;
|
name: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
@@ -1371,8 +1381,6 @@ declare module 'sqlops' {
|
|||||||
retriesAttempted: string;
|
retriesAttempted: string;
|
||||||
server: string;
|
server: string;
|
||||||
steps: AgentJobStep[];
|
steps: AgentJobStep[];
|
||||||
schedules: AgentJobScheduleInfo[];
|
|
||||||
alerts: AgentAlertInfo[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentProxyInfo {
|
export interface AgentProxyInfo {
|
||||||
@@ -1441,7 +1449,10 @@ declare module 'sqlops' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentJobHistoryResult extends ResultStatus {
|
export interface AgentJobHistoryResult extends ResultStatus {
|
||||||
jobs: AgentJobHistoryInfo[];
|
histories: AgentJobHistoryInfo[];
|
||||||
|
steps: AgentJobStepInfo[];
|
||||||
|
schedules: AgentJobScheduleInfo[];
|
||||||
|
alerts: AgentAlertInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateAgentJobResult extends ResultStatus {
|
export interface CreateAgentJobResult extends ResultStatus {
|
||||||
@@ -1529,7 +1540,7 @@ declare module 'sqlops' {
|
|||||||
export interface AgentServicesProvider extends DataProvider {
|
export interface AgentServicesProvider extends DataProvider {
|
||||||
// Job management methods
|
// Job management methods
|
||||||
getJobs(ownerUri: string): Thenable<AgentJobsResult>;
|
getJobs(ownerUri: string): Thenable<AgentJobsResult>;
|
||||||
getJobHistory(ownerUri: string, jobId: string): Thenable<AgentJobHistoryResult>;
|
getJobHistory(ownerUri: string, jobId: string, jobName: string): Thenable<AgentJobHistoryResult>;
|
||||||
jobAction(ownerUri: string, jobName: string, action: string): Thenable<ResultStatus>;
|
jobAction(ownerUri: string, jobName: string, action: string): Thenable<ResultStatus>;
|
||||||
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>;
|
||||||
|
|||||||
9
src/sql/sqlops.proposed.d.ts
vendored
9
src/sql/sqlops.proposed.d.ts
vendored
@@ -595,6 +595,10 @@ declare module 'sqlops' {
|
|||||||
* The languge mode for this text editor. The language mode is SQL by default.
|
* The languge mode for this text editor. The language mode is SQL by default.
|
||||||
*/
|
*/
|
||||||
languageMode?: string;
|
languageMode?: string;
|
||||||
|
/**
|
||||||
|
* Minimum height for editor component
|
||||||
|
*/
|
||||||
|
minimumHeight?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ButtonProperties extends ComponentProperties, ComponentWithIcon {
|
export interface ButtonProperties extends ComponentProperties, ComponentWithIcon {
|
||||||
@@ -722,6 +726,11 @@ declare module 'sqlops' {
|
|||||||
*/
|
*/
|
||||||
isAutoResizable: boolean;
|
isAutoResizable: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum height for editor component
|
||||||
|
*/
|
||||||
|
minimumHeight: number;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ButtonComponent extends Component, ButtonProperties {
|
export interface ButtonComponent extends Component, ButtonProperties {
|
||||||
|
|||||||
@@ -95,6 +95,16 @@ export enum JobCompletionActionCondition {
|
|||||||
Always = 3
|
Always = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum JobExecutionStatus {
|
||||||
|
Executing = 1,
|
||||||
|
WaitingForWorkerThread = 2,
|
||||||
|
BetweenRetries = 3,
|
||||||
|
Idle = 4,
|
||||||
|
Suspended = 5,
|
||||||
|
WaitingForStepToFinish = 6,
|
||||||
|
PerformingCompletionAction = 7
|
||||||
|
}
|
||||||
|
|
||||||
export enum AlertType {
|
export enum AlertType {
|
||||||
sqlServerEvent = 1,
|
sqlServerEvent = 1,
|
||||||
sqlServerPerformanceCondition = 2,
|
sqlServerPerformanceCondition = 2,
|
||||||
|
|||||||
@@ -578,8 +578,8 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
|||||||
/**
|
/**
|
||||||
* Get a Agent Job's history
|
* Get a Agent Job's history
|
||||||
*/
|
*/
|
||||||
public $getJobHistory(handle: number, ownerUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
|
public $getJobHistory(handle: number, ownerUri: string, jobID: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult> {
|
||||||
return this._resolveProvider<sqlops.AgentServicesProvider>(handle).getJobHistory(ownerUri, jobID);
|
return this._resolveProvider<sqlops.AgentServicesProvider>(handle).getJobHistory(ownerUri, jobID, jobName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -913,6 +913,14 @@ class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent {
|
|||||||
this.setProperty('isAutoResizable', v);
|
this.setProperty('isAutoResizable', v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get minimumHeight(): number {
|
||||||
|
return this.properties['minimumHeight'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public set minimumHeight(v: number) {
|
||||||
|
this.setProperty('minimumHeight', v);
|
||||||
|
}
|
||||||
|
|
||||||
public get onContentChanged(): vscode.Event<any> {
|
public get onContentChanged(): vscode.Event<any> {
|
||||||
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
|
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
|
||||||
return emitter && emitter.event;
|
return emitter && emitter.event;
|
||||||
|
|||||||
@@ -350,8 +350,8 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
|
|||||||
getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult> {
|
getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult> {
|
||||||
return self._proxy.$getJobs(handle, connectionUri);
|
return self._proxy.$getJobs(handle, connectionUri);
|
||||||
},
|
},
|
||||||
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
|
getJobHistory(connectionUri: string, jobID: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult> {
|
||||||
return self._proxy.$getJobHistory(handle, connectionUri, jobID);
|
return self._proxy.$getJobHistory(handle, connectionUri, jobID, jobName);
|
||||||
},
|
},
|
||||||
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
|
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
|
||||||
return self._proxy.$jobAction(handle, connectionUri, jobName, action);
|
return self._proxy.$jobAction(handle, connectionUri, jobName, action);
|
||||||
|
|||||||
@@ -422,6 +422,7 @@ export function createApiFactory(
|
|||||||
WeekDays: sqlExtHostTypes.WeekDays,
|
WeekDays: sqlExtHostTypes.WeekDays,
|
||||||
NotifyMethods: sqlExtHostTypes.NotifyMethods,
|
NotifyMethods: sqlExtHostTypes.NotifyMethods,
|
||||||
JobCompletionActionCondition: sqlExtHostTypes.JobCompletionActionCondition,
|
JobCompletionActionCondition: sqlExtHostTypes.JobCompletionActionCondition,
|
||||||
|
JobExecutionStatus: sqlExtHostTypes.JobExecutionStatus,
|
||||||
AlertType: sqlExtHostTypes.AlertType,
|
AlertType: sqlExtHostTypes.AlertType,
|
||||||
FrequencyTypes: sqlExtHostTypes.FrequencyTypes,
|
FrequencyTypes: sqlExtHostTypes.FrequencyTypes,
|
||||||
FrequencySubDayTypes: sqlExtHostTypes.FrequencySubDayTypes,
|
FrequencySubDayTypes: sqlExtHostTypes.FrequencySubDayTypes,
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ export abstract class ExtHostDataProtocolShape {
|
|||||||
/**
|
/**
|
||||||
* Get a Agent Job's history
|
* Get a Agent Job's history
|
||||||
*/
|
*/
|
||||||
$getJobHistory(handle: number, ownerUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> { throw ni(); }
|
$getJobHistory(handle: number, ownerUri: string, jobID: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult> { throw ni(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run an action on a Job
|
* Run an action on a Job
|
||||||
|
|||||||
Reference in New Issue
Block a user