Compare commits

...

29 Commits
1.0.1 ... 1.2.1

Author SHA1 Message Date
Karl Burtram
f7d92caae5 Correct SQL Tools Service filenames to netcoreapp2.2 2018-10-22 17:20:30 -07:00
Karl Burtram
201174e293 Bump Azure Data Studio to 1.2.1 2018-10-22 17:11:41 -07:00
Karl Burtram
dc2f6235a1 Update SQL Tools Service to 1.5.0-alpha.46 2018-10-22 17:04:54 -07:00
Todd Ortmann
0824c779db Fixed sync issue with my forked master so this commit is correct (#2948) 2018-10-19 16:16:40 -07:00
Anthony Dresser
e002ad3b6a fix test errors (#2938) 2018-10-18 13:27:30 -07:00
kenvanhyning
8a570069f8 Kenvh/editdatatabname (#2906)
* Remove unnecessary string encoding on edit data naming

* Cleaning up duplication in the naming methods.

* fix typo in method name
2018-10-18 12:58:07 -07:00
Karl Burtram
bfe44c1621 Update README for October release (#2950) 2018-10-18 07:35:44 -07:00
Karl Burtram
b17882a1c1 Bump agent and import extension versions (#2949) 2018-10-17 21:41:18 -07:00
Gene Lee
f309979126 Added feature to disable checkbox of checkboxTreeNode (#2942) 2018-10-17 19:26:56 -07:00
Matt Irvine
5a0490e81f Display multi-line messages correctly in query results (#2935) 2018-10-17 15:43:00 -07:00
Karl Burtram
2c007115f7 Fix query plan scrollbars (#2927)
* Fix query plan scrollbars

* Remove toString
2018-10-17 12:04:42 -07:00
Chris LaFreniere
ac47fb84a8 Fix Default Height for Editor Component (#2920)
Editor component didn't have a minimum height set, so fixing this by passing through a minimum height to EditorComponent. Now, if the scrollable height of the editor is less than the minimum height, we use the minimum height as the height of the component.

Also fixed an issue where the markdown code editor's height was far too high. Now we're calculating the height on the layout() call, which gets called every time we display the markdown editor.
2018-10-17 10:18:04 -07:00
Aditya Bist
7ba14a3925 schedules now get added in edit job (#2915) 2018-10-16 22:51:54 -07:00
Anthony Dresser
67514ccc5f change scroll container to fix ui glitch (#2924) 2018-10-16 21:55:21 -07:00
Anthony Dresser
6ee3886ecf clear out plan xml on executes (#2921) 2018-10-16 18:31:19 -07:00
Anthony Dresser
76282ed1ef Look for showplan colum name (#2919)
* change method for finding show plan through column name

* formatting
2018-10-16 18:12:01 -07:00
Anthony Dresser
bfa9e8c495 Handle query plan flow problems (#2918)
* modify the query plan work flow to account for some errors

* formatting
2018-10-16 16:15:47 -07:00
Aditya Bist
425eecf692 Agent/edit steps (#2846)
* steps can be now edited

* edit jobs now works with steps, alerts, schedules etc

* fixed bug when new step in new dialog would fail
2018-10-16 13:24:43 -07:00
Anthony Dresser
ac1f7542a9 change way we show query plan (#2866) 2018-10-15 17:18:14 -07:00
Karl Burtram
225d168fdd Add SQL vNext to recommended extensions list (#2858) 2018-10-15 16:10:00 -07:00
Karl Burtram
e073b2cf42 Change 'Clear All' to 'Show All Connections' (#2865) 2018-10-15 16:04:36 -07:00
Karl Burtram
1cb366d822 Update product name in Register Files setup checkbox (#2857) 2018-10-15 13:55:02 -07:00
Kevin Cunnane
c9c8e30ced Remove os.tmpDir deprecation warning (#2855)
- Import extension used very old version of telemetry. Updating avoids sending deprecation warning to console which CSS requested we fix.
2018-10-15 13:41:54 -07:00
Karl Burtram
c50941ac7d Update Azure Data Studio to 1.1.3 2018-10-15 13:15:36 -07:00
Karl Burtram
58f950ffbd Bump version to 1.1.2 for next build. 2018-10-12 16:43:03 -07:00
Alan Ren
a1341ba503 revert row edit and dirty row fix (#2845)
* revert row edit and dirty row fix

* undo the dirtyRow change
2018-10-12 16:39:08 -07:00
Matt Irvine
620f4a8bfb Bring in tools service fix for expanding columns being slow (#2844) 2018-10-12 16:22:36 -07:00
Todd Ortmann
7f11d44130 Fixed when right clicking and selecting Manage-correct name displays (#2794) 2018-10-12 11:15:05 -07:00
Chris LaFreniere
5c77e752f6 Allow for auto-resizable editor component (#2818) 2018-10-11 16:18:58 -07:00
49 changed files with 554 additions and 269 deletions

View File

@@ -1,5 +1,21 @@
# Change Log
## Version 1.1.3
* Release date: October 18, 2018
* Release status: General Availability
## What's new in this version
* Introducing the Azure Resource Explorer to browse Azure SQL Databases
* Improve Object Explorer and Query Editor connectivity robustness
* SQL Server 2019 and SQL Agent extension improvements
## Contributions and "thank you"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* philoushka for `center the icon #2760`
* anthonypants for `Typo #2775`
* kstolte for `Fix Invalid Configuration in Launch.json #2789`
* kstolte for `Fixing a reference to SQL Ops Studio #2788`
## Version 1.0.0
* Release date: September 24, 2018

View File

@@ -4,16 +4,16 @@
Azure Data Studio is a data management tool that enables you to work with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
**Download Azure Data Studio August Public Preview**
**Download the latest Azure Data Studio release**
Platform | Link
-- | --
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=2024683
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=2024680
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=2024677
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=2024675
Linux RPM | https://go.microsoft.com/fwlink/?linkid=2024672
Linux DEB | https://go.microsoft.com/fwlink/?linkid=2024668
Windows Setup Installer | https://go.microsoft.com/fwlink/?linkid=2030731
Windows ZIP | https://go.microsoft.com/fwlink/?linkid=2030736
macOS ZIP | https://go.microsoft.com/fwlink/?linkid=2030738
Linux TAR.GZ | https://go.microsoft.com/fwlink/?linkid=2030741
Linux RPM | https://go.microsoft.com/fwlink/?linkid=2030746
Linux DEB | https://go.microsoft.com/fwlink/?linkid=2030750
Go to our [download page](https://aka.ms/azuredatastudio) for more specific instructions.
@@ -61,6 +61,10 @@ The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.micro
## Contributions and "Thank You"
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
* philoushka for `center the icon #2760`
* anthonypants for `Typo #2775`
* kstolte for `Fix Invalid Configuration in Launch.json #2789`
* kstolte for `Fixing a reference to SQL Ops Studio #2788`
* AlexFsmn `Feature: Ability to add connection name #2332`
* AlexFsmn `Disabled connection name input when connecting to a server. #2566`
* SebastianPfliegel `Added more saveAsCsv options #2099`

View File

@@ -68,7 +68,7 @@ Type: filesandordirs; Name: "{app}\_"
[Tasks]
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: "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: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent

View File

@@ -2,7 +2,7 @@
"name": "agent",
"displayName": "SQL Server Agent",
"description": "Manage and troubleshoot SQL Server Agent jobs",
"version": "0.33.0",
"version": "0.34.0",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
@@ -26,12 +26,6 @@
"outputChannels": [
"sqlagent"
],
"commands": [
{
"command": "agent.openNewStepDialog",
"title": "agent.openNewStepDialog"
}
],
"dashboard.tabs": [
{
"id": "data-management-agent",

View File

@@ -35,4 +35,5 @@ export class AgentUtils {
}
return this._queryProvider;
}
}

View File

@@ -136,8 +136,13 @@ export class JobData implements IAgentDialogData {
}
public addJobSchedule(schedule: sqlops.AgentJobScheduleInfo) {
let existingSchedule = this.jobSchedules.find(item => item.name === schedule.name);
if (!existingSchedule) {
if (this.jobSchedules) {
let existingSchedule = this.jobSchedules.find(item => item.name === schedule.name);
if (!existingSchedule) {
this.jobSchedules.push(schedule);
}
} else {
this.jobSchedules = [];
this.jobSchedules.push(schedule);
}
}

View File

@@ -4,20 +4,22 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as nls from 'vscode-nls';
import * as vscode from 'vscode';
import { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
import { JobData } from './jobData';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
export class JobStepData implements IAgentDialogData {
// Error Messages
private 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_JobNameIsEmpty = localize('stepData.jobNameRequired', 'Job 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 jobId: string;
public jobName: string;
@@ -28,8 +30,8 @@ export class JobStepData implements IAgentDialogData {
public id: number;
public failureAction: string;
public successAction: string;
public failStepId: number;
public successStepId: number;
public failStepId: number;
public command: string;
public commandExecutionSuccessCode: number;
public databaseName: string;
@@ -43,10 +45,12 @@ export class JobStepData implements IAgentDialogData {
public retryAttempts: number;
public retryInterval: number;
public proxyName: string;
private jobModel: JobData;
constructor(ownerUri:string, jobModel?: JobData) {
this.ownerUri = ownerUri;
this.jobName = jobModel.name;
this.jobModel = jobModel;
}
public async initialize() {
@@ -54,47 +58,35 @@ export class JobStepData implements IAgentDialogData {
public async save() {
let agentService = await AgentUtils.getAgentService();
agentService.createJobStep(this.ownerUri, {
jobId: this.jobId,
jobName: this.jobName,
script: this.script,
scriptName: this.scriptName,
stepName: this.stepName,
subSystem: this.subSystem,
id: this.id,
failureAction: this.failureAction,
successAction: this.successAction,
failStepId: this.failStepId,
successStepId: this.successStepId,
command: this.command,
commandExecutionSuccessCode: this.commandExecutionSuccessCode,
databaseName: this.databaseName,
databaseUserName: this.databaseUserName,
server: this.server,
outputFileName: this.outputFileName,
appendToLogFile: this.appendToLogFile,
appendToStepHist: this.appendToStepHist,
writeLogToTable: this.writeLogToTable,
appendLogToTable: this.appendLogToTable,
retryAttempts: this.retryAttempts,
retryInterval: this.retryInterval,
proxyName: this.proxyName
}).then(result => {
if (result && result.success) {
console.info(result);
let result: any;
if (this.dialogMode === AgentDialogMode.CREATE) {
if (this.jobModel && this.jobModel.dialogMode === AgentDialogMode.CREATE) {
// create job -> create step
Promise.resolve(this);
return;
} else {
// edit job -> create step
result = await agentService.createJobStep(this.ownerUri, JobStepData.convertToAgentJobStepInfo(this));
}
});
} 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[] } {
let validationErrors: string[] = [];
if (!(this.stepName && this.stepName.trim())) {
validationErrors.push(this.CreateStepErrorMessage_StepNameIsEmpty);
validationErrors.push(JobStepData.CreateStepErrorMessage_StepNameIsEmpty);
}
if (!(this.jobName && this.jobName.trim())) {
validationErrors.push(this.CreateStepErrorMessage_JobNameIsEmpty);
validationErrors.push(JobStepData.CreateStepErrorMessage_JobNameIsEmpty);
}
return {
@@ -102,4 +94,66 @@ export class JobStepData implements IAgentDialogData {
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;
}
}

View File

@@ -13,9 +13,11 @@ export class PickScheduleData implements IAgentDialogData {
public ownerUri: string;
public schedules: sqlops.AgentJobScheduleInfo[];
public selectedSchedule: sqlops.AgentJobScheduleInfo;
private jobName: string;
constructor(ownerUri:string) {
constructor(ownerUri:string, jobName: string) {
this.ownerUri = ownerUri;
this.jobName = jobName;
}
public async initialize() {
@@ -27,5 +29,8 @@ export class PickScheduleData implements IAgentDialogData {
}
public async save() {
let agentService = await AgentUtils.getAgentService();
this.selectedSchedule.jobName = this.jobName;
let result = await agentService.createJobSchedule(this.ownerUri, this.selectedSchedule);
}
}

View File

@@ -108,12 +108,14 @@ export class JobDialog extends AgentDialog<JobData> {
// Alert tab controls
private alertsTable: sqlops.TableComponent;
private newAlertButton: sqlops.ButtonComponent;
private isEdit: boolean = false;
constructor(ownerUri: string, jobInfo: sqlops.AgentJobInfo = undefined) {
super(
ownerUri,
new JobData(ownerUri, jobInfo),
jobInfo ? JobDialog.EditDialogTitle : JobDialog.CreateDialogTitle);
this.isEdit = jobInfo ? true : false;
}
protected async initializeDialog() {
@@ -235,13 +237,17 @@ export class JobDialog extends AgentDialog<JobData> {
width: 80
}).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) => {
if (!this.model.jobSteps) {
this.model.jobSteps = [];
}
this.model.jobSteps.push(step);
this.stepsTable.data = this.convertStepsToData(this.model.jobSteps);
});
this.newStepButton.onDidClick((e)=>{
if (this.nameTextBox.value && this.nameTextBox.value.length > 0) {
stepDialog.jobName = this.nameTextBox.value;
stepDialog.openDialog();
} else {
this.dialog.message = { text: this.BlankJobNameErrorText };
@@ -267,17 +273,15 @@ export class JobDialog extends AgentDialog<JobData> {
// one step selection
if (this.stepsTable.selectedRows.length === 1) {
let rowNumber = this.stepsTable.selectedRows[0];
let stepData = steps[rowNumber];
let stepData = this.model.jobSteps[rowNumber];
this.deleteStepButton.enabled = true;
this.editStepButton.enabled = true;
this.editStepButton.onDidClick((e) => {
// implement edit steps
// let stepDialog = new JobStepDialog(this.model.ownerUri, this.nameTextBox.value, '' , 1, this.model);
// stepDialog.openNewStepDialog();
this.editStepButton.onDidClick(() => {
let stepDialog = new JobStepDialog(this.model.ownerUri, '' , this.model, stepData);
stepDialog.openDialog();
});
this.deleteStepButton.onDidClick((e) => {
this.deleteStepButton.onDidClick(() => {
AgentUtils.getAgentService().then((agentService) => {
let steps = this.model.jobSteps ? this.model.jobSteps : [];
agentService.deleteJobStep(this.ownerUri, stepData).then((result) => {
@@ -371,12 +375,12 @@ export class JobDialog extends AgentDialog<JobData> {
label: this.PickScheduleButtonString,
width: 80
}).component();
this.pickScheduleButton.onDidClick((e)=>{
let pickScheduleDialog = new PickScheduleDialog(this.model.ownerUri);
let pickScheduleDialog = new PickScheduleDialog(this.model.ownerUri, this.model.name);
pickScheduleDialog.onSuccess((dialogModel) => {
let selectedSchedule = dialogModel.selectedSchedule;
if (selectedSchedule) {
selectedSchedule.jobName = this.model.name;
this.model.addJobSchedule(selectedSchedule);
this.populateScheduleTable();
}

View File

@@ -10,6 +10,7 @@ import { JobStepData } from '../data/jobStepData';
import { AgentUtils } from '../agentUtils';
import { JobData } from '../data/jobData';
import { AgentDialog } from './agentDialog';
import { AgentDialogMode } from '../interfaces';
const path = require('path');
const localize = nls.loadMessageBundle();
@@ -19,7 +20,8 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
// TODO: localize
// 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 OkButtonText: string = localize('jobStepDialog.ok', 'OK');
private readonly CancelButtonText: string = localize('jobStepDialog.cancel', 'Cancel');
@@ -102,24 +104,32 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
// Checkbox
private appendToExistingFileCheckbox: sqlops.CheckBoxComponent;
private logToTableCheckbox: sqlops.CheckBoxComponent;
private logStepOutputHistoryCheckbox: sqlops.CheckBoxComponent;
private fileBrowserTree: sqlops.FileBrowserTreeComponent;
private jobModel: JobData;
private jobName: string;
public jobName: string;
private server: string;
private stepId: number;
private isEdit: boolean;
constructor(
ownerUri: string,
server: string,
stepId: number,
jobModel?: JobData
jobModel: JobData,
jobStepInfo?: sqlops.AgentJobStepInfo,
) {
super(ownerUri, new JobStepData(ownerUri, jobModel), 'New Step');
this.stepId = stepId;
this.jobName = jobModel.name;
this.server = server;
super(ownerUri,
jobStepInfo ? JobStepData.convertToJobStepData(jobStepInfo, jobModel) : new JobStepData(ownerUri, jobModel),
jobStepInfo ? JobStepDialog.EditDialogTitle : JobStepDialog.NewDialogTitle);
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.jobName = this.jobName ? this.jobName : this.jobModel.name;
this.server = server;
}
private initializeUIComponents() {
@@ -254,6 +264,14 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
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()
.withLayout({ flexFlow: 'row', justifyContent: 'space-between', width: 300 })
.withItems([this.logToTableCheckbox]).component();
let logStepOutputHistoryCheckbox = view.modelBuilder.checkBox()
this.logStepOutputHistoryCheckbox = view.modelBuilder.checkBox()
.withProperties({ label: this.IncludeStepOutputHistoryLabel }).component();
this.userInputBox = view.modelBuilder.inputBox()
.withProperties({ inputType: 'text', width: '100%' }).component();
@@ -315,7 +333,7 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
component: appendCheckboxContainer,
title: ' '
}, {
component: logStepOutputHistoryCheckbox,
component: this.logStepOutputHistoryCheckbox,
title: ''
}, {
component: this.userInputBox,
@@ -326,7 +344,19 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
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.id = this.stepId;
this.model.server = this.server;
this.model.stepName = this.nameTextBox.value;
this.model.subSystem = this.typeDropdown.value as string;
this.model.databaseName = this.databaseDropdown.value as string;
this.model.script = this.commandTextBox.value;

View File

@@ -33,8 +33,8 @@ export class PickScheduleDialog {
private _onSuccess: vscode.EventEmitter<PickScheduleData> = new vscode.EventEmitter<PickScheduleData>();
public readonly onSuccess: vscode.Event<PickScheduleData> = this._onSuccess.event;
constructor(ownerUri: string) {
this.model = new PickScheduleData(ownerUri);
constructor(ownerUri: string, jobName: string) {
this.model = new PickScheduleData(ownerUri, jobName);
}
public async showDialog() {

View File

@@ -13,6 +13,7 @@ import { OperatorDialog } from './dialogs/operatorDialog';
import { ProxyDialog } from './dialogs/proxyDialog';
import { JobStepDialog } from './dialogs/jobStepDialog';
import { PickScheduleDialog } from './dialogs/pickScheduleDialog';
import { JobData } from './data/jobData';
const localize = nls.loadMessageBundle();
@@ -40,12 +41,12 @@ export class MainController {
let dialog = new JobDialog(ownerUri, jobInfo);
dialog.openDialog();
});
vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, server: string, stepId: number) => {
let dialog = new JobStepDialog(ownerUri, server, stepId);
vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, server: string, jobData: JobData, jobStepInfo: sqlops.AgentJobStepInfo) => {
let dialog = new JobStepDialog(ownerUri, server, jobData, jobStepInfo);
dialog.openDialog();
});
vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string) => {
let dialog = new PickScheduleDialog(ownerUri);
vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string, jobName: string) => {
let dialog = new PickScheduleDialog(ownerUri, jobName);
dialog.showDialog();
});
vscode.commands.registerCommand('agent.openAlertDialog', (ownerUri: string, alertInfo: sqlops.AgentAlertInfo, jobs: string[]) => {

View File

@@ -14,7 +14,7 @@ export class TestAgentService implements sqlops.AgentServicesProvider {
getJobs(ownerUri: string): Thenable<sqlops.AgentJobsResult> {
return undefined;
}
getJobHistory(ownerUri: string, jobId: string): Thenable<sqlops.AgentJobHistoryResult> {
getJobHistory(ownerUri: string, jobId: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult> {
return undefined;
}
jobAction(ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {

View File

@@ -2,7 +2,7 @@
"name": "import",
"displayName": "SQL Server Import",
"description": "SQL Server Import for Azure Data Studio supports importing CSV or JSON files into SQL Server.",
"version": "0.2.0",
"version": "0.3.0",
"publisher": "Microsoft",
"preview": true,
"engines": {
@@ -83,9 +83,8 @@
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.7",
"opener": "^1.4.3",
"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"
},
"devDependencies": {
}
"devDependencies": {}
}

View File

@@ -8,9 +8,13 @@ agent-base@4, agent-base@^4.1.0:
dependencies:
es6-promisify "^5.0.0"
applicationinsights@0.15.6:
version "0.15.6"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-0.15.6.tgz#201a0682c0704fe4bdd9a92d0b2cbe34d2ae5972"
applicationinsights@1.0.1:
version "1.0.1"
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:
version "0.0.8"
@@ -120,6 +124,16 @@ decompress@^4.2.0:
pify "^2.3.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:
version "1.4.1"
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:
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":
version "0.1.5"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/6ebb0465573cc140e461a22f334260f55ef45546"
@@ -357,12 +375,11 @@ util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
vscode-extension-telemetry@^0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.5.tgz#21e2abb4cbce3326e469ddbb322123b3702f3f85"
vscode-extension-telemetry@0.0.18:
version "0.0.18"
resolved "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327"
dependencies:
applicationinsights "0.15.6"
winreg "0.0.13"
applicationinsights "1.0.1"
vscode-jsonrpc@3.5.0:
version "3.5.0"
@@ -389,10 +406,6 @@ vscode-nls@^3.2.1:
version "3.2.4"
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:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
@@ -407,3 +420,7 @@ yauzl@^2.4.2:
dependencies:
buffer-crc32 "~0.2.3"
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"

View File

@@ -1,18 +1,18 @@
{
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "1.5.0-alpha.41",
"version": "1.5.0-alpha.46",
"downloadFileNames": {
"Windows_86": "win-x86-netcoreapp2.1.zip",
"Windows_64": "win-x64-netcoreapp2.1.zip",
"OSX": "osx-x64-netcoreapp2.1.tar.gz",
"CentOS_7": "rhel-x64-netcoreapp2.1.tar.gz",
"Debian_8": "rhel-x64-netcoreapp2.1.tar.gz",
"Fedora_23": "rhel-x64-netcoreapp2.1.tar.gz",
"OpenSUSE_13_2": "rhel-x64-netcoreapp2.1.tar.gz",
"RHEL_7": "rhel-x64-netcoreapp2.1.tar.gz",
"SLES_12_2": "rhel-x64-netcoreapp2.1.tar.gz",
"Ubuntu_14": "rhel-x64-netcoreapp2.1.tar.gz",
"Ubuntu_16": "rhel-x64-netcoreapp2.1.tar.gz"
"Windows_86": "win-x86-netcoreapp2.2.zip",
"Windows_64": "win-x64-netcoreapp2.2.zip",
"OSX": "osx-x64-netcoreapp2.2.tar.gz",
"CentOS_7": "rhel-x64-netcoreapp2.2.tar.gz",
"Debian_8": "rhel-x64-netcoreapp2.2.tar.gz",
"Fedora_23": "rhel-x64-netcoreapp2.2.tar.gz",
"OpenSUSE_13_2": "rhel-x64-netcoreapp2.2.tar.gz",
"RHEL_7": "rhel-x64-netcoreapp2.2.tar.gz",
"SLES_12_2": "rhel-x64-netcoreapp2.2.tar.gz",
"Ubuntu_14": "rhel-x64-netcoreapp2.2.tar.gz",
"Ubuntu_16": "rhel-x64-netcoreapp2.2.tar.gz"
},
"installDirectory": "../sqltoolsservice/{#platform#}/{#version#}",
"executableFiles": ["MicrosoftSqlToolsServiceLayer.exe", "MicrosoftSqlToolsServiceLayer"]

View File

@@ -40,6 +40,7 @@ export interface AgentJobsParams {
export interface AgentJobHistoryParams {
ownerUri: string;
jobId: string;
jobName: string;
}
export interface AgentJobActionParams {

View File

@@ -80,8 +80,8 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
);
};
let getJobHistory = (ownerUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> => {
let params: contracts.AgentJobHistoryParams = { ownerUri: ownerUri, jobId: jobID };
let getJobHistory = (ownerUri: string, jobID: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult> => {
let params: contracts.AgentJobHistoryParams = { ownerUri: ownerUri, jobId: jobID, jobName: jobName };
return client.sendRequest(contracts.AgentJobHistoryRequest.type, params).then(
r => r,

View File

@@ -1,6 +1,6 @@
{
"name": "azuredatastudio",
"version": "1.0.1",
"version": "1.2.1",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"

View File

@@ -40,6 +40,7 @@
"Microsoft.import",
"Microsoft.profiler",
"Microsoft.server-report",
"Microsoft.sql-vnext",
"Microsoft.whoisactive",
"Redgate.sql-search"
],

View File

@@ -5,8 +5,8 @@
import { IThemable } from 'vs/platform/theme/common/styler';
import { Event, Emitter } from 'vs/base/common/event';
import { Dimension, EventType } from 'vs/base/browser/dom';
import { $, Builder } from 'vs/base/browser/builder';
import { Dimension, EventType, $, addDisposableListener } from 'vs/base/browser/dom';
import { $ as qb } from 'vs/base/browser/builder';
import { IAction } from 'vs/base/common/actions';
import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@@ -34,8 +34,8 @@ export interface IPanelTab {
}
interface IInternalPanelTab extends IPanelTab {
header: Builder;
label: Builder;
header: HTMLElement;
label: HTMLElement;
dispose(): void;
}
@@ -49,10 +49,10 @@ export class TabbedPanel extends Disposable implements IThemable {
private _tabMap = new Map<PanelTabIdentifier, IInternalPanelTab>();
private _shownTab: PanelTabIdentifier;
public readonly headersize = 35;
private $header: Builder;
private $tabList: Builder;
private $body: Builder;
private $parent: Builder;
private header: HTMLElement;
private tabList: HTMLElement;
private body: HTMLElement;
private parent: HTMLElement;
private _actionbar: ActionBar;
private _currentDimensions: Dimension;
private _collapsed = false;
@@ -65,26 +65,26 @@ export class TabbedPanel extends Disposable implements IThemable {
constructor(private container: HTMLElement, private options: IPanelOptions = defaultOptions) {
super();
this.$parent = this._register($('.tabbedPanel'));
this.$parent.appendTo(container);
this.$header = $('.composite.title');
this.$tabList = $('.tabList');
this.$tabList.attr('role', 'tablist');
this.$tabList.style('height', this.headersize + 'px');
this.$header.append(this.$tabList);
this.parent = $('.tabbedPanel');
container.appendChild(this.parent);
this.header = $('.composite.title');
this.tabList = $('.tabList');
this.tabList.setAttribute('role', 'tablist');
this.tabList.style.height = this.headersize + 'px';
this.header.appendChild(this.tabList);
let actionbarcontainer = $('.title-actions');
this._actionbar = new ActionBar(actionbarcontainer.getHTMLElement());
this.$header.append(actionbarcontainer);
this._actionbar = new ActionBar(actionbarcontainer);
this.header.appendChild(actionbarcontainer);
if (options.showHeaderWhenSingleView) {
this._headerVisible = true;
this.$parent.append(this.$header);
this.parent.appendChild(this.header);
} else {
this._headerVisible = false;
}
this.$body = $('.tabBody');
this.$body.attr('role', 'tabpanel');
this.$body.attr('tabindex', '0');
this.$parent.append(this.$body);
this.body = $('.tabBody');
this.body.setAttribute('role', 'tabpanel');
this.body.setAttribute('tabindex', '0');
this.parent.appendChild(this.body);
}
public contains(tab: IPanelTab): boolean {
@@ -99,7 +99,7 @@ export class TabbedPanel extends Disposable implements IThemable {
this.showTab(tab.identifier);
}
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.layout(this._currentDimensions);
}
@@ -116,30 +116,27 @@ export class TabbedPanel extends Disposable implements IThemable {
private _createTab(tab: IInternalPanelTab): void {
let tabHeaderElement = $('.tab-header');
tabHeaderElement.attr('tabindex', '0');
tabHeaderElement.attr('role', 'tab');
tabHeaderElement.attr('aria-selected', 'false');
tabHeaderElement.attr('aria-controls', tab.identifier);
tabHeaderElement.setAttribute('tabindex', '0');
tabHeaderElement.setAttribute('role', 'tab');
tabHeaderElement.setAttribute('aria-selected', 'false');
tabHeaderElement.setAttribute('aria-controls', tab.identifier);
let tabElement = $('.tab');
tabHeaderElement.append(tabElement);
tabHeaderElement.appendChild(tabElement);
let tabLabel = $('a.tabLabel');
tabLabel.safeInnerHtml(tab.title);
tabElement.append(tabLabel);
tabHeaderElement.on(EventType.CLICK, e => this.showTab(tab.identifier));
tabHeaderElement.on(EventType.KEY_DOWN, (e: KeyboardEvent) => {
tabLabel.innerText = tab.title;
tabElement.appendChild(tabLabel);
addDisposableListener(tabHeaderElement, EventType.CLICK, e => this.showTab(tab.identifier));
addDisposableListener(tabHeaderElement, EventType.KEY_DOWN, (e: KeyboardEvent) => {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) {
this.showTab(tab.identifier);
e.stopImmediatePropagation();
}
});
this.$tabList.append(tabHeaderElement);
this.tabList.appendChild(tabHeaderElement);
tab.header = tabHeaderElement;
tab.label = tabLabel;
tab.dispose = () => {
tab.header.dispose();
tab.label.dispose();
};
tab.dispose = () => { };
this._register(tab);
}
@@ -149,19 +146,20 @@ export class TabbedPanel extends Disposable implements IThemable {
}
if (this._shownTab) {
this._tabMap.get(this._shownTab).label.removeClass('active');
this._tabMap.get(this._shownTab).header.removeClass('active').attr('aria-selected', 'false');
qb(this._tabMap.get(this._shownTab).label).removeClass('active');
qb(this._tabMap.get(this._shownTab).header).removeClass('active');
this._tabMap.get(this._shownTab).header.setAttribute('aria-selected', 'false');
}
this._shownTab = id;
this.tabHistory.push(id);
this.$body.clearChildren();
qb(this.body).empty();
let tab = this._tabMap.get(this._shownTab);
this.$body.attr('aria-labelledby', tab.identifier);
tab.label.addClass('active');
tab.header.addClass('active');
tab.header.attr('aria-selected', 'true');
tab.view.render(this.$body.getHTMLElement());
this.body.setAttribute('aria-labelledby', tab.identifier);
qb(tab.label).addClass('active');
qb(tab.header).addClass('active');
tab.header.setAttribute('aria-selected', 'true');
tab.view.render(this.body);
this._onTabChange.fire(id);
if (this._currentDimensions) {
this._layoutCurrentTab(new Dimension(this._currentDimensions.width, this._currentDimensions.height - this.headersize));
@@ -170,11 +168,11 @@ export class TabbedPanel extends Disposable implements IThemable {
public removeTab(tab: PanelTabIdentifier) {
let actualTab = this._tabMap.get(tab);
actualTab.header.destroy();
qb(actualTab.header).destroy();
if (actualTab.view.remove) {
actualTab.view.remove();
}
this._tabMap.get(tab).header.destroy();
qb(this._tabMap.get(tab).header).destroy();
this._tabMap.delete(tab);
if (this._shownTab === tab) {
this._shownTab = undefined;
@@ -192,7 +190,7 @@ export class TabbedPanel extends Disposable implements IThemable {
}
if (!this.options.showHeaderWhenSingleView && this._tabMap.size === 1 && this._headerVisible) {
this.$header.offDOM();
this.header.remove();
this._headerVisible = false;
this.layout(this._currentDimensions);
}
@@ -205,12 +203,12 @@ export class TabbedPanel extends Disposable implements IThemable {
public layout(dimension: Dimension): void {
if (dimension) {
this._currentDimensions = dimension;
this.$parent.style('height', dimension.height + 'px');
this.$parent.style('width', dimension.width + 'px');
this.$header.style('width', dimension.width + 'px');
this.$body.style('width', dimension.width + 'px');
this.parent.style.height = dimension.height + 'px';
this.parent.style.height = dimension.width + 'px';
this.header.style.width = dimension.width + 'px';
this.body.style.width = dimension.width + 'px';
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));
}
}
@@ -232,9 +230,9 @@ export class TabbedPanel extends Disposable implements IThemable {
this._collapsed = val === false ? false : true;
if (this.collapsed) {
this.$body.offDOM();
this.body.remove();
} else {
this.$parent.append(this.$body);
this.parent.appendChild(this.body);
}
}

View File

@@ -84,7 +84,7 @@ export class DashboardInput extends EditorInput {
return '';
}
let name = this.connectionProfile.serverName;
let name = this.connectionProfile.connectionName ? this.connectionProfile.connectionName : this.connectionProfile.serverName;
if (this.connectionProfile.databaseName
&& !this.isMasterMssql()) {
// Only add DB name if this is a non-default, non-master connection

View File

@@ -60,7 +60,7 @@ export class BreadcrumbService implements IBreadcrumbService {
}
private getServerBreadcrumb(profile: ConnectionProfile): MenuItem {
return { label: profile.serverName, routerLink: ['server-dashboard'] };
return profile.connectionName ? { label: profile.connectionName, routerLink: ['server-dashboard'] } : { label: profile.serverName, routerLink: ['server-dashboard'] };
}
private getDbBreadcrumb(profile: ConnectionProfile): MenuItem {

View File

@@ -192,9 +192,9 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.onBeforeAppendCell = (row: number, column: number): string => {
let cellClass = undefined;
if (this.isRowDirty(row) && column === 0) {
cellClass = ' dirtyCell ';
} else if (this.isCellDirty(row, column)) {
cellClass = ' dirtyRowHeader ';
} else if (this.isCellDirty(row, column)) {
cellClass = ' dirtyCell ';
}
return cellClass;
@@ -279,8 +279,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
let cellSelectTasks: Promise<void> = this.submitCurrentCellChange(
(result: EditUpdateCellResult) => {
// Cell update was successful, update the flags
self.setCellDirtyState(row, self.currentCell.column, result.cell.isDirty);
self.setRowDirtyState(row, result.isRowDirty);
self.setCellDirtyState(self.currentCell.row, self.currentCell.column, result.cell.isDirty);
self.setRowDirtyState(self.currentCell.row, result.isRowDirty);
return Promise.resolve();
},
(error) => {
@@ -474,10 +474,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
//
this.currentEditCellValue = undefined;
this.dirtyCells = [];
let row = this.currentCell.row;
this.resetCurrentCell();
if (this.currentCell.row !== undefined) {
this.dataSet.dataRows.resetWindowsAroundIndex(this.currentCell.row);
if (row !== undefined) {
this.dataSet.dataRows.resetWindowsAroundIndex(row);
}
}
}
@@ -540,6 +541,9 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
}
} else {
$(grid.getCellNode(row, column)).removeClass('dirtyCell');
if (this.dirtyCells.indexOf(column) !== -1) {
this.dirtyCells.splice(this.dirtyCells.indexOf(column), 1);
}
}
}

View File

@@ -22,7 +22,7 @@ export interface IJobManagementService {
fireOnDidChange(): void;
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>;
deleteJobStep(connectionUri: string, step: sqlops.AgentJobStepInfo): Thenable<sqlops.ResultStatus>;

View File

@@ -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 runner.getJobHistory(connectionUri, jobID);
return runner.getJobHistory(connectionUri, jobID, jobName);
});
}
@@ -142,6 +142,9 @@ export class JobCacheObject {
_serviceBrand: any;
private _jobs: sqlops.AgentJobInfo[] = [];
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 _prevJobID: string;
private _serverName: string;
@@ -176,6 +179,18 @@ export class JobCacheObject {
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 */
public set jobs(value: sqlops.AgentJobInfo[]) {
this._jobs = value;
@@ -204,4 +219,16 @@ export class JobCacheObject {
public set dataView(value: Slick.Data.DataView<any>) {
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;
}
}

View File

@@ -150,16 +150,17 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
private loadHistory() {
const self = this;
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
this._jobManagementService.getJobHistory(ownerUri, this._agentViewComponent.jobId).then((result) => {
if (result && result.jobs) {
if (result.jobs.length > 0) {
let jobName = this._agentViewComponent.agentJobInfo.name;
this._jobManagementService.getJobHistory(ownerUri, this._agentViewComponent.jobId, jobName).then((result) => {
if (result && result.histories) {
if (result.histories.length > 0) {
self._showPreviousRuns = true;
self.buildHistoryTree(self, result.jobs);
self.buildHistoryTree(self, result.histories);
if (self._agentViewComponent.showHistory) {
self._cd.detectChanges();
}
} else {
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, result.jobs);
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, result.histories);
self._showPreviousRuns = false;
}
} else {

View File

@@ -53,6 +53,7 @@ export abstract class JobManagementView extends TabChild implements AfterContent
if (!this.isInitialized) {
this._showProgressWheel = true;
this.onFirstVisible();
this.layout();
this.isInitialized = true;
}
} else if (this.isVisible === true && this._parentComponent.refresh === true) {

View File

@@ -36,8 +36,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
import { escape } from 'sql/base/common/strings';
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { tableBackground, cellBackground, tableHoverBackground, jobsHeadingBackground, cellBorderColor } from 'sql/common/theme/colors';
import { JobStepsViewRow } from 'sql/parts/jobManagement/views/jobStepsViewTree';
import { tableBackground, cellBackground, cellBorderColor } from 'sql/common/theme/colors';
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
export const ROW_HEIGHT: number = 45;
@@ -87,7 +86,10 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
private sortingStylingMap: { [columnName: string]: any; } = {};
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;
@ViewChild('jobsgrid') _gridEl: ElementRef;
@@ -579,10 +581,14 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
private async curateJobHistory(jobs: sqlops.AgentJobInfo[], ownerUri: string) {
const self = this;
jobs.forEach(async (job) => {
await this._jobManagementService.getJobHistory(ownerUri, job.jobId).then((result) => {
if (result && result.jobs) {
self.jobHistories[job.jobId] = result.jobs;
self._jobCacheObject.setJobHistory(job.jobId, result.jobs);
await this._jobManagementService.getJobHistory(ownerUri, job.jobId, job.name).then((result) => {
if (result) {
self.jobSteps[job.jobId] = result.steps ? result.steps : [];
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 previousRuns: sqlops.AgentJobHistoryInfo[];
if (jobHistories.length >= 5) {
@@ -592,7 +598,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
}
self.createJobChart(job.jobId, previousRuns);
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 noStepsMessage = nls.localize('jobsView.noSteps', 'No Steps available for this job.');
let errorMessage = lastJobHistory ? lastJobHistory.message : noStepsMessage;
@@ -909,29 +915,22 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
jobId = data.getItem(rowIndex - 1).jobId;
}
}
let job: sqlops.AgentJobInfo[] = this.jobs.filter(job => {
return job.jobId === jobId;
});
let jobHistories = this.jobHistories[jobId];
let steps: sqlops.AgentJobStep[] = undefined;
let schedules: sqlops.AgentJobScheduleInfo[] = undefined;
let alerts: sqlops.AgentAlertInfo[] = undefined;
// add steps
if (this.jobSteps && this.jobSteps[jobId]) {
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]) {
// 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
schedules = jobHistories[jobHistories.length-1].schedules;
if (schedules && schedules.length > 0) {
if (!job[0].JobSchedules) {
job[0].JobSchedules = [];
@@ -944,7 +943,6 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
}
}
// add alerts
alerts = jobHistories[jobHistories.length-1].alerts;
if (!job[0].Alerts) {
job[0].Alerts = [];
}

View File

@@ -38,6 +38,8 @@ export default class EditorComponent extends ComponentBase implements IComponent
private _renderedContent: string;
private _languageMode: string;
private _uri: string;
private _isAutoResizable: boolean;
private _minimumHeight: number;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@@ -77,6 +79,12 @@ export default class EditorComponent extends ComponentBase implements IComponent
this._register(this._editorInput);
this._register(this._editorModel.onDidChangeContent(e => {
this.content = this._editorModel.getValue();
if (this._isAutoResizable) {
if (this._minimumHeight) {
this._editor.setMinimumHeight(this._minimumHeight);
}
this._editor.setHeightToScrollHeight();
}
// Notify via an event so that extensions can detect and propagate changes
this.fireEvent({
@@ -104,6 +112,10 @@ export default class EditorComponent extends ComponentBase implements IComponent
let width: number = this.convertSizeToNumber(this.width);
let height: number = this.convertSizeToNumber(this.height);
if (this._isAutoResizable) {
this._editor.setHeightToScrollHeight();
height = Math.max(this._editor.scrollHeight, this._minimumHeight ? this._minimumHeight : 0);
}
this._editor.layout(new DOM.Dimension(
width && width > 0 ? width : DOM.getContentWidth(this._el.nativeElement),
height && height > 0 ? height : DOM.getContentHeight(this._el.nativeElement)));
@@ -144,6 +156,8 @@ export default class EditorComponent extends ComponentBase implements IComponent
}
// Intentionally always updating editorUri as it's wiped out by parent setProperties call.
this.editorUri = this._uri;
this._isAutoResizable = this.isAutoResizable;
this._minimumHeight = this.minimumHeight;
}
// CSS-bound properties
@@ -163,6 +177,22 @@ export default class EditorComponent extends ComponentBase implements IComponent
this.setPropertyFromUI<sqlops.EditorProperties, string>((properties, languageMode) => { properties.languageMode = languageMode; }, newValue);
}
public get isAutoResizable(): boolean {
return this.getPropertyOrDefault<sqlops.EditorProperties, boolean>((props) => props.isAutoResizable, false);
}
public set isAutoResizable(newValue: boolean) {
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 {
return this.getPropertyOrDefault<sqlops.EditorProperties, string>((props) => props.editorUri, '');
}

View File

@@ -23,6 +23,8 @@ import { EditorOptions } from 'vs/workbench/common/editor';
import { StandaloneCodeEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Configuration } from 'vs/editor/browser/config/configuration';
/**
* Extension of TextResourceEditor that is always readonly rather than only with non UntitledInputs
@@ -31,6 +33,8 @@ export class QueryTextEditor extends BaseTextEditor {
public static ID = 'modelview.editors.textEditor';
private _dimension: DOM.Dimension;
private _config: editorCommon.IConfiguration;
private _minHeight: number;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@@ -98,7 +102,26 @@ export class QueryTextEditor extends BaseTextEditor {
public setHeight(height: number) {
if (this._dimension) {
this._dimension.height = height;
this.layout();
this.layout(this._dimension);
}
}
public get scrollHeight(): number {
let editorWidget = this.getControl() as ICodeEditor;
return editorWidget.getScrollHeight();
}
public setHeightToScrollHeight(): void {
let editorWidget = this.getControl() as ICodeEditor;
if (!this._config) {
this._config = new Configuration(undefined, editorWidget.getDomNode());
}
let editorHeightUsingLines = this._config.editor.lineHeight * editorWidget.getModel().getLineCount();
let editorHeightUsingMinHeight = Math.max(editorHeightUsingLines, this._minHeight);
this.setHeight(editorHeightUsingMinHeight);
}
public setMinimumHeight(height: number) : void {
this._minHeight = height;
}
}

View File

@@ -71,6 +71,13 @@ export class TreeDataTemplate extends Disposable {
}
}
public set enableCheckbox(value: boolean) {
if (value === undefined) {
value = true;
}
this._checkbox.disabled = !value;
}
public get checkbox(): HTMLInputElement {
return this._checkbox;
}
@@ -155,6 +162,7 @@ export class TreeComponentRenderer extends Disposable implements IRenderer {
templateData.label.textContent = label;
templateData.root.title = label;
templateData.checkboxState = this.getCheckboxState(treeNode);
templateData.enableCheckbox = treeNode.enabled;
}
private getCheckboxState(treeNode: ITreeComponentItem): TreeCheckboxState {

View File

@@ -218,7 +218,7 @@ export class ActiveConnectionsFilterAction extends Action {
public static LABEL = localize('activeConnections', 'Show Active Connections');
private static enabledClass = 'active-connections-action';
private static disabledClass = 'icon server-page';
private static clearAllLabel = localize('clearAll', 'Clear All');
private static showAllConnectionsLabel = localize('showAllConnections', 'Show All Connections');
private _isSet: boolean;
public static readonly ACTIVE = 'active';
public get isSet(): boolean {
@@ -249,7 +249,7 @@ export class ActiveConnectionsFilterAction extends Action {
// show active connections in the tree
this.view.showFilteredTree(ActiveConnectionsFilterAction.ACTIVE);
this.isSet = true;
this.label = ActiveConnectionsFilterAction.clearAllLabel;
this.label = ActiveConnectionsFilterAction.showAllConnectionsLabel;
} else {
// show full tree
this.view.refreshTree();

View File

@@ -74,8 +74,9 @@ export class MessagePanelState {
}
export class MessagePanel extends ViewletPanel {
private messageLineCountMap = new Map<IResultMessage, number>();
private ds = new MessageDataSource();
private renderer = new MessageRenderer();
private renderer = new MessageRenderer(this.messageLineCountMap);
private model = new Model();
private controller: MessageController;
private container = $('div message-tree').getHTMLElement();
@@ -143,29 +144,40 @@ export class MessagePanel extends ViewletPanel {
private onMessage(message: IResultMessage | IResultMessage[]) {
let hasError = false;
let lines: number;
if (isArray(message)) {
hasError = message.find(e => e.isError) ? true : false;
lines = message.reduce((currentTotal, resultMessage) => currentTotal + this.countMessageLines(resultMessage), 0);
this.model.messages.push(...message);
} else {
hasError = message.isError;
lines = this.countMessageLines(message);
this.model.messages.push(message);
}
this.maximumBodySize += lines * 22;
if (hasError) {
this.setExpanded(true);
}
if (this.state.scrollPosition) {
this.tree.refresh(this.model).then(() => {
this.tree.setScrollPosition(1);
// Restore the previous scroll position when switching between tabs
this.tree.setScrollPosition(this.state.scrollPosition);
});
} else {
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
// Scroll to the end if the user was already at the end otherwise leave the current scroll position
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
});
}
this.maximumBodySize = this.model.messages.length * 22;
}
private countMessageLines(resultMessage: IResultMessage): number {
let lines = resultMessage.message.split('\n').length;
this.messageLineCountMap.set(resultMessage, lines);
return lines;
}
private reset() {
@@ -220,8 +232,15 @@ class MessageDataSource implements IDataSource {
}
class MessageRenderer implements IRenderer {
constructor(private messageLineCountMap: Map<IResultMessage, number>) {
}
getHeight(tree: ITree, element: any): number {
return 22;
const lineHeight = 22;
if (this.messageLineCountMap.has(element)) {
return lineHeight * this.messageLineCountMap.get(element);
}
return lineHeight;
}
getTemplateId(tree: ITree, element: any): string {
@@ -258,7 +277,7 @@ class MessageRenderer implements IRenderer {
renderElement(tree: ITree, element: IResultMessage, templateId: string, templateData: IMessageTemplate | IBatchTemplate): void {
if (templateId === TemplateIds.MESSAGE || templateId === TemplateIds.ERROR) {
let data: IMessageTemplate = templateData;
data.message.innerText = element.message.replace(/(\r\n|\n|\r)/g, ' ');
data.message.innerText = element.message;
} else if (templateId === TemplateIds.BATCH) {
let data = templateData as IBatchTemplate;
data.timeStamp.innerText = element.time;

View File

@@ -76,7 +76,7 @@ class ResultsView implements IPanelView {
this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize);
}
this.panelViewlet.resizePanel(this.gridPanel, panelSize);
})
});
// once the user changes the sash we should stop trying to resize the grid
once(this.panelViewlet.onDidSashChange)(e => {
this.needsGridResize = false;
@@ -203,12 +203,14 @@ export class QueryResultsView {
if (!this._panelView.contains(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) {
this._panelView.showTab(this.input.state.activeTab);
}

View File

@@ -26,6 +26,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
import { TPromise } from 'vs/base/common/winjs.base';
import { Deferred } from 'sql/base/common/promise';
export interface IEditSessionReadyEvent {
ownerUri: string;
@@ -69,11 +70,11 @@ export default class QueryRunner {
private _hasCompleted: boolean = false;
private _batchSets: sqlops.BatchSummary[] = [];
private _eventEmitter = new EventEmitter();
private _isQueryPlan: boolean;
private _isQueryPlan: boolean;
public get isQueryPlan(): boolean { return this._isQueryPlan; }
private _planXml: string;
public get planXml(): string { return this._planXml; }
private _planXml = new Deferred<string>();
public get planXml(): Thenable<string> { return this._planXml.promise; }
private _onMessage = new Emitter<sqlops.IResultMessage>();
private _debouncedMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => {
@@ -183,6 +184,7 @@ export default class QueryRunner {
this._echoedResultSet.clear();
this._debouncedMessage.clear();
this._debouncedResultSet.clear();
this._planXml = new Deferred<string>();
let ownerUri = this.uri;
this._batchSets = [];
this._hasCompleted = false;
@@ -342,7 +344,11 @@ export default class QueryRunner {
}
// handle getting queryPlanxml if we need too
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) {
// Store the result set in the batch and emit that a result set has completed

View File

@@ -124,7 +124,7 @@ export class QueryEditorService implements IQueryEditorService {
try {
// Create file path and file URI
let objectName = schemaName ? schemaName + '.' + tableName : tableName;
let filePath = this.createEditDataFileName(objectName);
let filePath = this.createPrefixedSqlFilePath(objectName);
let docUri: URI = URI.from({ scheme: Schemas.untitled, path: filePath });
// Create a sql document pane with accoutrements
@@ -265,45 +265,26 @@ export class QueryEditorService implements IQueryEditorService {
////// Private functions
private createUntitledSqlFilePath(): string {
let sqlFileName = (counter: number): string => {
return `${untitledFilePrefix}${counter}`;
};
let counter = 1;
// Get document name and check if it exists
let filePath = sqlFileName(counter);
while (fs.existsSync(filePath)) {
counter++;
filePath = sqlFileName(counter);
}
// check if this document name already exists in any open documents
let untitledEditors = this._untitledEditorService.getAll();
while (untitledEditors.find(x => x.getName().toUpperCase() === filePath.toUpperCase())) {
counter++;
filePath = sqlFileName(counter);
}
return filePath;
return this.createPrefixedSqlFilePath(untitledFilePrefix);
}
private createEditDataFileName(tableName: string): string {
let editDataFileName = (counter: number): string => {
return encodeURIComponent(`${tableName}_${counter}`);
private createPrefixedSqlFilePath(prefix: string): string {
let prefixFileName = (counter: number): string => {
return `${prefix}_${counter}`;
};
let counter = 1;
// Get document name and check if it exists
let filePath = editDataFileName(counter);
let filePath = prefixFileName(counter);
while (fs.existsSync(filePath)) {
counter++;
filePath = editDataFileName(counter);
filePath = prefixFileName(counter);
}
let untitledEditors = this._untitledEditorService.getAll();
while (untitledEditors.find(x => x.getName().toUpperCase() === filePath.toUpperCase())) {
counter++;
filePath = editDataFileName(counter);
filePath = prefixFileName(counter);
}
return filePath;

View File

@@ -41,10 +41,12 @@ export class QueryPlanView implements IPanelView {
}
}
container.appendChild(this.container);
container.style.overflow = 'scroll';
this.container.style.overflow = 'scroll';
}
public layout(dimension: Dimension): void {
this.container.style.width = dimension.width + 'px';
this.container.style.height = dimension.height + 'px';
}
public showPlan(xml: string) {

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

@@ -1266,6 +1266,16 @@ declare module 'sqlops' {
Last = 16
}
export enum JobExecutionStatus {
Executing = 1,
WaitingForWorkerThread = 2,
BetweenRetries = 3,
Idle = 4,
Suspended = 5,
WaitingForStepToFinish = 6,
PerformingCompletionAction = 7
}
export interface AgentJobInfo {
name: string;
owner: string;
@@ -1371,8 +1381,6 @@ declare module 'sqlops' {
retriesAttempted: string;
server: string;
steps: AgentJobStep[];
schedules: AgentJobScheduleInfo[];
alerts: AgentAlertInfo[];
}
export interface AgentProxyInfo {
@@ -1441,7 +1449,10 @@ declare module 'sqlops' {
}
export interface AgentJobHistoryResult extends ResultStatus {
jobs: AgentJobHistoryInfo[];
histories: AgentJobHistoryInfo[];
steps: AgentJobStepInfo[];
schedules: AgentJobScheduleInfo[];
alerts: AgentAlertInfo[];
}
export interface CreateAgentJobResult extends ResultStatus {
@@ -1529,7 +1540,7 @@ declare module 'sqlops' {
export interface AgentServicesProvider extends DataProvider {
// Job management methods
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>;
createJob(ownerUri: string, jobInfo: AgentJobInfo): Thenable<CreateAgentJobResult>;
updateJob(ownerUri: string, originalJobName: string, jobInfo: AgentJobInfo): Thenable<UpdateAgentJobResult>;

View File

@@ -58,6 +58,7 @@ declare module 'sqlops' {
export class TreeComponentItem extends vscode.TreeItem {
checked?: boolean;
enabled?: boolean;
}
export interface ComponentBuilder<T extends Component> {
@@ -595,6 +596,10 @@ declare module 'sqlops' {
* The languge mode for this text editor. The language mode is SQL by default.
*/
languageMode?: string;
/**
* Minimum height for editor component
*/
minimumHeight?: number;
}
export interface ButtonProperties extends ComponentProperties, ComponentWithIcon {
@@ -717,6 +722,16 @@ declare module 'sqlops' {
*/
readonly onEditorCreated: vscode.Event<any>;
/**
* Toggle for whether the editor should be automatically resized or not
*/
isAutoResizable: boolean;
/**
* Minimum height for editor component
*/
minimumHeight: number;
}
export interface ButtonComponent extends Component, ButtonProperties {

View File

@@ -95,6 +95,16 @@ export enum JobCompletionActionCondition {
Always = 3
}
export enum JobExecutionStatus {
Executing = 1,
WaitingForWorkerThread = 2,
BetweenRetries = 3,
Idle = 4,
Suspended = 5,
WaitingForStepToFinish = 6,
PerformingCompletionAction = 7
}
export enum AlertType {
sqlServerEvent = 1,
sqlServerPerformanceCondition = 2,

View File

@@ -578,8 +578,8 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
/**
* Get a Agent Job's history
*/
public $getJobHistory(handle: number, ownerUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
return this._resolveProvider<sqlops.AgentServicesProvider>(handle).getJobHistory(ownerUri, jobID);
public $getJobHistory(handle: number, ownerUri: string, jobID: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult> {
return this._resolveProvider<sqlops.AgentServicesProvider>(handle).getJobHistory(ownerUri, jobID, jobName);
}
/**

View File

@@ -905,6 +905,22 @@ class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent {
return this.properties['editorUri'];
}
public get isAutoResizable(): boolean {
return this.properties['isAutoResizable'];
}
public set isAutoResizable(v: boolean) {
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> {
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
return emitter && emitter.event;

View File

@@ -164,7 +164,7 @@ export class ExtHostTreeView<T> extends vsTreeExt.ExtHostTreeView<T> {
protected createTreeItem(element: T, extensionTreeItem: sqlops.TreeComponentItem, parent?: vsTreeExt.TreeNode): ITreeComponentItem {
let item = super.createTreeItem(element, extensionTreeItem, parent);
item = Object.assign({}, item, { checked: extensionTreeItem.checked });
item = Object.assign({}, item, { checked: extensionTreeItem.checked, enabled: extensionTreeItem.enabled });
return item;
}
}

View File

@@ -350,8 +350,8 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult> {
return self._proxy.$getJobs(handle, connectionUri);
},
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
return self._proxy.$getJobHistory(handle, connectionUri, jobID);
getJobHistory(connectionUri: string, jobID: string, jobName: string): Thenable<sqlops.AgentJobHistoryResult> {
return self._proxy.$getJobHistory(handle, connectionUri, jobID, jobName);
},
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
return self._proxy.$jobAction(handle, connectionUri, jobName, action);

View File

@@ -422,6 +422,7 @@ export function createApiFactory(
WeekDays: sqlExtHostTypes.WeekDays,
NotifyMethods: sqlExtHostTypes.NotifyMethods,
JobCompletionActionCondition: sqlExtHostTypes.JobCompletionActionCondition,
JobExecutionStatus: sqlExtHostTypes.JobExecutionStatus,
AlertType: sqlExtHostTypes.AlertType,
FrequencyTypes: sqlExtHostTypes.FrequencyTypes,
FrequencySubDayTypes: sqlExtHostTypes.FrequencySubDayTypes,

View File

@@ -358,7 +358,7 @@ export abstract class ExtHostDataProtocolShape {
/**
* 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

View File

@@ -7,6 +7,7 @@ import { ITreeViewDataProvider, ITreeItem } from 'vs/workbench/common/views';
export interface ITreeComponentItem extends ITreeItem {
checked?: boolean;
enabled?: boolean;
onCheckedChanged?: (checked: boolean) => void;
children?: ITreeComponentItem[];
}

View File

@@ -298,7 +298,7 @@ ${this.description}
if (!changelogUrl) {
if (this.type === LocalExtensionType.System) {
return TPromise.as('Please check the [VS Code Release Notes](command:update.showCurrentReleaseNotes) for changes to the built-in extensions.');
return TPromise.as('Please check the [Azure Data Studio Release Notes](command:update.showCurrentReleaseNotes) for changes to the built-in extensions.');
}
return TPromise.wrapError<string>(new Error('not available'));