mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 11:01:37 -05:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
619c816e7f | ||
|
|
3d6fb7a8fa | ||
|
|
891624c085 | ||
|
|
a7c4686980 | ||
|
|
93aa052856 | ||
|
|
61d05f6782 | ||
|
|
60ccae48f1 | ||
|
|
4b454581fd | ||
|
|
f0aadebd2c | ||
|
|
d3f0ac7954 | ||
|
|
134f76c17f | ||
|
|
b9c877a109 | ||
|
|
aa243a8aae | ||
|
|
e5c1c6f544 | ||
|
|
9e9f85079e | ||
|
|
ae8e2e1f89 | ||
|
|
b2c70e9301 | ||
|
|
e022f4a0d1 | ||
|
|
8bc32e6371 | ||
|
|
f739c47984 | ||
|
|
13fb9fdfd2 | ||
|
|
9a5f51bfbf | ||
|
|
06bab6a38c | ||
|
|
c35a14d8fd | ||
|
|
b914073147 | ||
|
|
04ae18143b | ||
|
|
efe8e81b6e | ||
|
|
c33ddfabf9 | ||
|
|
8ec5451e64 | ||
|
|
20853ddf7e | ||
|
|
091d4cb924 | ||
|
|
cd0210c88a | ||
|
|
ed10f984b6 |
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@@ -89,7 +89,8 @@
|
||||
"skipFiles": [
|
||||
"**/winjs*.js"
|
||||
],
|
||||
"webRoot": "${workspaceFolder}"
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"timeout": 15000
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||
"version": "1.4.0-alpha.20",
|
||||
"version": "1.4.0-alpha.23",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-netcoreapp2.0.zip",
|
||||
"Windows_64": "win-x64-netcoreapp2.0.zip",
|
||||
"OSX": "osx-x64-netcoreapp2.0.tar.gz",
|
||||
"CentOS_7": "rhel-x64-netcoreapp2.0.tar.gz",
|
||||
"Debian_8": "rhel-x64-netcoreapp2.0.tar.gz",
|
||||
"Fedora_23": "rhel-x64-netcoreapp2.0.tar.gz",
|
||||
"OpenSUSE_13_2": "rhel-x64-netcoreapp2.0.tar.gz",
|
||||
"RHEL_7": "rhel-x64-netcoreapp2.0.tar.gz",
|
||||
"SLES_12_2": "rhel-x64-netcoreapp2.0.tar.gz",
|
||||
"Ubuntu_14": "rhel-x64-netcoreapp2.0.tar.gz",
|
||||
"Ubuntu_16": "rhel-x64-netcoreapp2.0.tar.gz"
|
||||
"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"
|
||||
},
|
||||
"installDirectory": "../sqltoolsservice/{#platform#}/{#version#}",
|
||||
"executableFiles": ["MicrosoftSqlToolsServiceLayer.exe", "MicrosoftSqlToolsServiceLayer"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sqlops",
|
||||
"version": "0.28.3",
|
||||
"version": "0.28.4",
|
||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
"darwinBundleIdentifier": "com.sqlopsstudio.oss",
|
||||
"reportIssueUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=customer%20reported%20issue",
|
||||
"requestFeatureUrl": "https://github.com/Microsoft/sqlopsstudio/issues/new?labels=feature-request",
|
||||
"privacyStatementUrl": "https://privacy.microsoft.com/en-us/privacystatement",
|
||||
"telemetryOptOutUrl": "https://github.com/Microsoft/sqlopsstudio/wiki/How-to-Disable-Telemetry-Reporting",
|
||||
"urlProtocol": "sqlops",
|
||||
"enableTelemetry": true,
|
||||
"aiConfig": {
|
||||
|
||||
3
samples/sqlservices/.gitignore
vendored
Normal file
3
samples/sqlservices/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
*.vsix
|
||||
typings/sqlops.proposed.d.ts
|
||||
57
samples/sqlservices/.vscode/launch.json
vendored
Normal file
57
samples/sqlservices/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// A launch configuration that launches the extension inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
|
||||
// To debug the extension:
|
||||
// 1. please install the "SQL Operations Studio Debug" extension into VSCode
|
||||
// 2. Ensure sqlops is added to your path:
|
||||
// - open SQL Operations Studio
|
||||
// - run the command "Install 'sqlops' command in PATH"
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "Debug in SqlOps install",
|
||||
"type": "sqlopsExtensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "sqlops",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"name": "Attach to Ops Studio",
|
||||
"protocol": "inspector",
|
||||
"port": 5870,
|
||||
"restart": true,
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "",
|
||||
"timeout": 25000
|
||||
},
|
||||
{
|
||||
"name": "Debug in enlistment",
|
||||
"type": "sqlopsExtensionHost",
|
||||
"request": "launch",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.bat"
|
||||
},
|
||||
"osx": {
|
||||
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.sh"
|
||||
},
|
||||
"linux": {
|
||||
"runtimeExecutable": "${workspaceFolder}/../../scripts/sql.sh"
|
||||
},
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"timeout": 20000
|
||||
}
|
||||
]
|
||||
}
|
||||
14
samples/sqlservices/.vscode/tasks.json
vendored
Normal file
14
samples/sqlservices/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "gulp",
|
||||
"task": "build",
|
||||
"problemMatcher": [
|
||||
"$gulp-tsc"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
4
samples/sqlservices/.vscodeignore
Normal file
4
samples/sqlservices/.vscodeignore
Normal file
@@ -0,0 +1,4 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
.gitignore
|
||||
vsc-extension-quickstart.md
|
||||
1
samples/sqlservices/README.md
Normal file
1
samples/sqlservices/README.md
Normal file
@@ -0,0 +1 @@
|
||||
This is a sample extension that will show some basic model-backed UI scenarios. The long-term goal is to use SQL Service querying (e.g. see if Agent and other services are running) and visualize in interesting ways. Additional suggestions for improving this sample are welcome.
|
||||
14
samples/sqlservices/gulpfile.js
Normal file
14
samples/sqlservices/gulpfile.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// NOTE: These are es6 gulpfiles
|
||||
|
||||
// Basic build tasks
|
||||
require('./tasks/buildtasks');
|
||||
|
||||
// VSIX generation tasks
|
||||
require('./tasks/packagetasks');
|
||||
7118
samples/sqlservices/package-lock.json
generated
Normal file
7118
samples/sqlservices/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
81
samples/sqlservices/package.json
Normal file
81
samples/sqlservices/package.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"name": "sqlservices",
|
||||
"displayName": "sqlservices",
|
||||
"description": "Lists SQL Server service status in the management dashboard for a server",
|
||||
"version": "0.0.1",
|
||||
"publisher": "demo",
|
||||
"engines": {
|
||||
"vscode": "^1.21.0",
|
||||
"sqlops": "*"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"*"
|
||||
],
|
||||
"main": "./out/src/extension",
|
||||
"contributes": {
|
||||
"dashboard.tabs": [
|
||||
{
|
||||
"id": "sqlservices.tab",
|
||||
"title": "sqlservices",
|
||||
"icon": {
|
||||
"light": "./out/src/media/insights.svg",
|
||||
"dark": "./out/src/media/insights_inverse.svg"
|
||||
},
|
||||
"description": "Shows available services running in the SQL Server instance",
|
||||
"container": {
|
||||
"nav-section": [
|
||||
{
|
||||
"id": "sqlservices",
|
||||
"title": "Services",
|
||||
"gridItemConfig": {
|
||||
"sizex": 2,
|
||||
"sizey": 1
|
||||
},
|
||||
"container": {
|
||||
"modelview-container": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "splitPanel",
|
||||
"title": "SplitPanel",
|
||||
"gridItemConfig": {
|
||||
"sizex": 2,
|
||||
"sizey": 1
|
||||
},
|
||||
"container": {
|
||||
"modelview-container": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp build",
|
||||
"compile": "gulp compile",
|
||||
"watch": "gulp watch",
|
||||
"typings": "gulp copytypings",
|
||||
"postinstall": "node ./node_modules/vscode/bin/install && node ./node_modules/sqlops/bin/install && gulp copytypings"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-nls": "^3.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^7.0.43",
|
||||
"child-process-promise": "^2.2.1",
|
||||
"del": "^3.0.0",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-color": "0.0.1",
|
||||
"gulp-sourcemaps": "^2.6.4",
|
||||
"gulp-tslint": "^6.0.2",
|
||||
"gulp-typescript": "^3.2.4",
|
||||
"sqlops": "github:anthonydresser/sqlops-extension-sqlops",
|
||||
"tslint": "^3.14.0",
|
||||
"typescript": "^2.6.1",
|
||||
"vscode": "^1.1.14"
|
||||
}
|
||||
}
|
||||
9
samples/sqlservices/sql/query.sql
Normal file
9
samples/sqlservices/sql/query.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
/*
|
||||
* This file should contain SQL code that returns a result set
|
||||
* To get started, build your own queries in SQL Operations Studio and click the
|
||||
* "View as Chart" button to get the correct chart format. Then choose "Create Insight"
|
||||
* and update the package.json with the JSON contents, and this file with the query
|
||||
* used to generate the chart.
|
||||
*/
|
||||
select 'My Label' as [Label], 1 as [Value]
|
||||
10
samples/sqlservices/src/constants.ts
Normal file
10
samples/sqlservices/src/constants.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// CONFIG VALUES ///////////////////////////////////////////////////////////
|
||||
export const extensionConfigSectionName = 'sqlservices';
|
||||
export const configLogDebugInfo = 'logDebugInfo';
|
||||
109
samples/sqlservices/src/controllers/mainController.ts
Normal file
109
samples/sqlservices/src/controllers/mainController.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as Utils from '../utils';
|
||||
import * as vscode from 'vscode';
|
||||
import SplitPropertiesPanel from './splitPropertiesPanel';
|
||||
|
||||
/**
|
||||
* The main controller class that initializes the extension
|
||||
*/
|
||||
export default class MainController implements vscode.Disposable {
|
||||
|
||||
constructor(protected context: vscode.ExtensionContext) {
|
||||
|
||||
}
|
||||
|
||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||
|
||||
public dispose(): void {
|
||||
this.deactivate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates the extension
|
||||
*/
|
||||
public deactivate(): void {
|
||||
Utils.logDebug('Main controller deactivated');
|
||||
}
|
||||
|
||||
public activate(): Promise<boolean> {
|
||||
this.registerSqlServicesModelView();
|
||||
this.registerSplitPanelModelView();
|
||||
|
||||
sqlops.tasks.registerTask('sqlservices.clickTask', (profile) => {
|
||||
vscode.window.showInformationMessage(`Clicked from profile ${profile.serverName}.${profile.databaseName}`);
|
||||
});
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
private registerSqlServicesModelView(): void {
|
||||
sqlops.dashboard.registerModelViewProvider('sqlservices', async (view) => {
|
||||
let flexModel = view.modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'row',
|
||||
alignItems: 'center'
|
||||
}).withItems([
|
||||
// 1st child panel with N cards
|
||||
view.modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
})
|
||||
.withItems([
|
||||
view.modelBuilder.card()
|
||||
.withProperties<sqlops.CardProperties>({
|
||||
label: 'label1',
|
||||
value: 'value1',
|
||||
actions: [{ label: 'action', taskId: 'sqlservices.clickTask' }]
|
||||
})
|
||||
.component()
|
||||
]).component(),
|
||||
// 2nd child panel with N cards
|
||||
view.modelBuilder.flexContainer()
|
||||
.withLayout({ flexFlow: 'column' })
|
||||
.withItems([
|
||||
view.modelBuilder.card()
|
||||
.withProperties<sqlops.CardProperties>({
|
||||
label: 'label2',
|
||||
value: 'value2',
|
||||
actions: [{ label: 'action', taskId: 'sqlservices.clickTask' }]
|
||||
})
|
||||
.component()
|
||||
]).component()
|
||||
], { flex: '1 1 50%' })
|
||||
.component();
|
||||
await view.initializeModel(flexModel);
|
||||
});
|
||||
}
|
||||
|
||||
private registerSplitPanelModelView(): void {
|
||||
sqlops.dashboard.registerModelViewProvider('splitPanel', async (view) => {
|
||||
let numPanels = 3;
|
||||
let splitPanel = new SplitPropertiesPanel(view, numPanels);
|
||||
await view.initializeModel(splitPanel.modelBase);
|
||||
|
||||
// Add a bunch of cards after an initial timeout
|
||||
setTimeout(async () => {
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
let panel = i % numPanels;
|
||||
let card = view.modelBuilder.card().component();
|
||||
card.label = `label${i.toString()}`;
|
||||
|
||||
splitPanel.addItem(card, panel);
|
||||
}
|
||||
|
||||
}, 0);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
43
samples/sqlservices/src/controllers/splitPropertiesPanel.ts
Normal file
43
samples/sqlservices/src/controllers/splitPropertiesPanel.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
/**
|
||||
* The main controller class that initializes the extension
|
||||
*/
|
||||
export default class SplitPropertiesPanel {
|
||||
private panels: sqlops.FlexContainer[];
|
||||
private _modelBase: sqlops.FlexContainer;
|
||||
constructor(view: sqlops.ModelView, numPanels: number) {
|
||||
this.panels = [];
|
||||
let ratio = Math.round(100 / numPanels);
|
||||
for (let i = 0; i < numPanels; i++) {
|
||||
this.panels.push(view.modelBuilder.flexContainer()
|
||||
.withLayout({ flexFlow: 'column' }).component());
|
||||
}
|
||||
this._modelBase = view.modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'row'
|
||||
}).withItems(this.panels, {
|
||||
flex: `0 1 ${ratio}%`
|
||||
})
|
||||
.component();
|
||||
}
|
||||
|
||||
public get modelBase(): sqlops.Component {
|
||||
return this._modelBase;
|
||||
}
|
||||
|
||||
public addItem(item: sqlops.Component, panel: number): void {
|
||||
if (panel >= this.panels.length) {
|
||||
throw new Error(`Cannot add to panel ${panel} as only ${this.panels.length - 1} panels defined`);
|
||||
}
|
||||
this.panels[panel].addItem(item, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
37
samples/sqlservices/src/extension.ts
Normal file
37
samples/sqlservices/src/extension.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import MainController from './controllers/mainController';
|
||||
|
||||
let mainController: MainController;
|
||||
|
||||
export function activate(context: vscode.ExtensionContext): Promise<boolean> {
|
||||
let activations: Promise<boolean>[] = [];
|
||||
|
||||
// Start the main controller
|
||||
mainController = new MainController(context);
|
||||
context.subscriptions.push(mainController);
|
||||
activations.push(mainController.activate());
|
||||
|
||||
return Promise.all(activations)
|
||||
.then((results: boolean[]) => {
|
||||
for (let result of results) {
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
if (mainController) {
|
||||
mainController.deactivate();
|
||||
}
|
||||
}
|
||||
1
samples/sqlservices/src/media/insights.svg
Normal file
1
samples/sqlservices/src/media/insights.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#212121;}</style></defs><title>insights</title><path class="cls-1" d="M15,4V8H14V5.71L9.49,10.2l-2-2L2,13.71V14H15v1H1V1H2V12.29L7.49,6.8l2,2L13.28,5H11V4Z"/></svg>
|
||||
|
After Width: | Height: | Size: 282 B |
1
samples/sqlservices/src/media/insights_inverse.svg
Normal file
1
samples/sqlservices/src/media/insights_inverse.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>insights_inverse</title><path class="cls-1" d="M15,4V8H14V5.71L9.49,10.2l-2-2L2,13.71V14H15v1H1V1H2V12.29L7.49,6.8l2,2L13.28,5H11V4Z"/></svg>
|
||||
|
After Width: | Height: | Size: 287 B |
52
samples/sqlservices/src/utils.ts
Normal file
52
samples/sqlservices/src/utils.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as fs from 'fs-extra';
|
||||
import * as handlebars from 'handlebars';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as Constants from './constants';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
/**
|
||||
* Helper to log messages to the developer console if enabled
|
||||
* @param msg Message to log to the console
|
||||
*/
|
||||
export function logDebug(msg: any): void {
|
||||
let config = vscode.workspace.getConfiguration(Constants.extensionConfigSectionName);
|
||||
let logDebugInfo = config[Constants.configLogDebugInfo];
|
||||
if (logDebugInfo === true) {
|
||||
let currentTime = new Date().toLocaleTimeString();
|
||||
let outputMsg = '[' + currentTime + ']: ' + msg ? msg.toString() : '';
|
||||
console.log(outputMsg);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderTemplateHtml(extensionPath: string, templateName: string, templateValues: object): Thenable<string> {
|
||||
let templatePath = path.join(extensionPath, 'resources', templateName);
|
||||
|
||||
// 1) Read the template from the disk
|
||||
// 2) Compile it as a handlebars template and render the HTML
|
||||
// 3) On failure, return a simple string as an error
|
||||
return fs.readFile(templatePath, 'utf-8')
|
||||
.then(templateText => {
|
||||
let template = handlebars.compile(templateText);
|
||||
return template(templateValues);
|
||||
})
|
||||
.then(
|
||||
undefined,
|
||||
error => {
|
||||
logDebug(error);
|
||||
return localize('errorLoadingTab', 'An error occurred while loading the tab');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
124
samples/sqlservices/tasks/buildtasks.js
Normal file
124
samples/sqlservices/tasks/buildtasks.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
"use strict";
|
||||
|
||||
let del = require('del');
|
||||
let gulp = require('gulp');
|
||||
let srcmap = require('gulp-sourcemaps');
|
||||
let tslint = require('gulp-tslint');
|
||||
let ts = require('gulp-typescript');
|
||||
let cproc = require('child_process');
|
||||
let os = require('os');
|
||||
|
||||
let config = require('./config');
|
||||
let tsProject = ts.createProject('tsconfig.json');
|
||||
|
||||
|
||||
// GULP TASKS //////////////////////////////////////////////////////////////
|
||||
gulp.task('clean', function(done) {
|
||||
return del('out', done);
|
||||
});
|
||||
|
||||
gulp.task('lint', () => {
|
||||
return gulp.src([
|
||||
config.paths.project.root + '/src/**/*.ts',
|
||||
config.paths.project.root + '/test/**/*.ts'
|
||||
])
|
||||
.pipe((tslint({
|
||||
formatter: "verbose"
|
||||
})))
|
||||
.pipe(tslint.report());
|
||||
});
|
||||
|
||||
gulp.task('compile:src', function(done) {
|
||||
gulp.src([
|
||||
config.paths.project.root + '/src/**/*.sql',
|
||||
config.paths.project.root + '/src/**/*.svg',
|
||||
config.paths.project.root + '/src/**/*.html'
|
||||
]).pipe(gulp.dest('out/src/'));
|
||||
|
||||
let srcFiles = [
|
||||
config.paths.project.root + '/src/**/*.ts',
|
||||
config.paths.project.root + '/src/**/*.js',
|
||||
config.paths.project.root + '/typings/**/*.ts'
|
||||
];
|
||||
|
||||
return gulp.src(srcFiles)
|
||||
.pipe(srcmap.init())
|
||||
.pipe(tsProject())
|
||||
.on('error', function() {
|
||||
if (process.env.BUILDMACHINE) {
|
||||
done('Extension Tests failed to build. See Above.');
|
||||
process.exit(1);
|
||||
}
|
||||
})
|
||||
.pipe(srcmap.write('.', {
|
||||
sourceRoot: function(file) {
|
||||
return file.cwd + '/src';
|
||||
}
|
||||
}))
|
||||
.pipe(gulp.dest('out/src/'));
|
||||
});
|
||||
|
||||
gulp.task('compile:test', function(done) {
|
||||
let srcFiles = [
|
||||
config.paths.project.root + '/test/**/*.ts',
|
||||
config.paths.project.root + '/typings/**/*.ts'
|
||||
];
|
||||
|
||||
return gulp.src(srcFiles)
|
||||
.pipe(srcmap.init())
|
||||
.pipe(tsProject())
|
||||
.on('error', function() {
|
||||
if(process.env.BUILDMACHINE) {
|
||||
done('Failed to compile test source, see above.');
|
||||
process.exit(1);
|
||||
}
|
||||
})
|
||||
.pipe(srcmap.write('.', {sourceRoot: function(file) { return file.cwd + '/test'; }}))
|
||||
.pipe(gulp.dest('out/test/'));
|
||||
});
|
||||
|
||||
// COMPOSED GULP TASKS /////////////////////////////////////////////////////
|
||||
gulp.task("compile", gulp.series("compile:src", "compile:test"));
|
||||
|
||||
gulp.task("build", gulp.series("clean", "lint", "compile"));
|
||||
|
||||
gulp.task("watch", function() {
|
||||
gulp.watch([config.paths.project.root + '/src/**/*',
|
||||
config.paths.project.root + '/test/**/*.ts'],
|
||||
gulp.series('build'));
|
||||
});
|
||||
|
||||
gulp.task('test', (done) => {
|
||||
let workspace = process.env['WORKSPACE'];
|
||||
if (!workspace) {
|
||||
workspace = process.cwd();
|
||||
}
|
||||
process.env.JUNIT_REPORT_PATH = workspace + '/test-reports/ext_xunit.xml';
|
||||
|
||||
let sqlopsPath = 'sqlops';
|
||||
if (process.env['SQLOPS_DEV']) {
|
||||
let suffix = os.platform === 'win32' ? 'bat' : 'sh';
|
||||
sqlopsPath = `${process.env['SQLOPS_DEV']}/scripts/sql-cli.${suffix}`;
|
||||
}
|
||||
console.log(`Using SQLOPS Path of ${sqlopsPath}`);
|
||||
|
||||
cproc.exec(`${sqlopsPath} --extensionDevelopmentPath="${workspace}" --extensionTestsPath="${workspace}/out/test" --verbose`, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`exec error: ${error}`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`stdout: ${stdout}`);
|
||||
console.log(`stderr: ${stderr}`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('copytypings', function() {
|
||||
return gulp.src(config.paths.project.root + '/../../src/sql/sqlops.proposed.d.ts')
|
||||
.pipe(gulp.dest('typings/'));
|
||||
});
|
||||
24
samples/sqlservices/tasks/config.js
Normal file
24
samples/sqlservices/tasks/config.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
var path = require('path');
|
||||
|
||||
var projectRoot = path.resolve(path.dirname(__dirname));
|
||||
var srcRoot = path.resolve(projectRoot, 'src');
|
||||
var localization = path.resolve(projectRoot, 'localization');
|
||||
|
||||
var config = {
|
||||
paths: {
|
||||
project: {
|
||||
root: projectRoot,
|
||||
localization: localization
|
||||
},
|
||||
extension: {
|
||||
root: srcRoot
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
36
samples/sqlservices/tasks/packagetasks.js
Normal file
36
samples/sqlservices/tasks/packagetasks.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let gulp = require('gulp');
|
||||
let exec = require('child-process-promise').exec;
|
||||
let color = require('gulp-color');
|
||||
|
||||
// CONSTANTS ///////////////////////////////////////////////////////////////
|
||||
let packageVals = require('../package');
|
||||
|
||||
// HELPER FUNCTIONS ////////////////////////////////////////////////////////
|
||||
|
||||
let buildPackage = function(packageName) {
|
||||
// Make sure there are
|
||||
if (!packageVals.repository) {
|
||||
return Promise.reject("Repository field is not defined in package.json");
|
||||
}
|
||||
|
||||
// Initialize the package command with program and command
|
||||
let vsceArgs = [];
|
||||
vsceArgs.push('./node_modules/vsce/out/vsce');
|
||||
vsceArgs.push('package'); // package command
|
||||
|
||||
// Add the package name
|
||||
vsceArgs.push('-o');
|
||||
vsceArgs.push(packageName);
|
||||
|
||||
// Put it all together and execute the command
|
||||
let command = vsceArgs.join(' ');
|
||||
console.log(command);
|
||||
return exec(command);
|
||||
};
|
||||
16
samples/sqlservices/tsconfig.json
Normal file
16
samples/sqlservices/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "ES6",
|
||||
"lib": [ "es6" ],
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"rootDir": "."
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode-test"
|
||||
]
|
||||
}
|
||||
123
samples/sqlservices/tslint.json
Normal file
123
samples/sqlservices/tslint.json
Normal file
@@ -0,0 +1,123 @@
|
||||
{
|
||||
"rules": {
|
||||
"align": [
|
||||
true,
|
||||
"parameters",
|
||||
"statements"
|
||||
],
|
||||
"ban": false,
|
||||
"class-name": true,
|
||||
"comment-format": [
|
||||
true,
|
||||
"check-space"
|
||||
],
|
||||
"curly": true,
|
||||
"eofline": true,
|
||||
"forin": true,
|
||||
"indent": [
|
||||
true,
|
||||
"tabs"
|
||||
],
|
||||
"interface-name": true,
|
||||
"jsdoc-format": true,
|
||||
"label-position": true,
|
||||
"label-undefined": true,
|
||||
"max-line-length": [
|
||||
true,
|
||||
160
|
||||
],
|
||||
"member-access": false,
|
||||
"member-ordering": false,
|
||||
"no-any": false,
|
||||
"no-arg": true,
|
||||
"no-bitwise": true,
|
||||
"no-conditional-assignment": true,
|
||||
"no-consecutive-blank-lines": false,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-construct": true,
|
||||
"no-constructor-vars": false,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-key": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-empty": true,
|
||||
"no-eval": true,
|
||||
"no-inferrable-types": false,
|
||||
"no-internal-module": true,
|
||||
"no-null-keyword": true,
|
||||
"no-require-imports": false,
|
||||
"no-shadowed-variable": true,
|
||||
"no-string-literal": false,
|
||||
"no-switch-case-fall-through": false,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-unreachable": true,
|
||||
"no-unused-expression": false,
|
||||
"no-unused-variable": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-var-keyword": true,
|
||||
"no-var-requires": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-catch",
|
||||
"check-else",
|
||||
"check-finally",
|
||||
"check-whitespace"
|
||||
],
|
||||
"quotemark": [
|
||||
true,
|
||||
"single",
|
||||
"avoid-escape"
|
||||
],
|
||||
"radix": true,
|
||||
"semicolon": true,
|
||||
"switch-default": true,
|
||||
"trailing-comma": [
|
||||
true,
|
||||
{
|
||||
"multiline": "never",
|
||||
"singleline": "never"
|
||||
}
|
||||
],
|
||||
"triple-equals": [
|
||||
true,
|
||||
"allow-null-check"
|
||||
],
|
||||
"typedef": [
|
||||
true,
|
||||
"call-signature",
|
||||
"property-declaration"
|
||||
],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
}
|
||||
],
|
||||
"use-strict": false,
|
||||
"variable-name": [
|
||||
true,
|
||||
"allow-leading-underscore",
|
||||
"ban-keywords"
|
||||
],
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
]
|
||||
}
|
||||
}
|
||||
3689
samples/sqlservices/yarn.lock
Normal file
3689
samples/sqlservices/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@ export interface ICheckboxOptions {
|
||||
enabled?: boolean;
|
||||
checked?: boolean;
|
||||
onChange?: (val: boolean) => void;
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export class Checkbox extends Widget {
|
||||
@@ -27,6 +28,10 @@ export class Checkbox extends Widget {
|
||||
this._el = document.createElement('input');
|
||||
this._el.type = 'checkbox';
|
||||
|
||||
if (opts.ariaLabel) {
|
||||
this._el.setAttribute('aria-label', opts.ariaLabel);
|
||||
}
|
||||
|
||||
this.onchange(this._el, e => {
|
||||
this._onChange.fire(this.checked);
|
||||
});
|
||||
|
||||
@@ -49,6 +49,10 @@ export interface IDropdownOptions extends IDropdownStyles {
|
||||
* Error Message to show if input is not part of the supplied list, only used if strictSelection = false
|
||||
*/
|
||||
errorMessage?: string;
|
||||
/**
|
||||
* Value to use as aria-label for the input box
|
||||
*/
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export interface IDropdownStyles {
|
||||
@@ -124,7 +128,8 @@ export class Dropdown extends Disposable {
|
||||
validation: v => this._inputValidator(v)
|
||||
},
|
||||
placeholder: this._options.placeholder,
|
||||
actions: [this._toggleAction]
|
||||
actions: [this._toggleAction],
|
||||
ariaLabel: this._options.ariaLabel
|
||||
});
|
||||
|
||||
this._register(DOM.addDisposableListener(this._input.inputElement, DOM.EventType.FOCUS, () => {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/cont
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
|
||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export const MODAL_SHOWING_KEY = 'modalShowing';
|
||||
export const MODAL_SHOWING_CONTEXT = new RawContextKey<Array<string>>(MODAL_SHOWING_KEY, []);
|
||||
@@ -155,6 +156,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
modalHeader.div({ class: 'modal-go-back' }, (cellContainer) => {
|
||||
this._backButton = new Button(cellContainer);
|
||||
this._backButton.icon = 'backButtonIcon';
|
||||
this._backButton.title = localize('modalBack', "Back");
|
||||
});
|
||||
}
|
||||
if (this._modalOptions.hasTitleIcon) {
|
||||
|
||||
@@ -43,7 +43,8 @@ export function createOptionElement(option: sqlops.ServiceOption, rowContainer:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ariaLabel: option.displayName
|
||||
});
|
||||
optionWidget.value = optionValue;
|
||||
inputElement = this.findElement(rowContainer, 'input');
|
||||
@@ -55,7 +56,8 @@ export function createOptionElement(option: sqlops.ServiceOption, rowContainer:
|
||||
optionWidget = new InputBox(rowContainer.getHTMLElement(), contextViewService, {
|
||||
validationOptions: {
|
||||
validation: (value: string) => (!value && option.isRequired) ? ({ type: MessageType.ERROR, content: option.displayName + missingErrorMessage }) : null
|
||||
}
|
||||
},
|
||||
ariaLabel: option.displayName
|
||||
});
|
||||
optionWidget.value = optionValue;
|
||||
if (option.valueType === ServiceOptionType.password) {
|
||||
|
||||
@@ -49,7 +49,7 @@ let idPool = 0;
|
||||
|
||||
<div class="tabbedPanel fullsize" #tabbedPanel>
|
||||
<div *ngIf="!options.showTabsWhenOne ? _tabs.length !== 1 : true" class="composite title" #titleContainer>
|
||||
<div class="tabList" #tabList>
|
||||
<div class="tabList" #tabList role="tablist">
|
||||
<div *ngFor="let tab of _tabs">
|
||||
<tab-header [tab]="tab" [showIcon]="options.showIcon" (onSelectTab)='selectTab($event)' (onCloseTab)='closeTab($event)'> </tab-header>
|
||||
</div>
|
||||
@@ -182,7 +182,7 @@ export class PanelComponent extends Disposable implements AfterContentInit, OnIn
|
||||
if (input instanceof TabComponent) {
|
||||
tab = input;
|
||||
} else if (types.isNumber(input)) {
|
||||
tab = this._tabs[input];
|
||||
tab = this._tabs.toArray()[input];
|
||||
} else if (types.isString(input)) {
|
||||
tab = this._tabs.find(i => i.identifier === input);
|
||||
}
|
||||
@@ -226,6 +226,18 @@ export class PanelComponent extends Disposable implements AfterContentInit, OnIn
|
||||
return this._activeTab.identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select on the next tab
|
||||
*/
|
||||
public selectOnNextTab(): void {
|
||||
let activeIndex = this._tabs.toArray().findIndex(i => i === this._activeTab);
|
||||
let nextTabIndex = activeIndex + 1;
|
||||
if (nextTabIndex === this._tabs.length) {
|
||||
nextTabIndex = 0;
|
||||
}
|
||||
this.selectTab(nextTabIndex);
|
||||
}
|
||||
|
||||
private findAndRemoveTabFromMRU(tab: TabComponent): void {
|
||||
let mruIndex = this._mru.findIndex(i => i === tab);
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ export class TabbedPanel extends Disposable implements IThemable {
|
||||
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);
|
||||
let actionbarcontainer = $('.title-actions');
|
||||
@@ -89,6 +90,9 @@ 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-label', tab.title);
|
||||
let tabElement = $('.tab');
|
||||
tabHeaderElement.append(tabElement);
|
||||
let tabLabel = $('a.tabLabel');
|
||||
@@ -114,7 +118,7 @@ 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');
|
||||
this._tabMap.get(this._shownTab).header.removeClass('active').attr('aria-selected', 'false');
|
||||
}
|
||||
|
||||
this._shownTab = id;
|
||||
@@ -122,6 +126,7 @@ export class TabbedPanel extends Disposable implements IThemable {
|
||||
let tab = this._tabMap.get(this._shownTab);
|
||||
tab.label.addClass('active');
|
||||
tab.header.addClass('active');
|
||||
tab.header.attr('aria-selected', 'true');
|
||||
tab.view.render(this.$body.getHTMLElement());
|
||||
this._onTabChange.fire(id);
|
||||
if (this._currentDimensions) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import { CloseTabAction } from './tabActions';
|
||||
selector: 'tab-header',
|
||||
template: `
|
||||
<div #actionHeader class="tab-header" style="flex: 0 0; flex-direction: row;" [class.active]="tab.active" tabindex="0" (keyup)="onKey($event)">
|
||||
<span class="tab" (click)="selectTab(tab)">
|
||||
<span class="tab" (click)="selectTab(tab)" role="tab" [attr.aria-selected]="tab.active" [attr.aria-label]="tab.title">
|
||||
<a class="tabLabel" [class.active]="tab.active" #tabLabel>
|
||||
</a>
|
||||
</span>
|
||||
|
||||
@@ -26,7 +26,7 @@ export class RowDetailView {
|
||||
this._options = mixin(options, this._defaults, false);
|
||||
}
|
||||
|
||||
public init(grid: any) {
|
||||
public init(grid: any): void {
|
||||
this._grid = grid;
|
||||
this._dataView = this._grid.getData();
|
||||
|
||||
@@ -61,17 +61,17 @@ export class RowDetailView {
|
||||
this._options = $.extend(true, {}, this._options, options);
|
||||
}
|
||||
|
||||
public handleClick(e: any, args: any) {
|
||||
public handleClick(e: any, args: any): void {
|
||||
// clicking on a row select checkbox
|
||||
if (this._options.useRowClick || this._grid.getColumns()[args.cell].id === this._options.columnId && $(e.target).hasClass("detailView-toggle")) {
|
||||
if (this._options.useRowClick || this._grid.getColumns()[args.cell].id === this._options.columnId && $(e.target).hasClass('detailView-toggle')) {
|
||||
// if editing, try to commit
|
||||
if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
return;
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
var item = this._dataView.getItem(args.row);
|
||||
let item = this._dataView.getItem(args.row);
|
||||
|
||||
// trigger an event before toggling
|
||||
this.onBeforeRowDetailToggle.notify({
|
||||
@@ -100,36 +100,35 @@ export class RowDetailView {
|
||||
// If we scroll save detail views that go out of cache range
|
||||
public handleScroll(e, args) {
|
||||
|
||||
var range = this._grid.getRenderedRange();
|
||||
let range = this._grid.getRenderedRange();
|
||||
|
||||
var start = (range.top > 0 ? range.top : 0);
|
||||
var end = (range.bottom > this._dataView.getLength() ? range.bottom : this._dataView.getLength());
|
||||
let start: number = (range.top > 0 ? range.top : 0);
|
||||
let end: number = (range.bottom > this._dataView.getLength() ? range.bottom : this._dataView.getLength());
|
||||
if (end <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the item at the top of the view
|
||||
var topMostItem = this._dataView.getItemByIdx(start);
|
||||
let topMostItem = this._dataView.getItemByIdx(start);
|
||||
|
||||
// Check it is a parent item
|
||||
if (topMostItem._parent === undefined)
|
||||
{
|
||||
if (topMostItem._parent === undefined) {
|
||||
// This is a standard row as we have no parent.
|
||||
var nextItem = this._dataView.getItemByIdx(start + 1);
|
||||
if(nextItem !== undefined && nextItem._parent !== undefined)
|
||||
{
|
||||
let nextItem = this._dataView.getItemByIdx(start + 1);
|
||||
if (nextItem !== undefined && nextItem._parent !== undefined) {
|
||||
// This is likely the expanded Detail Row View
|
||||
// Check for safety
|
||||
if(nextItem._parent === topMostItem)
|
||||
{
|
||||
if (nextItem._parent === topMostItem) {
|
||||
this.saveDetailView(topMostItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the bottom most item that is likely to go off screen
|
||||
var bottomMostItem = this._dataView.getItemByIdx(end - 1);
|
||||
let bottomMostItem = this._dataView.getItemByIdx(end - 1);
|
||||
|
||||
// If we are a detailView and we are about to go out of cache view
|
||||
if(bottomMostItem._parent !== undefined)
|
||||
{
|
||||
if (bottomMostItem._parent !== undefined) {
|
||||
this.saveDetailView(bottomMostItem._parent);
|
||||
}
|
||||
}
|
||||
@@ -143,17 +142,17 @@ export class RowDetailView {
|
||||
|
||||
// Collapse all of the open items
|
||||
public collapseAll() {
|
||||
for (var i = this._expandedRows.length - 1; i >= 0; i--) {
|
||||
for (let i = this._expandedRows.length - 1; i >= 0; i--) {
|
||||
this.collapseItem(this._expandedRows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Saves the current state of the detail view
|
||||
public saveDetailView(item) {
|
||||
var view = $('#innerDetailView_' + item.id);
|
||||
let view = $('#innerDetailView_' + item.id);
|
||||
if (view) {
|
||||
var html = $('#innerDetailView_' + item.id).html();
|
||||
if(html !== undefined) {
|
||||
let html = $('#innerDetailView_' + item.id).html();
|
||||
if (html !== undefined) {
|
||||
item._detailContent = html;
|
||||
}
|
||||
}
|
||||
@@ -230,7 +229,7 @@ export class RowDetailView {
|
||||
|
||||
args.itemDetail._detailViewLoaded = true;
|
||||
|
||||
var idxParent = this._dataView.getIdxById(args.itemDetail.id);
|
||||
let idxParent = this._dataView.getIdxById(args.itemDetail.id);
|
||||
this._dataView.updateItem(args.itemDetail.id, args.itemDetail);
|
||||
|
||||
// trigger an event once the post template is finished loading
|
||||
@@ -254,7 +253,7 @@ export class RowDetailView {
|
||||
//////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////
|
||||
public getPaddingItem(parent, offset) {
|
||||
var item: any = {};
|
||||
let item: any = {};
|
||||
|
||||
for (let prop in this._grid.getData()) {
|
||||
item[prop] = null;
|
||||
@@ -284,7 +283,7 @@ export class RowDetailView {
|
||||
item._height = (item._sizePadding * this._grid.getOptions().rowHeight);
|
||||
|
||||
let idxParent = this._dataView.getIdxById(item.id);
|
||||
for (var idx = 1; idx <= item._sizePadding; idx++) {
|
||||
for (let idx = 1; idx <= item._sizePadding; idx++) {
|
||||
this._dataView.insertItem(idxParent + idx, this.getPaddingItem(item, idx));
|
||||
}
|
||||
}
|
||||
@@ -307,12 +306,12 @@ export class RowDetailView {
|
||||
public detailSelectionFormatter(row, cell, value, columnDef, dataContext) {
|
||||
|
||||
if (dataContext._collapsed === undefined) {
|
||||
dataContext._collapsed = true,
|
||||
dataContext._sizePadding = 0, //the required number of pading rows
|
||||
dataContext._height = 0, //the actual height in pixels of the detail field
|
||||
dataContext._isPadding = false,
|
||||
dataContext._parent = undefined,
|
||||
dataContext._offset = 0
|
||||
dataContext._collapsed = true;
|
||||
dataContext._sizePadding = 0; //the required number of pading rows
|
||||
dataContext._height = 0; //the actual height in pixels of the detail field
|
||||
dataContext._isPadding = false;
|
||||
dataContext._parent = undefined;
|
||||
dataContext._offset = 0;
|
||||
}
|
||||
|
||||
if (dataContext._isPadding === true) {
|
||||
@@ -320,9 +319,9 @@ export class RowDetailView {
|
||||
} else if (dataContext._collapsed) {
|
||||
return '<div class=\'detailView-toggle expand\'></div>';
|
||||
} else {
|
||||
var html = [];
|
||||
var rowHeight = this._grid.getOptions().rowHeight;
|
||||
var bottomMargin = 5;
|
||||
let html = [];
|
||||
let rowHeight = this._grid.getOptions().rowHeight;
|
||||
let bottomMargin = 5;
|
||||
|
||||
//V313HAX:
|
||||
//putting in an extra closing div after the closing toggle div and ommiting a
|
||||
@@ -339,7 +338,7 @@ export class RowDetailView {
|
||||
html.push("style='height:", dataContext._height, "px;"); //set total height of padding
|
||||
html.push("top:", rowHeight, "px'>"); //shift detail below 1st row
|
||||
html.push("<div id='detailViewContainer_", dataContext.id, "' class='detail-container' style='max-height:" + (dataContext._height - rowHeight + bottomMargin) + "px'>"); //sub ctr for custom styling
|
||||
html.push("<div id='innerDetailView_" , dataContext.id , "'>" , dataContext._detailContent, "</div></div>");
|
||||
html.push("<div id='innerDetailView_", dataContext.id, "'>", dataContext._detailContent, "</div></div>");
|
||||
//&omit a final closing detail container </div> that would come next
|
||||
|
||||
return html.join('');
|
||||
@@ -351,33 +350,32 @@ export class RowDetailView {
|
||||
if (!item) return;
|
||||
|
||||
// Grad each of the dom items
|
||||
var mainContainer = document.getElementById('detailViewContainer_' + item.id);
|
||||
var cellItem = document.getElementById('cellDetailView_' + item.id);
|
||||
var inner = document.getElementById('innerDetailView_' + item.id);
|
||||
let mainContainer = document.getElementById('detailViewContainer_' + item.id);
|
||||
let cellItem = document.getElementById('cellDetailView_' + item.id);
|
||||
let inner = document.getElementById('innerDetailView_' + item.id);
|
||||
|
||||
if (!mainContainer || !cellItem || !inner) return;
|
||||
|
||||
for (var idx = 1; idx <= item._sizePadding; idx++) {
|
||||
for (let idx = 1; idx <= item._sizePadding; idx++) {
|
||||
this._dataView.deleteItem(item.id + "." + idx);
|
||||
}
|
||||
|
||||
var rowHeight = this._grid.getOptions().rowHeight; // height of a row
|
||||
var lineHeight = 13; //we know cuz we wrote the custom css innit ;)
|
||||
let rowHeight = this._grid.getOptions().rowHeight; // height of a row
|
||||
let lineHeight = 13; //we know cuz we wrote the custom css innit ;)
|
||||
|
||||
// Get the inner Item height as this will be the actual size
|
||||
var itemHeight = inner.clientHeight;
|
||||
let itemHeight = inner.clientHeight;
|
||||
|
||||
// Now work out how many rows
|
||||
var rowCount = Math.ceil(itemHeight / rowHeight) + 1;
|
||||
let rowCount = Math.ceil(itemHeight / rowHeight) + 1;
|
||||
|
||||
item._sizePadding = Math.ceil(((rowCount * 2) * lineHeight) / rowHeight);
|
||||
item._height = (item._sizePadding * rowHeight);
|
||||
|
||||
// If the padding is now more than the original minRowBuff we need to increase it
|
||||
if (this._grid.getOptions().minRowBuffer < item._sizePadding)
|
||||
{
|
||||
if (this._grid.getOptions().minRowBuffer < item._sizePadding) {
|
||||
// Update the minRowBuffer so that the view doesn't disappear when it's at top of screen + the original default 3
|
||||
this._grid.getOptions().minRowBuffer =item._sizePadding + 3;
|
||||
this._grid.getOptions().minRowBuffer = item._sizePadding + 3;
|
||||
}
|
||||
|
||||
mainContainer.setAttribute("style", "max-height: " + item._height + "px");
|
||||
@@ -386,7 +384,7 @@ export class RowDetailView {
|
||||
}
|
||||
|
||||
let idxParent = this._dataView.getIdxById(item.id);
|
||||
for (var idx = 1; idx <= item._sizePadding; idx++) {
|
||||
for (let idx = 1; idx <= item._sizePadding; idx++) {
|
||||
this._dataView.insertItem(idxParent + idx, this.getPaddingItem(item, idx));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { mixin } from 'vs/base/common/objects';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Dimension } from 'vs/base/browser/builder';
|
||||
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
|
||||
export interface ITableStyles extends IListStyles {
|
||||
tableHeaderBackground?: Color;
|
||||
@@ -27,18 +28,22 @@ function getDefaultOptions<T>(): Slick.GridOptions<T> {
|
||||
};
|
||||
}
|
||||
|
||||
export class Table<T extends Slick.SlickData> implements IThemable {
|
||||
export class Table<T extends Slick.SlickData> extends Widget implements IThemable {
|
||||
private styleElement: HTMLStyleElement;
|
||||
private idPrefix: string;
|
||||
|
||||
private _grid: Slick.Grid<T>;
|
||||
private _columns: Slick.Column<T>[];
|
||||
private _data: TableDataView<T>;
|
||||
private _styleElement: HTMLStyleElement;
|
||||
private _idPrefix: string;
|
||||
private _autoscroll: boolean;
|
||||
private _onRowCountChangeListener: IDisposable;
|
||||
private _container: HTMLElement;
|
||||
private _tableContainer: HTMLElement;
|
||||
|
||||
private _classChangeTimeout: number;
|
||||
|
||||
constructor(parent: HTMLElement, data?: Array<T> | TableDataView<T>, columns?: Slick.Column<T>[], options?: Slick.GridOptions<T>) {
|
||||
super();
|
||||
if (data instanceof TableDataView) {
|
||||
this._data = data;
|
||||
} else {
|
||||
@@ -55,13 +60,28 @@ export class Table<T extends Slick.SlickData> implements IThemable {
|
||||
|
||||
this._container = document.createElement('div');
|
||||
this._container.className = 'monaco-table';
|
||||
this._register(DOM.addDisposableListener(this._container, DOM.EventType.FOCUS, () => {
|
||||
clearTimeout(this._classChangeTimeout);
|
||||
this._classChangeTimeout = setTimeout(() => {
|
||||
DOM.addClass(this._container, 'focused');
|
||||
}, 100);
|
||||
}, true));
|
||||
|
||||
this._register(DOM.addDisposableListener(this._container, DOM.EventType.BLUR, () => {
|
||||
clearTimeout(this._classChangeTimeout);
|
||||
this._classChangeTimeout = setTimeout(() => {
|
||||
DOM.removeClass(this._container, 'focused');
|
||||
}, 100);
|
||||
}, true));
|
||||
|
||||
parent.appendChild(this._container);
|
||||
this._styleElement = DOM.createStyleSheet(this._container);
|
||||
this.styleElement = DOM.createStyleSheet(this._container);
|
||||
this._tableContainer = document.createElement('div');
|
||||
this._container.appendChild(this._tableContainer);
|
||||
this._styleElement = DOM.createStyleSheet(this._container);
|
||||
this.styleElement = DOM.createStyleSheet(this._container);
|
||||
this._grid = new Slick.Grid<T>(this._tableContainer, this._data, this._columns, newOptions);
|
||||
this._idPrefix = this._tableContainer.classList[0];
|
||||
this.idPrefix = this._tableContainer.classList[0];
|
||||
DOM.addClass(this._container, this.idPrefix);
|
||||
this._onRowCountChangeListener = this._data.onRowCountChange(() => this._handleRowCountChange());
|
||||
this._grid.onSort.subscribe((e, args) => {
|
||||
this._data.sort(args);
|
||||
@@ -200,78 +220,77 @@ export class Table<T extends Slick.SlickData> implements IThemable {
|
||||
const content: string[] = [];
|
||||
|
||||
if (styles.tableHeaderBackground) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-header .slick-header-column { background-color: ${styles.tableHeaderBackground}; }`);
|
||||
content.push(`.monaco-table .${this.idPrefix} .slick-header .slick-header-column { background-color: ${styles.tableHeaderBackground}; }`);
|
||||
}
|
||||
|
||||
if (styles.tableHeaderForeground) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-header .slick-header-column { color: ${styles.tableHeaderForeground}; }`);
|
||||
content.push(`.monaco-table .${this.idPrefix} .slick-header .slick-header-column { color: ${styles.tableHeaderForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listFocusBackground) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row .active { background-color: ${styles.listFocusBackground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix}.focused .slick-row .active { background-color: ${styles.listFocusBackground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix}.focused .slick-row .active:hover { background-color: ${styles.listFocusBackground}; }`); // overwrite :hover style in this case!
|
||||
}
|
||||
|
||||
if (styles.listFocusForeground) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row .active { color: ${styles.listFocusForeground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix}.focused .slick-row .active { color: ${styles.listFocusForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listActiveSelectionBackground) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { background-color: ${styles.listActiveSelectionBackground}; }`);
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected:hover { background-color: ${styles.listActiveSelectionBackground}; }`); // overwrite :hover style in this case!
|
||||
content.push(`.monaco-table.${this.idPrefix}.focused .slick-row .selected { background-color: ${styles.listActiveSelectionBackground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix}.focused .slick-row .selected:hover { background-color: ${styles.listActiveSelectionBackground}; }`); // overwrite :hover style in this case!
|
||||
}
|
||||
|
||||
if (styles.listActiveSelectionForeground) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { color: ${styles.listActiveSelectionForeground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix}.focused .slick-row .selected { color: ${styles.listActiveSelectionForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listFocusAndSelectionBackground) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected.active { background-color: ${styles.listFocusAndSelectionBackground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix}.focused .slick-row .selected.active { background-color: ${styles.listFocusAndSelectionBackground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listFocusAndSelectionForeground) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected.active { color: ${styles.listFocusAndSelectionForeground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix}.focused .slick-row .selected.active { color: ${styles.listFocusAndSelectionForeground}; }`);
|
||||
}
|
||||
|
||||
/* Commented out andresse 8/17/2017; keeping for reference as we iterate on the table styling */
|
||||
// if (styles.listInactiveFocusBackground) {
|
||||
// content.push(`.monaco-table .${this._idPrefix} .slick-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`);
|
||||
// content.push(`.monaco-table .${this._idPrefix} .slick-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case!
|
||||
// }
|
||||
if (styles.listInactiveFocusBackground) {
|
||||
content.push(`.monaco-table.${this.idPrefix} .slick-row .selected.active { background-color: ${styles.listInactiveFocusBackground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix} .slick-row .selected.active:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case!
|
||||
}
|
||||
|
||||
// if (styles.listInactiveSelectionBackground) {
|
||||
// content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { background-color: ${styles.listInactiveSelectionBackground}; }`);
|
||||
// content.push(`.monaco-table .${this._idPrefix} .slick-row .selected:hover { background-color: ${styles.listInactiveSelectionBackground}; }`); // overwrite :hover style in this case!
|
||||
// }
|
||||
if (styles.listInactiveSelectionBackground) {
|
||||
content.push(`.monaco-table.${this.idPrefix} .slick-row .selected { background-color: ${styles.listInactiveSelectionBackground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix} .slick-row .selected:hover { background-color: ${styles.listInactiveSelectionBackground}; }`); // overwrite :hover style in this case!
|
||||
}
|
||||
|
||||
// if (styles.listInactiveSelectionForeground) {
|
||||
// content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { color: ${styles.listInactiveSelectionForeground}; }`);
|
||||
// }
|
||||
if (styles.listInactiveSelectionForeground) {
|
||||
content.push(`.monaco-table.${this.idPrefix} .slick-row .selected { color: ${styles.listInactiveSelectionForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listHoverBackground) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row:hover { background-color: ${styles.listHoverBackground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix} .slick-row:hover { background-color: ${styles.listHoverBackground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listHoverForeground) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row:hover { color: ${styles.listHoverForeground}; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix} .slick-row:hover { color: ${styles.listHoverForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.listSelectionOutline) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected.active { outline: 1px dotted ${styles.listSelectionOutline}; outline-offset: -1px; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix} .slick-row .selected.active { outline: 1px dotted ${styles.listSelectionOutline}; outline-offset: -1px; }`);
|
||||
}
|
||||
|
||||
/* Commented out andresse 8/17/2017; keeping for reference as we iterate on the table styling */
|
||||
// if (styles.listFocusOutline) {
|
||||
// content.push(`.monaco-table .${this._idPrefix}:focus .slick-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`);
|
||||
// }
|
||||
if (styles.listFocusOutline) {
|
||||
content.push(`.monaco-table.${this.idPrefix}.focused .slick-row .selected { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`);
|
||||
}
|
||||
|
||||
// if (styles.listInactiveFocusOutline) {
|
||||
// content.push(`.monaco-table .${this._idPrefix} .slick-row.focused { outline: 1px dotted ${styles.listInactiveFocusOutline}; outline-offset: -1px; }`);
|
||||
// }
|
||||
if (styles.listInactiveFocusOutline) {
|
||||
content.push(`.monaco-table.${this.idPrefix} .slick-row .selected .active { outline: 1px dotted ${styles.listInactiveFocusOutline}; outline-offset: -1px; }`);
|
||||
}
|
||||
|
||||
if (styles.listHoverOutline) {
|
||||
content.push(`.monaco-table .${this._idPrefix} .slick-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`);
|
||||
content.push(`.monaco-table.${this.idPrefix} .slick-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`);
|
||||
}
|
||||
|
||||
this._styleElement.innerHTML = content.join('\n');
|
||||
this.styleElement.innerHTML = content.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,12 +153,18 @@ export class ActionBar extends ActionRunner implements IActionRunner {
|
||||
}
|
||||
|
||||
private updateFocusedItem(): void {
|
||||
let actionIndex = 0;
|
||||
for (let i = 0; i < this._actionsList.children.length; i++) {
|
||||
let elem = this._actionsList.children[i];
|
||||
|
||||
if (DOM.isAncestor(document.activeElement, elem)) {
|
||||
this._focusedItem = i;
|
||||
this._focusedItem = actionIndex;
|
||||
break;
|
||||
}
|
||||
|
||||
if (elem.classList.contains('action-item')) {
|
||||
actionIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,9 @@ export class AutoOAuthDialog extends Modal {
|
||||
});
|
||||
|
||||
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
||||
inputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService);
|
||||
inputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, {
|
||||
ariaLabel: label
|
||||
});
|
||||
});
|
||||
});
|
||||
return inputBox;
|
||||
|
||||
@@ -34,6 +34,11 @@ import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
// in case that other non-Azure sign in is to be used
|
||||
const firewallHelpUri = 'https://aka.ms/sqlopsfirewallhelp';
|
||||
|
||||
const LocalizedStrings = {
|
||||
FROM: localize('from', 'From'),
|
||||
TO: localize('to', 'To')
|
||||
};
|
||||
|
||||
export class FirewallRuleDialog extends Modal {
|
||||
public viewModel: FirewallRuleViewModel;
|
||||
private _createButton: Button;
|
||||
@@ -140,19 +145,23 @@ export class FirewallRuleDialog extends Modal {
|
||||
subnetIPRangeSection = subnetIPRangeContainer.getHTMLElement();
|
||||
subnetIPRangeContainer.div({ 'class': 'dialog-input-section' }, (inputContainer) => {
|
||||
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
|
||||
labelContainer.innerHtml(localize('from', 'From'));
|
||||
labelContainer.innerHtml(LocalizedStrings.FROM);
|
||||
});
|
||||
|
||||
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
|
||||
this._fromRangeinputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService);
|
||||
this._fromRangeinputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, {
|
||||
ariaLabel: LocalizedStrings.FROM
|
||||
});
|
||||
});
|
||||
|
||||
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
|
||||
labelContainer.innerHtml(localize('to', 'To'));
|
||||
labelContainer.innerHtml(LocalizedStrings.TO);
|
||||
});
|
||||
|
||||
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
|
||||
this._toRangeinputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService);
|
||||
this._toRangeinputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, {
|
||||
ariaLabel: LocalizedStrings.TO
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -128,6 +128,7 @@ export class ConnectionWidget {
|
||||
validationOptions: {
|
||||
validation: (value: string) => !value ? ({ type: MessageType.ERROR, content: serverNameOption.displayName + errorMessage }) : null
|
||||
},
|
||||
ariaLabel: serverNameOption.displayName
|
||||
});
|
||||
|
||||
if (this._optionsMaps[ConnectionOptionSpecialType.authType]) {
|
||||
@@ -141,12 +142,13 @@ export class ConnectionWidget {
|
||||
this._userNameInputBox = new InputBox(userNameBuilder.getHTMLElement(), this._contextViewService, {
|
||||
validationOptions: {
|
||||
validation: (value: string) => self.validateUsername(value, userNameOption.isRequired) ? ({ type: MessageType.ERROR, content: userNameOption.displayName + errorMessage }) : null
|
||||
}
|
||||
},
|
||||
ariaLabel: userNameOption.displayName
|
||||
});
|
||||
|
||||
let passwordOption = this._optionsMaps[ConnectionOptionSpecialType.password];
|
||||
let passwordBuilder = DialogHelper.appendRow(this._tableContainer, passwordOption.displayName, 'connection-label', 'connection-input');
|
||||
this._passwordInputBox = new InputBox(passwordBuilder.getHTMLElement(), this._contextViewService);
|
||||
this._passwordInputBox = new InputBox(passwordBuilder.getHTMLElement(), this._contextViewService, { ariaLabel: passwordOption.displayName });
|
||||
this._passwordInputBox.inputElement.type = 'password';
|
||||
this._password = '';
|
||||
|
||||
@@ -158,9 +160,10 @@ export class ConnectionWidget {
|
||||
|
||||
this._databaseNameInputBox = new Dropdown(databaseNameBuilder.getHTMLElement(), this._contextViewService, this._themeService, {
|
||||
values: [this._defaultDatabaseName, this._loadingDatabaseName],
|
||||
strictSelection : false,
|
||||
strictSelection: false,
|
||||
placeholder: this._defaultDatabaseName,
|
||||
maxHeight: 125
|
||||
maxHeight: 125,
|
||||
ariaLabel: databaseOption.displayName
|
||||
});
|
||||
|
||||
let serverGroupLabel = localize('serverGroup', 'Server group');
|
||||
@@ -204,7 +207,7 @@ export class ConnectionWidget {
|
||||
container.element('tr', {}, (rowContainer) => {
|
||||
rowContainer.element('td');
|
||||
rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => {
|
||||
checkbox = new Checkbox(inputCellContainer.getHTMLElement(), { label, checked: isChecked });
|
||||
checkbox = new Checkbox(inputCellContainer.getHTMLElement(), { label, checked: isChecked, ariaLabel: label });
|
||||
});
|
||||
});
|
||||
return checkbox;
|
||||
|
||||
@@ -210,7 +210,6 @@ export class CollapseWidgetAction extends Action {
|
||||
private _uri: string,
|
||||
private _widgetUuid: string,
|
||||
private collpasedState: boolean,
|
||||
private collapsedStateChangedEvent: Event<boolean>,
|
||||
@IAngularEventingService private _angularEventService: IAngularEventingService
|
||||
) {
|
||||
super(
|
||||
@@ -218,7 +217,6 @@ export class CollapseWidgetAction extends Action {
|
||||
collpasedState ? CollapseWidgetAction.EXPAND_LABEL : CollapseWidgetAction.COLLPASE_LABEL,
|
||||
collpasedState ? CollapseWidgetAction.EXPAND_ICON : CollapseWidgetAction.COLLAPSE_ICON
|
||||
);
|
||||
this.collapsedStateChangedEvent(collapsed => this._updateState(collapsed));
|
||||
}
|
||||
|
||||
run(): TPromise<boolean> {
|
||||
@@ -232,8 +230,15 @@ export class CollapseWidgetAction extends Action {
|
||||
}
|
||||
|
||||
private _updateState(collapsed: boolean): void {
|
||||
if (collapsed === this.collpasedState) {
|
||||
return;
|
||||
}
|
||||
this.collpasedState = collapsed;
|
||||
this._setClass(this.collpasedState ? CollapseWidgetAction.EXPAND_ICON : CollapseWidgetAction.COLLAPSE_ICON);
|
||||
this._setLabel(this.collpasedState ? CollapseWidgetAction.EXPAND_LABEL : CollapseWidgetAction.COLLPASE_LABEL);
|
||||
}
|
||||
|
||||
public set state(collapsed: boolean) {
|
||||
this._updateState(collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
|
||||
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
|
||||
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
|
||||
import { WEBVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWebviewContainer.contribution';
|
||||
import { MODELVIEW_CONTAINER } from 'sql/parts/dashboard/containers/dashboardModelViewContainer.contribution';
|
||||
import { CONTROLHOST_CONTAINER } from 'sql/parts/dashboard/containers/dashboardControlHostContainer.contribution';
|
||||
import { NAV_SECTION } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
|
||||
import { IDashboardContainerRegistry, Extensions as DashboardContainerExtensions, IDashboardContainer, registerContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
|
||||
@@ -25,6 +26,7 @@ const containerTypes = [
|
||||
WIDGETS_CONTAINER,
|
||||
GRID_CONTAINER,
|
||||
WEBVIEW_CONTAINER,
|
||||
MODELVIEW_CONTAINER,
|
||||
CONTROLHOST_CONTAINER,
|
||||
NAV_SECTION
|
||||
];
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
</dashboard-webview-container>
|
||||
<dashboard-widget-container *ngIf="getContentType(tab) === 'widgets-container'" [tab]="tab">
|
||||
</dashboard-widget-container>
|
||||
<dashboard-modelview-container *ngIf="getContentType(tab) === 'modelview-container'" [tab]="tab">
|
||||
</dashboard-modelview-container>
|
||||
<dashboard-controlhost-container *ngIf="getContentType(tab) === 'controlhost-container'" [tab]="tab">
|
||||
</dashboard-controlhost-container>
|
||||
<dashboard-nav-section *ngIf="getContentType(tab) === 'nav-section'" [tab]="tab">
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'sql/parts/dashboard/common/dashboardPanelStyles';
|
||||
import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { WidgetConfig, TabConfig, TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
|
||||
@@ -85,13 +86,15 @@ export abstract class DashboardPage extends AngularDisposable {
|
||||
|
||||
protected abstract propertiesWidget: WidgetConfig;
|
||||
protected abstract get context(): string;
|
||||
protected dashboardService: DashboardServiceInterface;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) protected commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ElementRef)) protected _el: ElementRef,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
this.dashboardService = commonService as DashboardServiceInterface;
|
||||
}
|
||||
|
||||
protected init() {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./dashboardControlHostContainer';
|
||||
|
||||
import { Component, forwardRef, Input, AfterContentInit, ViewChild } from '@angular/core';
|
||||
import { Component, forwardRef, Input, AfterContentInit, ViewChild, OnChanges } from '@angular/core';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
@@ -18,6 +18,7 @@ import { ControlHostContent } from 'sql/parts/dashboard/contents/controlHostCont
|
||||
</controlhost-content>
|
||||
`
|
||||
})
|
||||
|
||||
export class DashboardControlHostContainer extends DashboardTab implements AfterContentInit {
|
||||
@Input() private tab: TabConfig;
|
||||
|
||||
@@ -53,6 +54,6 @@ export class DashboardControlHostContainer extends DashboardTab implements After
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
// no op
|
||||
this._hostContent.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export class DashboardErrorContainer extends DashboardTab implements AfterViewIn
|
||||
|
||||
ngAfterViewInit() {
|
||||
let errorMessage = this._errorMessageContainer.nativeElement as HTMLElement;
|
||||
errorMessage.innerHTML = nls.localize('dashboardNavSection_loadTabError', 'The {0} has an invalid content. Please contact extension owner.', this.tab.title);
|
||||
errorMessage.innerHTML = nls.localize('dashboardNavSection_loadTabError', 'The "{0}" section has invalid content. Please contact extension owner.', this.tab.title);
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildr
|
||||
import { NgGridConfig, NgGrid, NgGridItem } from 'angular2-grid';
|
||||
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { TabConfig, WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
|
||||
import { subscriptionToDisposable } from 'sql/base/common/lifecycle';
|
||||
@@ -153,7 +154,7 @@ export class DashboardGridContainer extends DashboardTab implements OnDestroy {
|
||||
@ViewChildren(DashboardWidgetWrapper) private _widgets: QueryList<DashboardWidgetWrapper>;
|
||||
@ViewChildren(WebviewContent) private _webViews: QueryList<WebviewContent>;
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) protected dashboardService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ElementRef)) protected _el: ElementRef,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef
|
||||
) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboa
|
||||
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { AngularEventType } from '../../../services/angularEventing/angularEventingService';
|
||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -30,12 +31,15 @@ export class DashboardHomeContainer extends DashboardWidgetContainer {
|
||||
@Input() private properties: WidgetConfig;
|
||||
@ViewChild('propertiesClass') private _propertiesClass: DashboardWidgetWrapper;
|
||||
|
||||
private dashboardService: DashboardServiceInterface;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) protected commonService: CommonServiceInterface,
|
||||
|
||||
) {
|
||||
super(_cd);
|
||||
this.dashboardService = commonService as DashboardServiceInterface;
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./dashboardWebviewContainer';
|
||||
|
||||
import { Component, forwardRef, Input, AfterContentInit, ViewChild } from '@angular/core';
|
||||
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
|
||||
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
|
||||
|
||||
@Component({
|
||||
selector: 'dashboard-modelview-container',
|
||||
providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardModelViewContainer) }],
|
||||
template: `
|
||||
<modelview-content [modelViewId]="tab.id">
|
||||
</modelview-content>
|
||||
`
|
||||
})
|
||||
export class DashboardModelViewContainer extends DashboardTab implements AfterContentInit {
|
||||
@Input() private tab: TabConfig;
|
||||
|
||||
private _onResize = new Emitter<void>();
|
||||
public readonly onResize: Event<void> = this._onResize.event;
|
||||
|
||||
@ViewChild(ModelViewContent) private _modelViewContent: ModelViewContent;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
this._register(this._modelViewContent.onResize(() => {
|
||||
this._onResize.fire();
|
||||
}));
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
this._modelViewContent.layout();
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this.tab.id;
|
||||
}
|
||||
|
||||
public get editable(): boolean {
|
||||
return this.tab.editable;
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
// no op
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
import { registerContainerType, registerNavSectionContainerType } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
|
||||
|
||||
export const MODELVIEW_CONTAINER = 'modelview-container';
|
||||
|
||||
let modelviewSchema: IJSONSchema = {
|
||||
type: 'null',
|
||||
description: nls.localize('dashboard.container.modelview', "The model-backed view that will be displayed in this tab."),
|
||||
default: null
|
||||
};
|
||||
|
||||
registerContainerType(MODELVIEW_CONTAINER, modelviewSchema);
|
||||
registerNavSectionContainerType(MODELVIEW_CONTAINER, modelviewSchema);
|
||||
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
dashboard-modelview-container {
|
||||
height: 100%;
|
||||
width : 100%;
|
||||
display: block;
|
||||
}
|
||||
@@ -11,6 +11,8 @@
|
||||
</dashboard-webview-container>
|
||||
<dashboard-widget-container *ngIf="getContentType(tab) === 'widgets-container'" [tab]="tab">
|
||||
</dashboard-widget-container>
|
||||
<dashboard-modelview-container *ngIf="getContentType(tab) === 'modelview-container'" [tab]="tab">
|
||||
</dashboard-modelview-container>
|
||||
<dashboard-grid-container *ngIf="getContentType(tab) === 'grid-container'" [tab]="tab">
|
||||
</dashboard-grid-container>
|
||||
<dashboard-error-container *ngIf="getContentType(tab) === 'error-container'" [tab]="tab">
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'vs/css!./dashboardNavSection';
|
||||
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, EventEmitter, OnChanges, AfterContentInit } from '@angular/core';
|
||||
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { WidgetConfig, TabConfig, NavSectionConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
|
||||
import { TabComponent } from 'sql/base/browser/ui/panel/tab.component';
|
||||
@@ -53,7 +54,7 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh
|
||||
@ViewChildren(DashboardTab) private _tabs: QueryList<DashboardTab>;
|
||||
@ViewChild(PanelComponent) private _panel: PanelComponent;
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) protected dashboardService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -5,5 +5,4 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
|
||||
<agentview-component *ngIf="(controlType) === 'agent'">
|
||||
</agentview-component>
|
||||
<agentview-component #agent *ngIf="(controlType) === 'agent'"></agentview-component>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./controlHostContent';
|
||||
|
||||
import { Component, forwardRef, Input, OnInit, Inject, ChangeDetectorRef, ElementRef } from '@angular/core';
|
||||
import { Component, forwardRef, Input, OnInit, Inject, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
|
||||
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Parts } from 'vs/workbench/services/part/common/partService';
|
||||
@@ -13,15 +13,17 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { AgentViewComponent } from '../../jobManagement/agent/agentView.component';
|
||||
|
||||
@Component({
|
||||
templateUrl: decodeURI(require.toUrl('sql/parts/dashboard/contents/controlHostContent.component.html')),
|
||||
selector: 'controlhost-content'
|
||||
})
|
||||
export class ControlHostContent implements OnInit {
|
||||
export class ControlHostContent {
|
||||
@Input() private webviewId: string;
|
||||
|
||||
private _onResize = new Emitter<void>();
|
||||
@@ -32,16 +34,16 @@ export class ControlHostContent implements OnInit {
|
||||
private _onMessageDisposable: IDisposable;
|
||||
private _type: string;
|
||||
|
||||
/* Children components */
|
||||
@ViewChild('agent') private _agentViewComponent: AgentViewComponent;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) private _dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
}
|
||||
|
||||
@@ -73,4 +75,8 @@ export class ControlHostContent implements OnInit {
|
||||
public get controlType(): string {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
public refresh() {
|
||||
this._agentViewComponent.refresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWid
|
||||
import { WebviewWidget } from 'sql/parts/dashboard/widgets/webview/webviewWidget.component';
|
||||
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
@@ -53,6 +54,7 @@ export class DashboardWidgetWrapper extends AngularDisposable implements OnInit
|
||||
@Input() private _config: WidgetConfig;
|
||||
@Input() private collapsable = false;
|
||||
|
||||
private _collapseAction: CollapseWidgetAction;
|
||||
private _collapsed = false;
|
||||
|
||||
public get collapsed(): boolean {
|
||||
@@ -64,17 +66,13 @@ export class DashboardWidgetWrapper extends AngularDisposable implements OnInit
|
||||
return;
|
||||
}
|
||||
this._collapsed = val;
|
||||
if (this.collapsedStateChangedEmitter) {
|
||||
this.collapsedStateChangedEmitter.fire(this._collapsed);
|
||||
}
|
||||
this._collapseAction.state = val;
|
||||
this._changeref.detectChanges();
|
||||
if (!val) {
|
||||
this.loadWidget();
|
||||
}
|
||||
}
|
||||
|
||||
private collapsedStateChangedEmitter: Emitter<boolean>;
|
||||
|
||||
@memoize
|
||||
public get guid(): string {
|
||||
return generateUuid();
|
||||
@@ -91,7 +89,7 @@ export class DashboardWidgetWrapper extends AngularDisposable implements OnInit
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver,
|
||||
@Inject(forwardRef(() => ElementRef)) private _ref: ElementRef,
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrap: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeref: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => Injector)) private _injector: Injector
|
||||
) {
|
||||
@@ -114,8 +112,8 @@ export class DashboardWidgetWrapper extends AngularDisposable implements OnInit
|
||||
this._actionbar = new ActionBar(this._actionbarRef.nativeElement);
|
||||
if (this._actions) {
|
||||
if (this.collapsable) {
|
||||
this.collapsedStateChangedEmitter = new Emitter<boolean>();
|
||||
this._actionbar.push(this._bootstrap.instantiationService.createInstance(CollapseWidgetAction, this._bootstrap.getUnderlyingUri(), this.guid, this.collapsed, this.collapsedStateChangedEmitter.event), { icon: true, label: false });
|
||||
this._collapseAction = this._bootstrap.instantiationService.createInstance(CollapseWidgetAction, this._bootstrap.getUnderlyingUri(), this.guid, this.collapsed);
|
||||
this._actionbar.push(this._collapseAction, { icon: true, label: false });
|
||||
}
|
||||
this._actionbar.push(this._bootstrap.instantiationService.createInstance(ToggleMoreWidgetAction, this._actions, this._component.actionsContext), { icon: true, label: false });
|
||||
}
|
||||
@@ -123,13 +121,13 @@ export class DashboardWidgetWrapper extends AngularDisposable implements OnInit
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
if (this._component && this._component.refresh) {
|
||||
if (!this.collapsed && this._component && this._component.refresh) {
|
||||
this._component.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public layout(): void {
|
||||
if (this._component && this._component.layout) {
|
||||
if (!this.collapsed && this._component && this._component.layout) {
|
||||
this._component.layout();
|
||||
}
|
||||
}
|
||||
@@ -170,7 +168,7 @@ export class DashboardWidgetWrapper extends AngularDisposable implements OnInit
|
||||
this._component = componentRef.instance;
|
||||
let actions = componentRef.instance.actions;
|
||||
if (componentRef.instance.refresh) {
|
||||
actions.push(new RefreshWidgetAction(componentRef.instance.refresh, componentRef.instance));
|
||||
actions.push(new RefreshWidgetAction(this.refresh, this));
|
||||
}
|
||||
if (actions !== undefined && actions.length > 0) {
|
||||
this._actions = actions;
|
||||
|
||||
@@ -16,7 +16,8 @@ import { memoize } from 'vs/base/common/decorators';
|
||||
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
|
||||
import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { IDashboardWebview } from 'sql/services/dashboard/common/dashboardViewService';
|
||||
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
@@ -36,17 +37,19 @@ export class WebviewContent extends AngularDisposable implements OnInit, IDashbo
|
||||
private _onMessageDisposable: IDisposable;
|
||||
private _webview: Webview;
|
||||
private _html: string;
|
||||
private _dashboardService: DashboardServiceInterface;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) private _dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef
|
||||
) {
|
||||
super();
|
||||
this._dashboardService = commonService as DashboardServiceInterface;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._dashboardService.dashboardWebviewService.registerWebview(this);
|
||||
this._dashboardService.dashboardViewService.registerWebview(this);
|
||||
this._createWebview();
|
||||
this._register(addDisposableListener(window, EventType.RESIZE, e => {
|
||||
this.layout();
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Component, Inject, Input, forwardRef, ViewChild, ViewChildren, QueryLis
|
||||
import { NgGridConfig, NgGrid, NgGridItem } from 'angular2-grid';
|
||||
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
|
||||
import { subscriptionToDisposable, AngularDisposable } from 'sql/base/common/lifecycle';
|
||||
@@ -111,11 +112,14 @@ export class WidgetContent extends AngularDisposable implements AfterViewInit {
|
||||
@ViewChildren(NgGridItem) private _items: QueryList<NgGridItem>;
|
||||
@ViewChild('scrollable', { read: ElementRef }) private _scrollable: ElementRef;
|
||||
@ViewChild('scrollContainer', { read: ElementRef }) private _scrollContainer: ElementRef;
|
||||
|
||||
protected dashboardService: DashboardServiceInterface;
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) protected dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) protected commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
this.dashboardService = commonService as DashboardServiceInterface;
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, O
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { DashboardServiceInterface } from './services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import * as Utils from 'sql/parts/connection/common/utils';
|
||||
import { RefreshWidgetAction, EditDashboardAction } from 'sql/parts/dashboard/common/actions';
|
||||
@@ -36,7 +37,7 @@ export class DashboardComponent extends AngularDisposable implements OnInit {
|
||||
private editDisposable: IDisposable;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrapService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrapService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => Router)) private _router: Router,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef
|
||||
) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ChartsModule } from 'ng2-charts/ng2-charts';
|
||||
import CustomUrlSerializer from 'sql/common/urlSerializer';
|
||||
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { Extensions as ComponentExtensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
@@ -25,6 +26,7 @@ import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||
/* Services */
|
||||
import { BreadcrumbService } from 'sql/parts/dashboard/services/breadcrumb.service';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
|
||||
/* Directives */
|
||||
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
||||
@@ -35,9 +37,12 @@ import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWi
|
||||
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
|
||||
import { DashboardGridContainer } from 'sql/parts/dashboard/containers/dashboardGridContainer.component';
|
||||
import { DashboardWebviewContainer } from 'sql/parts/dashboard/containers/dashboardWebviewContainer.component';
|
||||
import { DashboardModelViewContainer } from 'sql/parts/dashboard/containers/dashboardModelViewContainer.component';
|
||||
import { DashboardErrorContainer } from 'sql/parts/dashboard/containers/dashboardErrorContainer.component';
|
||||
import { DashboardNavSection } from 'sql/parts/dashboard/containers/dashboardNavSection.component';
|
||||
import { WidgetContent } from 'sql/parts/dashboard/contents/widgetContent.component';
|
||||
import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
|
||||
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
||||
import { WebviewContent } from 'sql/parts/dashboard/contents/webviewContent.component';
|
||||
import { BreadcrumbComponent } from 'sql/base/browser/ui/breadcrumb/breadcrumb.component';
|
||||
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
|
||||
@@ -49,9 +54,9 @@ import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.comp
|
||||
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
||||
|
||||
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
||||
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, WebviewContent, WidgetContent,
|
||||
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
|
||||
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent];
|
||||
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
|
||||
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
|
||||
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper];
|
||||
|
||||
/* Panel */
|
||||
import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';
|
||||
@@ -81,6 +86,9 @@ let widgetComponents = [
|
||||
/* Insights */
|
||||
let insightComponents = Registry.as<IInsightRegistry>(Extensions.InsightContribution).getAllCtors();
|
||||
|
||||
/* Model-backed components */
|
||||
let extensionComponents = Registry.as<IComponentRegistry>(ComponentExtensions.ComponentContribution).getAllCtors();
|
||||
|
||||
// Setup routes for various child components
|
||||
const appRoutes: Routes = [
|
||||
{ path: 'database-dashboard', component: DatabaseDashboardPage },
|
||||
@@ -99,13 +107,15 @@ const appRoutes: Routes = [
|
||||
...baseComponents,
|
||||
...pageComponents,
|
||||
...widgetComponents,
|
||||
...insightComponents
|
||||
...insightComponents,
|
||||
...extensionComponents
|
||||
],
|
||||
// also for widgets
|
||||
entryComponents: [
|
||||
DashboardComponent,
|
||||
...widgetComponents,
|
||||
...insightComponents
|
||||
...insightComponents,
|
||||
...extensionComponents
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@@ -119,18 +129,19 @@ const appRoutes: Routes = [
|
||||
providers: [
|
||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||
{ provide: IBreadcrumbService, useClass: BreadcrumbService },
|
||||
DashboardServiceInterface,
|
||||
{ provide: CommonServiceInterface, useClass: DashboardServiceInterface },
|
||||
{ provide: UrlSerializer, useClass: CustomUrlSerializer }
|
||||
]
|
||||
})
|
||||
export class DashboardModule {
|
||||
|
||||
private _bootstrap: DashboardServiceInterface;
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver,
|
||||
@Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService,
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) bootstrap: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => Router)) private _router: Router
|
||||
) {
|
||||
this._bootstrap = bootstrap as DashboardServiceInterface;
|
||||
}
|
||||
|
||||
ngDoBootstrap(appRef: ApplicationRef) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/
|
||||
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import * as nls from 'vs/nls';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
|
||||
export class DatabaseDashboardPage extends DashboardPage implements OnInit {
|
||||
protected propertiesWidget: WidgetConfig = {
|
||||
@@ -35,7 +36,7 @@ export class DatabaseDashboardPage extends DashboardPage implements OnInit {
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => IBreadcrumbService)) private _breadcrumbService: IBreadcrumbService,
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef
|
||||
) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { BreadcrumbClass } from 'sql/parts/dashboard/services/breadcrumb.service
|
||||
import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
|
||||
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService';
|
||||
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
@@ -36,7 +37,7 @@ export class ServerDashboardPage extends DashboardPage implements OnInit {
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => IBreadcrumbService)) private breadcrumbService: IBreadcrumbService,
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) dashboardService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef
|
||||
) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Injectable, forwardRef, Inject, OnDestroy } from '@angular/core';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
|
||||
import { DashboardServiceInterface } from './dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { MenuItem, IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
|
||||
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
|
||||
|
||||
@@ -23,9 +24,11 @@ export class BreadcrumbService implements IBreadcrumbService {
|
||||
public breadcrumbItem: Subject<MenuItem[]>;
|
||||
private itemBreadcrums: MenuItem[];
|
||||
private _currentPage: BreadcrumbClass;
|
||||
private _bootstrap: DashboardServiceInterface;
|
||||
|
||||
constructor( @Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface) {
|
||||
_bootstrap.onUpdatePage(() => {
|
||||
constructor( @Inject(forwardRef(() => CommonServiceInterface)) private commonService: CommonServiceInterface) {
|
||||
this._bootstrap = commonService as DashboardServiceInterface;
|
||||
this._bootstrap.onUpdatePage(() => {
|
||||
this.setBreadcrumbs(this._currentPage);
|
||||
});
|
||||
this.breadcrumbItem = new Subject<MenuItem[]>();
|
||||
|
||||
@@ -23,9 +23,11 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
|
||||
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
|
||||
import { TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { IDashboardWebviewService } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||
import { IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
|
||||
import { AngularDisposable } from 'sql/base/common/lifecycle';
|
||||
import { ConnectionContextkey } from 'sql/parts/connection/common/connectionContextKey';
|
||||
import { SingleConnectionMetadataService, SingleConnectionManagementService, SingleAdminService, SingleQueryManagementService, CommonServiceInterface }
|
||||
from 'sql/services/common/commonServiceInterface.service';
|
||||
|
||||
import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'sqlops';
|
||||
|
||||
@@ -50,68 +52,6 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
|
||||
const DASHBOARD_SETTINGS = 'dashboard';
|
||||
|
||||
/* Wrapper for a metadata service that contains the uri string to use on each request */
|
||||
export class SingleConnectionMetadataService {
|
||||
|
||||
constructor(
|
||||
private _metadataService: IMetadataService,
|
||||
private _uri: string
|
||||
) { }
|
||||
|
||||
get metadata(): Observable<ProviderMetadata> {
|
||||
return Observable.fromPromise(this._metadataService.getMetadata(this._uri));
|
||||
}
|
||||
|
||||
get databaseNames(): Observable<string[]> {
|
||||
return Observable.fromPromise(this._metadataService.getDatabaseNames(this._uri));
|
||||
}
|
||||
}
|
||||
|
||||
/* Wrapper for a connection service that contains the uri string to use on each request */
|
||||
export class SingleConnectionManagementService {
|
||||
|
||||
constructor(
|
||||
private _connectionService: IConnectionManagementService,
|
||||
private _uri: string,
|
||||
private _contextKey: ConnectionContextkey
|
||||
) { }
|
||||
|
||||
public changeDatabase(name: string): Thenable<boolean> {
|
||||
return this._connectionService.changeDatabase(this._uri, name).then(e => {
|
||||
// we need to update our context
|
||||
this._contextKey.set(this.connectionInfo.connectionProfile);
|
||||
return e;
|
||||
});
|
||||
}
|
||||
|
||||
public get connectionInfo(): ConnectionManagementInfo {
|
||||
return this._connectionService.getConnectionInfo(this._uri);
|
||||
}
|
||||
}
|
||||
|
||||
export class SingleAdminService {
|
||||
|
||||
constructor(
|
||||
private _adminService: IAdminService,
|
||||
private _uri: string
|
||||
) { }
|
||||
|
||||
public get databaseInfo(): Observable<DatabaseInfo> {
|
||||
return Observable.fromPromise(this._adminService.getDatabaseInfo(this._uri));
|
||||
}
|
||||
}
|
||||
|
||||
export class SingleQueryManagementService {
|
||||
constructor(
|
||||
private _queryManagementService: IQueryManagementService,
|
||||
private _uri: string
|
||||
) { }
|
||||
|
||||
public runQueryAndReturn(queryString: string): Thenable<SimpleExecuteResult> {
|
||||
return this._queryManagementService.runQueryAndReturn(this._uri, queryString);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Providers a interface between a dashboard interface and the rest of carbon.
|
||||
Stores the uri and unique selector of a dashboard instance and uses that
|
||||
@@ -120,35 +60,12 @@ export class SingleQueryManagementService {
|
||||
usage of a widget.
|
||||
*/
|
||||
@Injectable()
|
||||
export class DashboardServiceInterface extends AngularDisposable {
|
||||
private _uniqueSelector: string;
|
||||
private _uri: string;
|
||||
private _bootstrapParams: DashboardComponentParams;
|
||||
export class DashboardServiceInterface extends CommonServiceInterface {
|
||||
|
||||
/* Static Services */
|
||||
private _themeService = this._bootstrapService.themeService;
|
||||
private _contextMenuService = this._bootstrapService.contextMenuService;
|
||||
private _instantiationService = this._bootstrapService.instantiationService;
|
||||
private _configService = this._bootstrapService.configurationService;
|
||||
private _insightsDialogService = this._bootstrapService.insightsDialogService;
|
||||
private _contextViewService = this._bootstrapService.contextViewService;
|
||||
private _notificationService = this._bootstrapService.notificationService;
|
||||
private _workspaceContextService = this._bootstrapService.workspaceContextService;
|
||||
private _storageService = this._bootstrapService.storageService;
|
||||
private _capabilitiesService = this._bootstrapService.capabilitiesService;
|
||||
private _configurationEditingService = this._bootstrapService.configurationEditorService;
|
||||
private _commandService = this._bootstrapService.commandService;
|
||||
private _dashboardWebviewService = this._bootstrapService.dashboardWebviewService;
|
||||
private _partService = this._bootstrapService.partService;
|
||||
private _angularEventingService = this._bootstrapService.angularEventingService;
|
||||
private _environmentService = this._bootstrapService.environmentService;
|
||||
|
||||
/* Special Services */
|
||||
private _metadataService: SingleConnectionMetadataService;
|
||||
private _connectionManagementService: SingleConnectionManagementService;
|
||||
private _adminService: SingleAdminService;
|
||||
private _queryManagementService: SingleQueryManagementService;
|
||||
private _contextKeyService: IContextKeyService;
|
||||
private _dashboardViewService = this._bootstrapService.dashboardViewService;
|
||||
|
||||
|
||||
private _updatePage = new Emitter<void>();
|
||||
public readonly onUpdatePage: Event<void> = this._updatePage.event;
|
||||
@@ -168,91 +85,17 @@ export class DashboardServiceInterface extends AngularDisposable {
|
||||
private _dashboardContextKey = new RawContextKey<string>('dashboardContext', undefined);
|
||||
public dashboardContextKey: IContextKey<string>;
|
||||
|
||||
private _connectionContextKey: ConnectionContextkey;
|
||||
|
||||
private _numberOfPageNavigations = 0;
|
||||
|
||||
constructor(
|
||||
@Inject(BOOTSTRAP_SERVICE_ID) private _bootstrapService: IBootstrapService,
|
||||
@Inject(BOOTSTRAP_SERVICE_ID) bootstrapService: IBootstrapService,
|
||||
@Inject(forwardRef(() => Router)) private _router: Router,
|
||||
) {
|
||||
super();
|
||||
super(bootstrapService);
|
||||
}
|
||||
|
||||
public get notificationService(): INotificationService {
|
||||
return this._notificationService;
|
||||
}
|
||||
|
||||
public get configurationEditingService(): ConfigurationEditingService {
|
||||
return this._configurationEditingService;
|
||||
}
|
||||
|
||||
public get metadataService(): SingleConnectionMetadataService {
|
||||
return this._metadataService;
|
||||
}
|
||||
|
||||
public get connectionManagementService(): SingleConnectionManagementService {
|
||||
return this._connectionManagementService;
|
||||
}
|
||||
|
||||
public get commandService(): ICommandService {
|
||||
return this._commandService;
|
||||
}
|
||||
|
||||
public get themeService(): IWorkbenchThemeService {
|
||||
return this._themeService;
|
||||
}
|
||||
|
||||
public get contextMenuService(): IContextMenuService {
|
||||
return this._contextMenuService;
|
||||
}
|
||||
|
||||
public get instantiationService(): IInstantiationService {
|
||||
return this._instantiationService;
|
||||
}
|
||||
|
||||
public get dashboardWebviewService(): IDashboardWebviewService {
|
||||
return this._dashboardWebviewService;
|
||||
}
|
||||
|
||||
public get partService(): IPartService {
|
||||
return this._partService;
|
||||
}
|
||||
|
||||
public get contextKeyService(): IContextKeyService {
|
||||
return this._contextKeyService;
|
||||
}
|
||||
|
||||
public get adminService(): SingleAdminService {
|
||||
return this._adminService;
|
||||
}
|
||||
|
||||
public get queryManagementService(): SingleQueryManagementService {
|
||||
return this._queryManagementService;
|
||||
}
|
||||
|
||||
public get environmentService(): IEnvironmentService {
|
||||
return this._environmentService;
|
||||
}
|
||||
|
||||
public get contextViewService(): IContextViewService {
|
||||
return this._contextViewService;
|
||||
}
|
||||
|
||||
public get workspaceContextService(): IWorkspaceContextService {
|
||||
return this._workspaceContextService;
|
||||
}
|
||||
|
||||
public get storageService(): IStorageService {
|
||||
return this._storageService;
|
||||
}
|
||||
|
||||
public get capabilitiesService(): ICapabilitiesService {
|
||||
return this._capabilitiesService;
|
||||
}
|
||||
|
||||
public get angularEventingService(): IAngularEventingService {
|
||||
return this._angularEventingService;
|
||||
public get dashboardViewService(): IDashboardViewService {
|
||||
return this._dashboardViewService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,7 +106,7 @@ export class DashboardServiceInterface extends AngularDisposable {
|
||||
this._getbootstrapParams();
|
||||
}
|
||||
|
||||
private _getbootstrapParams(): void {
|
||||
protected _getbootstrapParams(): void {
|
||||
this._bootstrapParams = this._bootstrapService.getBootstrapParams<DashboardComponentParams>(this._uniqueSelector);
|
||||
this._contextKeyService = this._bootstrapParams.scopedContextService;
|
||||
this._connectionContextKey = this._bootstrapParams.connectionContextKey;
|
||||
@@ -275,27 +118,11 @@ export class DashboardServiceInterface extends AngularDisposable {
|
||||
* Set the uri for this dashboard instance, should only be set once
|
||||
* Inits all the services that depend on knowing a uri
|
||||
*/
|
||||
private set uri(uri: string) {
|
||||
this._uri = uri;
|
||||
this._metadataService = new SingleConnectionMetadataService(this._bootstrapService.metadataService, this._uri);
|
||||
this._connectionManagementService = new SingleConnectionManagementService(this._bootstrapService.connectionManagementService, this._uri, this._connectionContextKey);
|
||||
this._adminService = new SingleAdminService(this._bootstrapService.adminService, this._uri);
|
||||
this._queryManagementService = new SingleQueryManagementService(this._bootstrapService.queryManagementService, this._uri);
|
||||
protected set uri(uri: string) {
|
||||
super.setUri(uri);
|
||||
this._register(toDisposableSubscription(this._bootstrapService.angularEventingService.onAngularEvent(this._uri, (event) => this.handleDashboardEvent(event))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying Uri for dashboard
|
||||
* In general don't use this, use specific services instances exposed publically
|
||||
*/
|
||||
public getUnderlyingUri(): string {
|
||||
return this._uri;
|
||||
}
|
||||
|
||||
public getOriginalConnectionProfile(): IConnectionProfile {
|
||||
return this._bootstrapParams.connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of page navigation
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Router } from '@angular/router';
|
||||
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { MetadataType } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { SingleConnectionManagementService } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
|
||||
import {
|
||||
NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, ScriptExecuteAction, ScriptAlterAction,
|
||||
BackupAction, ManageActionContext, BaseActionContext, ManageAction, RestoreAction
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Router } from '@angular/router';
|
||||
|
||||
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
|
||||
import { ExplorerFilter, ExplorerRenderer, ExplorerDataSource, ExplorerController, ObjectMetadataWrapper, ExplorerModel } from './explorerTree';
|
||||
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
|
||||
@@ -49,7 +50,7 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget,
|
||||
@ViewChild('table') private _tableContainer: ElementRef;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrap: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => Router)) private _router: Router,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(WIDGET_CONFIG) protected _config: WidgetConfig,
|
||||
@@ -62,8 +63,11 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget,
|
||||
ngOnInit() {
|
||||
this._inited = true;
|
||||
|
||||
let placeholderLabel = this._config.context === 'database' ? nls.localize('seachObjects', 'Search by name of type (a:, t:, v:, f:, or sp:)') : nls.localize('searchDatabases', 'Search databases');
|
||||
|
||||
let inputOptions: IInputOptions = {
|
||||
placeholder: this._config.context === 'database' ? nls.localize('seachObjects', 'Search by name of type (a:, t:, v:, f:, or sp:)') : nls.localize('searchDatabases', 'Search databases')
|
||||
placeholder: placeholderLabel,
|
||||
ariaLabel: placeholderLabel
|
||||
};
|
||||
this._input = new InputBox(this._inputContainer.nativeElement, this._bootstrap.contextViewService, inputOptions);
|
||||
this._register(this._input.onDidChange(e => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { DashboardWidget, IDashboardWidget, WIDGET_CONFIG, WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
||||
import { InsightAction, InsightActionContext } from 'sql/workbench/common/actions';
|
||||
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
|
||||
@@ -26,6 +27,7 @@ import * as pfs from 'vs/base/node/pfs';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IntervalTimer } from 'vs/base/common/async';
|
||||
|
||||
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
|
||||
|
||||
@@ -51,13 +53,14 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
|
||||
|
||||
private _typeKey: string;
|
||||
private _init: boolean = false;
|
||||
private _intervalTimer: IntervalTimer;
|
||||
|
||||
public error: string;
|
||||
public lastUpdated: string;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver,
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) private dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private dashboardService: CommonServiceInterface,
|
||||
@Inject(WIDGET_CONFIG) protected _config: WidgetConfig,
|
||||
@Inject(forwardRef(() => ViewContainerRef)) private viewContainerRef: ViewContainerRef,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef
|
||||
@@ -75,6 +78,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
|
||||
result => {
|
||||
if (this._init) {
|
||||
this._updateChild(result);
|
||||
this.setupInterval();
|
||||
} else {
|
||||
this.queryObv = Observable.fromPromise(Promise.resolve<SimpleExecuteResult>(result));
|
||||
}
|
||||
@@ -99,6 +103,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
|
||||
this._register(toDisposableSubscription(this.queryObv.subscribe(
|
||||
result => {
|
||||
this._updateChild(result);
|
||||
this.setupInterval();
|
||||
},
|
||||
error => {
|
||||
this.showError(error);
|
||||
@@ -107,6 +112,14 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
|
||||
}
|
||||
}
|
||||
|
||||
private setupInterval(): void {
|
||||
if (this.insightConfig.autoRefreshInterval) {
|
||||
this._intervalTimer = new IntervalTimer();
|
||||
this._register(this._intervalTimer);
|
||||
this._intervalTimer.cancelAndSet(() => this.refresh(), this.insightConfig.autoRefreshInterval * 60 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
private showError(error: string): void {
|
||||
this.error = error;
|
||||
this._cd.detectChanges();
|
||||
@@ -151,6 +164,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
|
||||
this.lastUpdated = nls.localize('insights.lastUpdated', "Last Updated: {0} {1}", date.toLocaleTimeString(), date.toLocaleDateString());
|
||||
if (this._init) {
|
||||
this._updateChild(storedResult.results);
|
||||
this.setupInterval();
|
||||
this._cd.detectChanges();
|
||||
} else {
|
||||
this.queryObv = Observable.fromPromise(Promise.resolve<SimpleExecuteResult>(JSON.parse(storage)));
|
||||
@@ -230,6 +244,10 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
|
||||
throw new Error('No query was specified for this insight');
|
||||
}
|
||||
|
||||
if (this.insightConfig.autoRefreshInterval && !types.isNumber(this.insightConfig.autoRefreshInterval)) {
|
||||
throw new Error('Auto Refresh Interval must be a number if specified');
|
||||
}
|
||||
|
||||
if (!types.isStringArray(this.insightConfig.query)
|
||||
&& !types.isString(this.insightConfig.query)
|
||||
&& !types.isString(this.insightConfig.queryFile)) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { join } from 'path';
|
||||
|
||||
import { registerDashboardWidget, registerNonCustomDashboardWidget } from 'sql/platform/dashboard/common/widgetRegistry';
|
||||
import { Extensions as InsightExtensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { IInsightsConfig } from './interfaces';
|
||||
import { IInsightsConfig, IInsightTypeContrib } from './interfaces';
|
||||
import { insightsContribution, insightsSchema } from 'sql/parts/dashboard/widgets/insights/insightsWidgetSchemas';
|
||||
|
||||
import { IExtensionPointUser, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
@@ -14,10 +14,6 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
const insightRegistry = Registry.as<IInsightRegistry>(InsightExtensions.InsightContribution);
|
||||
|
||||
interface IInsightTypeContrib {
|
||||
id: string;
|
||||
contrib: IInsightsConfig;
|
||||
}
|
||||
|
||||
registerDashboardWidget('insights-widget', '', insightsSchema);
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ export const insightsSchema: IJSONSchema = {
|
||||
type: 'string',
|
||||
description: nls.localize('insightQueryFileDescription', '[Optional] path to a file that contains a query. Use if "query" is not set')
|
||||
},
|
||||
autoRefreshInterval: {
|
||||
type: 'number',
|
||||
description: nls.localize('insightAutoRefreshIntervalDescription', '[Optional] Auto refresh interval in minutes, if not set, there will be no auto refresh')
|
||||
},
|
||||
details: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -93,15 +97,15 @@ export const insightsSchema: IJSONSchema = {
|
||||
},
|
||||
database: {
|
||||
type: 'string',
|
||||
description: nls.localize('actionDatabaseDescription', 'Target database for the action; can use the format "${columnName} to use a data driven column name.')
|
||||
description: nls.localize('actionDatabaseDescription', 'Target database for the action; can use the format "${columnName}" to use a data driven column name.')
|
||||
},
|
||||
server: {
|
||||
type: 'string',
|
||||
description: nls.localize('actionServerDescription', 'Target server for the action; can use the format "${columnName} to use a data driven column name.')
|
||||
description: nls.localize('actionServerDescription', 'Target server for the action; can use the format "${columnName}" to use a data driven column name.')
|
||||
},
|
||||
user: {
|
||||
type: 'string',
|
||||
description: nls.localize('actionUserDescription', 'Target user for the action; can use the format "${columnName} to use a data driven column name.')
|
||||
description: nls.localize('actionUserDescription', 'Target user for the action; can use the format "${columnName}" to use a data driven column name.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,4 +56,9 @@ export interface IInsightsConfig {
|
||||
query?: string | Array<string>;
|
||||
queryFile?: string;
|
||||
details?: IInsightsConfigDetails;
|
||||
autoRefreshInterval?: number;
|
||||
}
|
||||
export interface IInsightTypeContrib {
|
||||
id: string;
|
||||
contrib: IInsightsConfig;
|
||||
}
|
||||
@@ -79,6 +79,7 @@ export interface IChartConfig {
|
||||
legendPosition?: LegendPosition;
|
||||
dataDirection?: DataDirection;
|
||||
columnsAsLabels?: boolean;
|
||||
showTopNData?: number;
|
||||
}
|
||||
|
||||
export const defaultChartConfig: IChartConfig = {
|
||||
@@ -179,7 +180,7 @@ export abstract class ChartInsight extends Disposable implements IInsightsView {
|
||||
// unmemoize chart data as the data needs to be recalced
|
||||
unmemoize(this, 'chartData');
|
||||
unmemoize(this, 'labels');
|
||||
this._data = data;
|
||||
this._data = this.filterToTopNData(data);
|
||||
if (isValidData(data)) {
|
||||
this._isDataAvailable = true;
|
||||
}
|
||||
@@ -187,6 +188,34 @@ export abstract class ChartInsight extends Disposable implements IInsightsView {
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
private filterToTopNData(data: IInsightData): IInsightData {
|
||||
if (this._config.dataDirection === 'horizontal') {
|
||||
return {
|
||||
columns: this.getTopNData(data.columns),
|
||||
rows: data.rows.map((row) => {
|
||||
return this.getTopNData(row);
|
||||
})
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
columns: data.columns,
|
||||
rows: data.rows.slice(0, this._config.showTopNData)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private getTopNData(data: any[]): any[] {
|
||||
if (this._config.showTopNData) {
|
||||
if (this._config.dataDirection === 'horizontal' && this._config.labelFirstColumn) {
|
||||
return data.slice(0, this._config.showTopNData + 1);
|
||||
} else {
|
||||
return data.slice(0, this._config.showTopNData);
|
||||
}
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
protected clearMemoize(): void {
|
||||
// unmemoize getters since their result can be changed by a new config
|
||||
unmemoize(this, 'getChartData');
|
||||
@@ -250,7 +279,11 @@ export abstract class ChartInsight extends Disposable implements IInsightsView {
|
||||
@memoize
|
||||
public getLabels(): Array<string> {
|
||||
if (this._config.dataDirection === 'horizontal') {
|
||||
return this._data.columns;
|
||||
if (this._config.labelFirstColumn) {
|
||||
return this._data.columns.slice(1);
|
||||
} else {
|
||||
return this._data.columns;
|
||||
}
|
||||
} else {
|
||||
return this._data.rows.map(row => row[0]);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ export const chartInsightSchema: IJSONSchema = {
|
||||
default: 'vertical',
|
||||
enum: ['vertical', 'horizontal'],
|
||||
enumDescriptions: ['When vertical, the first column is used to define the x-axis labels, with other columns expected to be numerical.', 'When horizontal, the column names are used as the x-axis labels.']
|
||||
},
|
||||
showTopNData: {
|
||||
type: 'number',
|
||||
description: nls.localize('showTopNData', 'If showTopNData is set, showing only top N data in the chart.')
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -12,7 +12,7 @@ import { chartInsightSchema } from 'sql/parts/dashboard/widgets/insights/views/c
|
||||
|
||||
import BarChart from './barChart.component';
|
||||
|
||||
export const properties: IJSONSchema = {
|
||||
const properties: IJSONSchema = {
|
||||
properties: {
|
||||
yAxisMin: {
|
||||
type: 'number',
|
||||
@@ -41,6 +41,6 @@ export const properties: IJSONSchema = {
|
||||
}
|
||||
};
|
||||
|
||||
const barSchema = mixin(clone(chartInsightSchema), properties) as IJSONSchema;
|
||||
export const barChartSchema = mixin(clone(chartInsightSchema), properties) as IJSONSchema;
|
||||
|
||||
registerInsight('bar', '', barSchema, BarChart);
|
||||
registerInsight('bar', '', barChartSchema, BarChart);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { mixin } from 'vs/base/common/objects';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
|
||||
import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { properties as BarChartSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution';
|
||||
import { barChartSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution';
|
||||
|
||||
import HorizontalBarChart from './horizontalBarChart.component';
|
||||
|
||||
@@ -15,6 +15,6 @@ const properties: IJSONSchema = {
|
||||
|
||||
};
|
||||
|
||||
const horizontalBarSchema = mixin(clone(BarChartSchema), properties) as IJSONSchema;
|
||||
const horizontalBarSchema = mixin(clone(barChartSchema), properties) as IJSONSchema;
|
||||
|
||||
registerInsight('horizontalBar', '', horizontalBarSchema, HorizontalBarChart);
|
||||
|
||||
@@ -8,7 +8,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { properties as BarChartSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution';
|
||||
import { barChartSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution';
|
||||
|
||||
import LineChart from './lineChart.component';
|
||||
|
||||
@@ -24,6 +24,6 @@ const properties: IJSONSchema = {
|
||||
}
|
||||
};
|
||||
|
||||
export const lineSchema = mixin(clone(BarChartSchema), properties) as IJSONSchema;
|
||||
export const lineSchema = mixin(clone(barChartSchema), properties) as IJSONSchema;
|
||||
|
||||
registerInsight('line', '', lineSchema, LineChart);
|
||||
|
||||
@@ -7,13 +7,13 @@ import { clone } from 'sql/base/common/objects';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
|
||||
import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { properties as BarChartSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution';
|
||||
import { barChartSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution';
|
||||
|
||||
import ScatterChart from './scatterChart.component';
|
||||
|
||||
const properties: IJSONSchema = {
|
||||
};
|
||||
|
||||
const scatterSchema = mixin(clone(BarChartSchema), properties) as IJSONSchema;
|
||||
const scatterSchema = mixin(clone(barChartSchema), properties) as IJSONSchema;
|
||||
|
||||
registerInsight('scatter', '', scatterSchema, ScatterChart);
|
||||
|
||||
@@ -7,13 +7,13 @@ import { clone } from 'sql/base/common/objects';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
|
||||
import { registerInsight } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { properties as BarChartSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution';
|
||||
import { barChartSchema } from 'sql/parts/dashboard/widgets/insights/views/charts/types/barChart.contribution';
|
||||
|
||||
import TimeSeriesChart from './timeSeriesChart.component';
|
||||
|
||||
const properties: IJSONSchema = {
|
||||
};
|
||||
|
||||
const timeSeriesSchema = mixin(clone(BarChartSchema), properties) as IJSONSchema;
|
||||
const timeSeriesSchema = mixin(clone(barChartSchema), properties) as IJSONSchema;
|
||||
|
||||
registerInsight('timeSeries', '', timeSeriesSchema, TimeSeriesChart);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Component, Inject, forwardRef, ChangeDetectorRef, OnInit, ElementRef, V
|
||||
|
||||
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
||||
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
|
||||
import { error } from 'sql/base/common/log';
|
||||
@@ -68,7 +69,7 @@ export class PropertiesWidgetComponent extends DashboardWidget implements IDashb
|
||||
@ViewChild('parent', { read: ElementRef }) private _parent: ElementRef;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrap: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
||||
@Inject(WIDGET_CONFIG) protected _config: WidgetConfig,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { DomSanitizer } from '@angular/platform-browser';
|
||||
/* SQL imports */
|
||||
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { TaskRegistry } from 'sql/platform/tasks/common/tasks';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { BaseActionContext } from 'sql/workbench/common/actions';
|
||||
@@ -51,13 +52,14 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
|
||||
private _profile: IConnectionProfile;
|
||||
private _scrollableElement: ScrollableElement;
|
||||
private $container: Builder;
|
||||
static readonly ICON_PATH_TO_CSS_RULES: Map<string /* path*/, string /* CSS rule */> = new Map<string, string>();
|
||||
|
||||
private _inited = false;
|
||||
|
||||
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) private _bootstrap: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _bootstrap: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => DomSanitizer)) private _sanitizer: DomSanitizer,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeref: ChangeDetectorRef,
|
||||
@Inject(WIDGET_CONFIG) protected _config: WidgetConfig
|
||||
@@ -130,9 +132,9 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
|
||||
let tile = $('div.task-tile').style('height', this._size + 'px').style('width', this._size + 'px');
|
||||
let innerTile = $('div');
|
||||
|
||||
// @SQLTODO - iconPath shouldn't be used as a CSS class
|
||||
if (action.iconPath && action.iconPath.dark) {
|
||||
let icon = $('span.icon').addClass(action.iconPath.dark);
|
||||
let iconClassName = TaskRegistry.getOrCreateTaskIconClassName(action);
|
||||
if (iconClassName) {
|
||||
let icon = $('span.icon').addClass(iconClassName);
|
||||
innerTile.append(icon);
|
||||
}
|
||||
innerTile.append(label);
|
||||
|
||||
@@ -13,7 +13,8 @@ import { memoize } from 'vs/base/common/decorators';
|
||||
|
||||
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget';
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { IDashboardWebview } from 'sql/services/dashboardWebview/common/dashboardWebviewService';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { IDashboardWebview } from 'sql/services/dashboard/common/dashboardViewService';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
@@ -35,19 +36,21 @@ export class WebviewWidget extends DashboardWidget implements IDashboardWidget,
|
||||
private _onMessage = new Emitter<string>();
|
||||
public readonly onMessage: Event<string> = this._onMessage.event;
|
||||
private _onMessageDisposable: IDisposable;
|
||||
private _dashboardService: DashboardServiceInterface;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => DashboardServiceInterface)) private _dashboardService: DashboardServiceInterface,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||
@Inject(WIDGET_CONFIG) protected _config: WidgetConfig,
|
||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef
|
||||
) {
|
||||
super();
|
||||
this._id = (_config.widget[selector] as IWebviewWidgetConfig).id;
|
||||
this._dashboardService = commonService as DashboardServiceInterface;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._dashboardService.dashboardWebviewService.registerWebview(this);
|
||||
this._dashboardService.dashboardViewService.registerWebview(this);
|
||||
this._createWebview();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,24 +8,24 @@
|
||||
<div class="angular-modal-body" style="display: flex; flex-direction: column;">
|
||||
<div class="angular-modal-body-content">
|
||||
<div class="dialog-label">
|
||||
{{backupNameLabel}}
|
||||
{{localizedStrings.BACKUP_NAME}}
|
||||
</div>
|
||||
<div class="input-divider" #backupsetName>
|
||||
</div>
|
||||
<div class="dialog-label">
|
||||
{{recoveryModelLabel}}
|
||||
{{localizedStrings.RECOVERY_MODEL}}
|
||||
</div>
|
||||
<div class="input-divider" #recoveryModelContainer>
|
||||
</div>
|
||||
<div class="dialog-label">
|
||||
{{backupTypeLabel}}
|
||||
{{localizedStrings.BACKUP_TYPE}}
|
||||
</div>
|
||||
<div class="input-divider" #backupTypeContainer>
|
||||
</div>
|
||||
<div class="input-divider check" #copyOnlyContainer>
|
||||
</div>
|
||||
<div class="dialog-label">
|
||||
{{backupDeviceLabel}}
|
||||
{{localizedStrings.BACKUP_DEVICE}}
|
||||
</div>
|
||||
<div class="backup-path-list">
|
||||
<div #pathContainer>
|
||||
@@ -47,11 +47,11 @@
|
||||
<div class="advanced-main-body" #advancedOptionBodyContainer>
|
||||
<!-- Compression -->
|
||||
<div class="dialog-label advanced-header">
|
||||
{{compressionLabel}}
|
||||
{{localizedStrings.COMPRESSION}}
|
||||
</div>
|
||||
<div class="indent">
|
||||
<div class="dialog-label">
|
||||
{{setBackupCompressionLabel}}
|
||||
{{localizedStrings.SET_BACKUP_COMPRESSION}}
|
||||
</div>
|
||||
<div class="dialog-label" #compressionContainer>
|
||||
</div>
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
<!-- Encryption -->
|
||||
<div class="dialog-label advanced-header">
|
||||
{{encryptionLabel}}
|
||||
{{localizedStrings.ENCRYPTION}}
|
||||
</div>
|
||||
<div class="indent">
|
||||
<div class="option check" #encryptCheckContainer>
|
||||
@@ -68,17 +68,17 @@
|
||||
<div class="icon warning">
|
||||
</div>
|
||||
<div class="warning-message">
|
||||
{{noEncryptorWarning}}
|
||||
{{localizedStrings.NO_ENCRYPTOR_WARNING}}
|
||||
</div>
|
||||
</div>
|
||||
<div #encryptContainer>
|
||||
<div class="dialog-label">
|
||||
{{algorithmLabel}}
|
||||
{{localizedStrings.ALGORITHM}}
|
||||
</div>
|
||||
<div class="dialog-label" #algorithmContainer>
|
||||
</div>
|
||||
<div class="dialog-label">
|
||||
{{certificateOrAsymmetricKeyLabel}}
|
||||
{{localizedStrings.CERTIFICATE_OR_ASYMMETRIC_KEY}}
|
||||
</div>
|
||||
<div class="dialog-label" #encryptorContainer>
|
||||
</div>
|
||||
@@ -86,33 +86,33 @@
|
||||
</div>
|
||||
|
||||
<!-- Overwrite media -->
|
||||
<div class="dialog-label advanced-header">
|
||||
{{mediaLabel}}
|
||||
<div id="media" class="dialog-label advanced-header">
|
||||
{{localizedStrings.MEDIA}}
|
||||
</div>
|
||||
<div class="radio-indent">
|
||||
<div role="radiogroup" aria-labelledby="media" class="radio-indent">
|
||||
<div class="option">
|
||||
<input type="radio" name=media-option value="no_format" [checked]="!isFormatChecked" (change)="onChangeMediaFormat()" [disabled]="isEncryptChecked">{{mediaOptionLabel}}
|
||||
<input role="radio" type="radio" name="media-option" value="no_format" [checked]="!isFormatChecked" (change)="onChangeMediaFormat()" [disabled]="isEncryptChecked" aria-labelledby="mediaOption"><span id="mediaOption">{{localizedStrings.MEDIA_OPTION}}</span>
|
||||
</div>
|
||||
<div style="margin-left:15px">
|
||||
<div role="radiogroup" aria-labelledby="mediaOption" style="margin-left:15px">
|
||||
<div class="option">
|
||||
<input type="radio" name=existing-media value="append" [(ngModel)]="selectedInitOption" [disabled]="isFormatChecked">{{existingMediaAppendLabel}}
|
||||
<input role="radio" type="radio" name="existing-media" value="append" [(ngModel)]="selectedInitOption" [disabled]="isFormatChecked" aria-labelledby="existingMediaAppend"><span id="existingMediaAppend">{{localizedStrings.EXISTING_MEDIA_APPEND}}</span>
|
||||
</div>
|
||||
<div class="option">
|
||||
<input type="radio" name=existing-media value="overwrite" [(ngModel)]="selectedInitOption" [disabled]="isFormatChecked">{{existingMediaOverwriteLabel}}
|
||||
<input role="radio" type="radio" name="existing-media" value="overwrite" [(ngModel)]="selectedInitOption" [disabled]="isFormatChecked" aria-labelledby="existingMediaOverwrite"><span id="existingMediaOverwrite">{{localizedStrings.EXISTING_MEDIA_OVERWRITE}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option">
|
||||
<input type="radio" name=media-option value="format" [checked]="isFormatChecked" (change)="onChangeMediaFormat()">{{mediaOptionFormatLabel}}
|
||||
<input role="radio" type="radio" name="media-option" value="format" [checked]="isFormatChecked" (change)="onChangeMediaFormat()" aria-labelledby="mediaOptionFormat"><span id="mediaOptionFormat">{{localizedStrings.MEDIA_OPTION_FORMAT}}</span>
|
||||
</div>
|
||||
<div style="margin-left: 22px">
|
||||
<div class="dialog-label">
|
||||
{{newMediaSetNameLabel}}
|
||||
{{localizedStrings.NEW_MEDIA_SET_NAME}}
|
||||
</div>
|
||||
<div class="dialog-label" #mediaName>
|
||||
</div>
|
||||
<div class="dialog-label">
|
||||
{{newMediaSetDescriptionLabel}}
|
||||
{{localizedStrings.NEW_MEDIA_SET_DESCRIPTION}}
|
||||
</div>
|
||||
<div class="dialog-label" #mediaDescription>
|
||||
</div>
|
||||
@@ -120,21 +120,21 @@
|
||||
</div>
|
||||
|
||||
<!-- Transaction log -->
|
||||
<div class="dialog-label advanced-header">
|
||||
{{transactionLogLabel}}
|
||||
<div id="transactionLog" class="dialog-label advanced-header">
|
||||
{{localizedStrings.TRANSACTION_LOG}}
|
||||
</div>
|
||||
<div class="radio-indent">
|
||||
<div role="radiogroup" aria-labelledby="transactionLog" class="radio-indent">
|
||||
<div class="option">
|
||||
<input type="radio" name=t-log value="truncate" [checked]="isTruncateChecked" (change)="onChangeTlog()" [disabled]="disableTlog">Truncate the transaction log
|
||||
<input role="radio" type="radio" name="t-log" value="truncate" [checked]="isTruncateChecked" (change)="onChangeTlog()" [disabled]="disableTlog" aria-labelledby="truncateTransaction"><span id="truncateTransaction">{{localizedStrings.TRUNCATE_TRANSACTION_LOG}}</span>
|
||||
</div>
|
||||
<div class="option">
|
||||
<input type="radio" name=t-log value="taillog" [checked]="isTaillogChecked" (change)="onChangeTlog()" [disabled]="disableTlog">Backup the tail of the log
|
||||
<input role="radio" type="radio" name="t-log" value="taillog" [checked]="isTaillogChecked" (change)="onChangeTlog()" [disabled]="disableTlog" aria-labelledby="backupTail"><span id="backupTail">{{localizedStrings.BACKUP_TAIL}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reliability -->
|
||||
<div class="dialog-label advanced-header">
|
||||
{{reliabilityLabel}}
|
||||
{{localizedStrings.RELIABILITY}}
|
||||
</div>
|
||||
<div class="indent">
|
||||
<div class="option check" #checksumContainer>
|
||||
@@ -147,11 +147,11 @@
|
||||
|
||||
<!-- Backup expiration -->
|
||||
<div class="dialog-label advanced-header">
|
||||
{{expirationLabel}}
|
||||
{{localizedStrings.EXPIRATION}}
|
||||
</div>
|
||||
<div class="indent">
|
||||
<div class="dialog-label">
|
||||
{{setBackupRetainDaysLabel}}
|
||||
{{localizedStrings.SET_BACKUP_RETAIN_DAYS}}
|
||||
</div>
|
||||
<div class="dialog-label">
|
||||
<div #backupDaysContainer></div>
|
||||
|
||||
@@ -76,6 +76,38 @@ interface MssqlBackupInfo {
|
||||
encryptorName: string;
|
||||
}
|
||||
|
||||
const LocalizedStrings = {
|
||||
BACKUP_NAME: localize('backup.backupName', 'Backup name'),
|
||||
RECOVERY_MODEL: localize('backup.recoveryModel', 'Recovery model'),
|
||||
BACKUP_TYPE: localize('backup.backupType', 'Backup type'),
|
||||
BACKUP_DEVICE: localize('backup.backupDevice', 'Backup files'),
|
||||
ALGORITHM: localize('backup.algorithm', 'Algorithm'),
|
||||
CERTIFICATE_OR_ASYMMETRIC_KEY: localize('backup.certificateOrAsymmetricKey', 'Certificate or Asymmetric key'),
|
||||
MEDIA: localize('backup.media', 'Media'),
|
||||
MEDIA_OPTION: localize('backup.mediaOption', 'Backup to the existing media set'),
|
||||
MEDIA_OPTION_FORMAT: localize('backup.mediaOptionFormat', 'Backup to a new media set'),
|
||||
EXISTING_MEDIA_APPEND: localize('backup.existingMediaAppend', 'Append to the existing backup set'),
|
||||
EXISTING_MEDIA_OVERWRITE: localize('backup.existingMediaOverwrite', 'Overwrite all existing backup sets'),
|
||||
NEW_MEDIA_SET_NAME: localize('backup.newMediaSetName', 'New media set name'),
|
||||
NEW_MEDIA_SET_DESCRIPTION: localize('backup.newMediaSetDescription', 'New media set description'),
|
||||
CHECKSUM_CONTAINER: localize('backup.checksumContainer', 'Perform checksum before writing to media'),
|
||||
VERIFY_CONTAINER: localize('backup.verifyContainer', 'Verify backup when finished'),
|
||||
CONTINUE_ON_ERROR_CONTAINER: localize('backup.continueOnErrorContainer', 'Continue on error'),
|
||||
EXPIRATION: localize('backup.expiration', 'Expiration'),
|
||||
SET_BACKUP_RETAIN_DAYS: localize('backup.setBackupRetainDays', 'Set backup retain days'),
|
||||
COPY_ONLY: localize('backup.copyOnly', 'Copy-only backup'),
|
||||
ADVANCED_CONFIGURATION: localize('backup.advancedConfiguration', 'Advanced Configuration'),
|
||||
COMPRESSION: localize('backup.compression', 'Compression'),
|
||||
SET_BACKUP_COMPRESSION: localize('backup.setBackupCompression', 'Set backup compression'),
|
||||
ENCRYPTION: localize('backup.encryption', 'Encryption'),
|
||||
TRANSACTION_LOG: localize('backup.transactionLog', 'Transaction log'),
|
||||
TRUNCATE_TRANSACTION_LOG: localize('backup.truncateTransactionLog', 'Truncate the transaction log'),
|
||||
BACKUP_TAIL: localize('backup.backupTail', 'Backup the tail of the log'),
|
||||
RELIABILITY: localize('backup.reliability', 'Reliability'),
|
||||
MEDIA_NAME_REQUIRED_ERROR: localize('backup.mediaNameRequired', 'Media name is required'),
|
||||
NO_ENCRYPTOR_WARNING: localize('backup.noEncryptorWarning', "No certificate or asymmetric key is available")
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: BACKUP_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('sql/parts/disasterRecovery/backup/backup.component.html'))
|
||||
@@ -109,36 +141,7 @@ export class BackupComponent {
|
||||
@ViewChild('advancedOptionContainer', { read: ElementRef }) advancedOptionElement;
|
||||
@ViewChild('advancedOptionBodyContainer', { read: ElementRef }) advancedOptionBodyElement;
|
||||
|
||||
// tslint:disable:no-unused-variable
|
||||
private readonly backupNameLabel: string = localize('backup.backupName', 'Backup name');
|
||||
private readonly recoveryModelLabel: string = localize('backup.recoveryModel', 'Recovery model');
|
||||
private readonly backupTypeLabel: string = localize('backup.backupType', 'Backup type');
|
||||
private readonly backupDeviceLabel: string = localize('backup.backupDevice', 'Backup files');
|
||||
private readonly algorithmLabel: string = localize('backup.algorithm', 'Algorithm');
|
||||
private readonly certificateOrAsymmetricKeyLabel: string = localize('backup.certificateOrAsymmetricKey', 'Certificate or Asymmetric key');
|
||||
private readonly mediaLabel: string = localize('backup.media', 'Media');
|
||||
private readonly mediaOptionLabel: string = localize('backup.mediaOption', 'Backup to the existing media set');
|
||||
private readonly mediaOptionFormatLabel: string = localize('backup.mediaOptionFormat', 'Backup to a new media set');
|
||||
private readonly existingMediaAppendLabel: string = localize('backup.existingMediaAppend', 'Append to the existing backup set');
|
||||
private readonly existingMediaOverwriteLabel: string = localize('backup.existingMediaOverwrite', 'Overwrite all existing backup sets');
|
||||
private readonly newMediaSetNameLabel: string = localize('backup.newMediaSetName', 'New media set name');
|
||||
private readonly newMediaSetDescriptionLabel: string = localize('backup.newMediaSetDescription', 'New media set description');
|
||||
private readonly checksumContainerLabel: string = localize('backup.checksumContainer', 'Perform checksum before writing to media');
|
||||
private readonly verifyContainerLabel: string = localize('backup.verifyContainer', 'Verify backup when finished');
|
||||
private readonly continueOnErrorContainerLabel: string = localize('backup.continueOnErrorContainer', 'Continue on error');
|
||||
private readonly expirationLabel: string = localize('backup.expiration', 'Expiration');
|
||||
private readonly setBackupRetainDaysLabel: string = localize('backup.setBackupRetainDays', 'Set backup retain days');
|
||||
private readonly copyOnlyLabel: string = localize('backup.copyOnly', 'Copy-only backup');
|
||||
private readonly advancedConfigurationLabel: string = localize('backup.advancedConfiguration', 'Advanced Configuration');
|
||||
private readonly compressionLabel: string = localize('backup.compression', 'Compression');
|
||||
private readonly setBackupCompressionLabel: string = localize('backup.setBackupCompression', 'Set backup compression');
|
||||
private readonly encryptionLabel: string = localize('backup.encryption', 'Encryption');
|
||||
private readonly transactionLogLabel: string = localize('backup.transactionLog', 'Transaction log');
|
||||
private readonly reliabilityLabel: string = localize('backup.reliability', 'Reliability');
|
||||
private readonly mediaNameRequiredError: string = localize('backup.mediaNameRequired', 'Media name is required');
|
||||
private readonly noEncryptorWarning: string = localize('backup.noEncryptorWarning', "No certificate or asymmetric key is available");
|
||||
|
||||
// tslint:enable:no-unused-variable
|
||||
private localizedStrings = LocalizedStrings;
|
||||
|
||||
private _backupService: IBackupService;
|
||||
private _backupUiService: IBackupUiService;
|
||||
@@ -207,48 +210,58 @@ export class BackupComponent {
|
||||
let self = this;
|
||||
this.addFooterButtons();
|
||||
|
||||
this.recoveryBox = new InputBox(this.recoveryModelElement.nativeElement, this._bootstrapService.contextViewService, { placeholder: this.recoveryModel });
|
||||
this.recoveryBox = new InputBox(this.recoveryModelElement.nativeElement, this._bootstrapService.contextViewService, {
|
||||
placeholder: this.recoveryModel,
|
||||
ariaLabel: LocalizedStrings.RECOVERY_MODEL
|
||||
});
|
||||
// Set backup type
|
||||
this.backupTypeSelectBox = new SelectBox([], '', this._bootstrapService.contextViewService);
|
||||
this.backupTypeSelectBox.render(this.backupTypeElement.nativeElement);
|
||||
|
||||
// Set copy-only check box
|
||||
this.copyOnlyCheckBox = new Checkbox(this.copyOnlyElement.nativeElement, {
|
||||
label: this.copyOnlyLabel,
|
||||
label: LocalizedStrings.COPY_ONLY,
|
||||
checked: false,
|
||||
onChange: (viaKeyboard) => { }
|
||||
onChange: (viaKeyboard) => { },
|
||||
ariaLabel: LocalizedStrings.COPY_ONLY
|
||||
});
|
||||
|
||||
// Encryption checkbox
|
||||
this.encryptCheckBox = new Checkbox(this.encryptElement.nativeElement, {
|
||||
label: this.encryptionLabel,
|
||||
label: LocalizedStrings.ENCRYPTION,
|
||||
checked: false,
|
||||
onChange: (viaKeyboard) => self.onChangeEncrypt()
|
||||
onChange: (viaKeyboard) => self.onChangeEncrypt(),
|
||||
ariaLabel: LocalizedStrings.ENCRYPTION
|
||||
});
|
||||
|
||||
// Verify backup checkbox
|
||||
this.verifyCheckBox = new Checkbox(this.verifyElement.nativeElement, {
|
||||
label: this.verifyContainerLabel,
|
||||
label: LocalizedStrings.VERIFY_CONTAINER,
|
||||
checked: false,
|
||||
onChange: (viaKeyboard) => { }
|
||||
onChange: (viaKeyboard) => { },
|
||||
ariaLabel: LocalizedStrings.VERIFY_CONTAINER
|
||||
});
|
||||
|
||||
// Perform checksum checkbox
|
||||
this.checksumCheckBox = new Checkbox(this.checksumElement.nativeElement, {
|
||||
label: this.checksumContainerLabel,
|
||||
label: LocalizedStrings.CHECKSUM_CONTAINER,
|
||||
checked: false,
|
||||
onChange: (viaKeyboard) => { }
|
||||
onChange: (viaKeyboard) => { },
|
||||
ariaLabel: LocalizedStrings.CHECKSUM_CONTAINER
|
||||
});
|
||||
|
||||
// Continue on error checkbox
|
||||
this.continueOnErrorCheckBox = new Checkbox(this.continueOnErrorElement.nativeElement, {
|
||||
label: this.continueOnErrorContainerLabel,
|
||||
label: LocalizedStrings.CONTINUE_ON_ERROR_CONTAINER,
|
||||
checked: false,
|
||||
onChange: (viaKeyboard) => { }
|
||||
onChange: (viaKeyboard) => { },
|
||||
ariaLabel: LocalizedStrings.CONTINUE_ON_ERROR_CONTAINER
|
||||
});
|
||||
|
||||
// Set backup name
|
||||
this.backupNameBox = new InputBox(this.backupNameElement.nativeElement, this._bootstrapService.contextViewService);
|
||||
this.backupNameBox = new InputBox(this.backupNameElement.nativeElement, this._bootstrapService.contextViewService, {
|
||||
ariaLabel: LocalizedStrings.BACKUP_NAME
|
||||
});
|
||||
|
||||
// Set backup path list
|
||||
this.pathListBox = new ListBox([], '', this._bootstrapService.contextViewService, this._bootstrapService.clipboardService);
|
||||
@@ -278,11 +291,15 @@ export class BackupComponent {
|
||||
this._bootstrapService.contextViewService,
|
||||
{
|
||||
validationOptions: {
|
||||
validation: (value: string) => !value ? ({ type: MessageType.ERROR, content: this.mediaNameRequiredError }) : null
|
||||
}
|
||||
});
|
||||
validation: (value: string) => !value ? ({ type: MessageType.ERROR, content: LocalizedStrings.MEDIA_NAME_REQUIRED_ERROR }) : null
|
||||
},
|
||||
ariaLabel: LocalizedStrings.NEW_MEDIA_SET_NAME
|
||||
}
|
||||
);
|
||||
|
||||
this.mediaDescriptionBox = new InputBox(this.mediaDescriptionElement.nativeElement, this._bootstrapService.contextViewService);
|
||||
this.mediaDescriptionBox = new InputBox(this.mediaDescriptionElement.nativeElement, this._bootstrapService.contextViewService, {
|
||||
ariaLabel: LocalizedStrings.NEW_MEDIA_SET_DESCRIPTION
|
||||
});
|
||||
|
||||
// Set backup retain days
|
||||
let invalidInputMessage = localize('backupComponent.invalidInput', 'Invalid input. Value must be greater than or equal 0.');
|
||||
@@ -300,7 +317,8 @@ export class BackupComponent {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ariaLabel: LocalizedStrings.SET_BACKUP_RETAIN_DAYS
|
||||
});
|
||||
|
||||
// Disable elements
|
||||
@@ -316,7 +334,7 @@ export class BackupComponent {
|
||||
// Set category view for advanced options. This should be defined in ngAfterViewInit so that it correctly calculates the text height after data binding.
|
||||
var splitview = new SplitView(this.advancedOptionElement.nativeElement);
|
||||
var advancedBodySize = DOM.getTotalHeight(this.advancedOptionBodyElement.nativeElement);
|
||||
var categoryView = new CategoryView(this.advancedConfigurationLabel, this.advancedOptionBodyElement.nativeElement, true, advancedBodySize, this._advancedHeaderSize);
|
||||
var categoryView = new CategoryView(LocalizedStrings.ADVANCED_CONFIGURATION, this.advancedOptionBodyElement.nativeElement, true, advancedBodySize, this._advancedHeaderSize);
|
||||
splitview.addView(categoryView);
|
||||
splitview.layout(advancedBodySize + this._advancedHeaderSize);
|
||||
|
||||
@@ -584,7 +602,7 @@ export class BackupComponent {
|
||||
if (strings.isFalsyOrWhitespace(this.mediaNameBox.value)) {
|
||||
this.backupEnabled = false;
|
||||
this.backupButton.enabled = false;
|
||||
this.mediaNameBox.showMessage({ type: MessageType.ERROR, content: this.mediaNameRequiredError });
|
||||
this.mediaNameBox.showMessage({ type: MessageType.ERROR, content: LocalizedStrings.MEDIA_NAME_REQUIRED_ERROR });
|
||||
}
|
||||
} else {
|
||||
this.enableBackupButton();
|
||||
|
||||
@@ -145,23 +145,26 @@ export class BackupUiService implements IBackupUiService {
|
||||
|
||||
let backupOptions = this.getOptions(this._currentProvider);
|
||||
return new TPromise<void>(() => {
|
||||
let uri = this._connectionManagementService.getConnectionId(connection)
|
||||
+ ProviderConnectionInfo.idSeparator
|
||||
+ ConnectionUtils.ConnectionUriBackupIdAttributeName
|
||||
+ ProviderConnectionInfo.nameValueSeparator
|
||||
+ BackupUiService._connectionUniqueId;
|
||||
|
||||
this._connectionUri = uri;
|
||||
|
||||
BackupUiService._connectionUniqueId++;
|
||||
|
||||
// Create connection if needed
|
||||
if (!this._connectionManagementService.isConnected(uri)) {
|
||||
this._connectionManagementService.connect(connection, uri).then(() => {
|
||||
this._onShowBackupEvent.fire({ connection: connection, ownerUri: uri });
|
||||
});
|
||||
}
|
||||
|
||||
if (backupOptions) {
|
||||
(backupDialog as OptionsDialog).open(backupOptions, self._optionValues);
|
||||
} else {
|
||||
let uri = this._connectionManagementService.getConnectionId(connection)
|
||||
+ ProviderConnectionInfo.idSeparator
|
||||
+ ConnectionUtils.ConnectionUriBackupIdAttributeName
|
||||
+ ProviderConnectionInfo.nameValueSeparator
|
||||
+ BackupUiService._connectionUniqueId;
|
||||
|
||||
BackupUiService._connectionUniqueId++;
|
||||
|
||||
// Create connection if needed
|
||||
if (!this._connectionManagementService.isConnected(uri)) {
|
||||
this._connectionManagementService.connect(connection, uri).then(() => {
|
||||
this._onShowBackupEvent.fire({ connection: connection, ownerUri: uri });
|
||||
});
|
||||
}
|
||||
(backupDialog as BackupDialog).open(connection);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -40,6 +40,7 @@ import * as DOM from 'vs/base/browser/dom';
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
interface FileListElement {
|
||||
logicalFileName: string;
|
||||
@@ -48,6 +49,11 @@ interface FileListElement {
|
||||
restoreAs: string;
|
||||
}
|
||||
|
||||
const LocalizedStrings = {
|
||||
BACKFILEPATH: localize('backupFilePath', "Backup file path"),
|
||||
TARGETDATABASE: localize('targetDatabase', 'Target database')
|
||||
};
|
||||
|
||||
export class RestoreDialog extends Modal {
|
||||
public viewModel: RestoreViewModel;
|
||||
|
||||
@@ -176,12 +182,13 @@ export class RestoreDialog extends Modal {
|
||||
validationOptions: {
|
||||
validation: (value: string) => !value ? ({ type: MessageType.ERROR, content: errorMessage }) : null
|
||||
},
|
||||
placeholder: localize('multipleBackupFilePath', 'Please enter one or more file paths separated by commas')
|
||||
placeholder: localize('multipleBackupFilePath', 'Please enter one or more file paths separated by commas'),
|
||||
ariaLabel: LocalizedStrings.BACKFILEPATH
|
||||
};
|
||||
|
||||
filePathContainer.div({ class: 'dialog-input-section' }, (inputContainer) => {
|
||||
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
|
||||
labelContainer.innerHtml(localize('backupFilePath', "Backup file path"));
|
||||
labelContainer.safeInnerHtml(LocalizedStrings.BACKFILEPATH);
|
||||
});
|
||||
|
||||
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
||||
@@ -218,7 +225,7 @@ export class RestoreDialog extends Modal {
|
||||
|
||||
destinationContainer.div({ class: 'dialog-input-section' }, (inputContainer) => {
|
||||
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
|
||||
labelContainer.innerHtml(localize('targetDatabase', 'Target database'));
|
||||
labelContainer.innerHtml(LocalizedStrings.TARGETDATABASE);
|
||||
});
|
||||
|
||||
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
||||
@@ -226,7 +233,8 @@ export class RestoreDialog extends Modal {
|
||||
inputCellContainer.style('width', '100%');
|
||||
this._databaseDropdown = new Dropdown(inputCellContainer.getHTMLElement(), this._contextViewService, this._themeService,
|
||||
{
|
||||
strictSelection: false
|
||||
strictSelection: false,
|
||||
ariaLabel: LocalizedStrings.TARGETDATABASE
|
||||
}
|
||||
);
|
||||
this._databaseDropdown.onValueChange(s => {
|
||||
@@ -514,7 +522,8 @@ export class RestoreDialog extends Modal {
|
||||
checkbox = new Checkbox(inputCellContainer.getHTMLElement(), {
|
||||
label: label,
|
||||
checked: isChecked,
|
||||
onChange: onCheck
|
||||
onChange: onCheck,
|
||||
ariaLabel: label
|
||||
});
|
||||
});
|
||||
return checkbox;
|
||||
@@ -537,13 +546,16 @@ export class RestoreDialog extends Modal {
|
||||
|
||||
private createInputBoxHelper(container: Builder, label: string, options?: IInputOptions): InputBox {
|
||||
let inputBox: InputBox;
|
||||
let ariaOptions = {
|
||||
ariaLabel: label
|
||||
};
|
||||
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
|
||||
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
|
||||
labelContainer.innerHtml(label);
|
||||
labelContainer.safeInnerHtml(label);
|
||||
});
|
||||
|
||||
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
||||
inputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, options);
|
||||
inputBox = new InputBox(inputCellContainer.getHTMLElement(), this._contextViewService, mixin(ariaOptions, options));
|
||||
});
|
||||
});
|
||||
return inputBox;
|
||||
|
||||
110
src/sql/parts/extensions/sqlExtensionsHelper.ts
Normal file
110
src/sql/parts/extensions/sqlExtensionsHelper.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!vs/workbench/parts/extensions/browser/media/extensionEditor';
|
||||
|
||||
|
||||
import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { append, $, addClass, removeClass, finalHandler, join, toggleClass } from 'vs/base/browser/dom';
|
||||
import { IInsightTypeContrib } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { IDashboardTabContrib } from 'sql/parts/dashboard/common/dashboardTab.contribution';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
class ContributionReader {
|
||||
constructor(private manifest: IExtensionManifest) { }
|
||||
|
||||
public dashboardInsights(): IInsightTypeContrib[] {
|
||||
let contributes = this.manifest.contributes;
|
||||
if (contributes) {
|
||||
let insights: IInsightTypeContrib | IInsightTypeContrib[] = contributes['dashboard.insights'];
|
||||
if (insights) {
|
||||
if (!Array.isArray(insights)) {
|
||||
return [insights];
|
||||
}
|
||||
return insights;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public dashboardTabs(): IDashboardTabContrib[] {
|
||||
let contributes = this.manifest.contributes;
|
||||
if (contributes) {
|
||||
let tabs: IDashboardTabContrib | IDashboardTabContrib[] = contributes['dashboard.tabs'];
|
||||
if (tabs) {
|
||||
if (!Array.isArray(tabs)) {
|
||||
return [tabs];
|
||||
}
|
||||
return tabs;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function renderDashboardContributions(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean {
|
||||
let contributionReader = new ContributionReader(manifest);
|
||||
renderDashboardTabs(onDetailsToggle, contributionReader, container);
|
||||
renderDashboardInsights(onDetailsToggle, contributionReader, container);
|
||||
return true;
|
||||
}
|
||||
|
||||
function renderDashboardTabs(onDetailsToggle: Function, contributionReader: ContributionReader, container: HTMLElement): boolean {
|
||||
let tabs = contributionReader.dashboardTabs();
|
||||
|
||||
if (!tabs || !tabs.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const details = $('details', { open: true, ontoggle: onDetailsToggle },
|
||||
$('summary', null, localize('tabs', "Dashboard Tabs ({0})", tabs.length)),
|
||||
$('table', null,
|
||||
$('tr', null,
|
||||
$('th', null, localize('tabId', "Id")),
|
||||
$('th', null, localize('tabTitle', "Title")),
|
||||
$('th', null, localize('tabDescription', "Description"))
|
||||
),
|
||||
...tabs.map(tab => $('tr', null,
|
||||
$('td', null, $('code', null, tab.id)),
|
||||
$('td', null, tab.title ? tab.title : tab.id),
|
||||
$('td', null, tab.description),
|
||||
))
|
||||
)
|
||||
);
|
||||
|
||||
append(container, details);
|
||||
return true;
|
||||
}
|
||||
|
||||
function renderDashboardInsights(onDetailsToggle: Function, contributionReader: ContributionReader, container: HTMLElement): boolean {
|
||||
let insights = contributionReader.dashboardInsights();
|
||||
|
||||
if (!insights || !insights.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const details = $('details', { open: true, ontoggle: onDetailsToggle },
|
||||
$('summary', null, localize('insights', "Dashboard Insights ({0})", insights.length)),
|
||||
$('table', null,
|
||||
$('tr', null,
|
||||
$('th', null, localize('insightId', "Id")),
|
||||
$('th', null, localize('name', "Name")),
|
||||
$('th', null, localize('insight condition', "When"))
|
||||
),
|
||||
...insights.map(insight => $('tr', null,
|
||||
$('td', null, $('code', null, insight.id)),
|
||||
$('td', null, insight.contrib.name ? insight.contrib.name : insight.id),
|
||||
$('td', null, insight.contrib.when),
|
||||
))
|
||||
)
|
||||
);
|
||||
|
||||
append(container, details);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,9 @@ export class FileBrowserDialog extends Modal {
|
||||
tableWrapper.element('table', { class: 'file-table-content' }, (tableContainer) => {
|
||||
let pathLabel = localize('filebrowser.filepath', 'Selected path');
|
||||
let pathBuilder = DialogHelper.appendRow(tableContainer, pathLabel, 'file-input-label', 'file-input-box');
|
||||
this._filePathInputBox = new InputBox(pathBuilder.getHTMLElement(), this._contextViewService);
|
||||
this._filePathInputBox = new InputBox(pathBuilder.getHTMLElement(), this._contextViewService, {
|
||||
ariaLabel: pathLabel
|
||||
});
|
||||
|
||||
this._fileFilterSelectBox = new SelectBox(['*'], '*', this._contextViewService);
|
||||
let filterLabel = localize('fileFilter', 'Files of type');
|
||||
|
||||
@@ -17,3 +17,4 @@ export let SelectAllMessages = 'SelectAllMessages';
|
||||
export let SaveAsCsv = 'SaveAsCSV';
|
||||
export let SaveAsJSON = 'SaveAsJSON';
|
||||
export let SaveAsExcel = 'SaveAsExcel';
|
||||
export let GoToNextQueryOutputTab = 'GoToNextQueryOutputTab';
|
||||
|
||||
@@ -24,6 +24,8 @@ export const MESSAGES_SELECTALL_ID = 'grid.messages.selectAll';
|
||||
export const MESSAGES_COPY_ID = 'grid.messages.copy';
|
||||
export const TOGGLERESULTS_ID = 'grid.toggleResultPane';
|
||||
export const TOGGLEMESSAGES_ID = 'grid.toggleMessagePane';
|
||||
export const GOTONEXTQUERYOUTPUTTAB_ID = 'query.goToNextQueryOutputTab';
|
||||
|
||||
|
||||
export class GridActionProvider {
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ export const toggleResultsPane = (accessor: ServicesAccessor) => {
|
||||
runActionOnActiveResultsEditor(accessor, GridContentEvents.ToggleResultPane);
|
||||
};
|
||||
|
||||
export const goToNextQueryOutputTab = (accessor: ServicesAccessor) => {
|
||||
runActionOnActiveResultsEditor(accessor, GridContentEvents.GoToNextQueryOutputTab);
|
||||
};
|
||||
|
||||
export const saveAsCsv = (accessor: ServicesAccessor) => {
|
||||
runActionOnActiveResultsEditor(accessor, GridContentEvents.SaveAsCsv);
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ import { DataService } from 'sql/parts/grid/services/dataService';
|
||||
import * as actions from 'sql/parts/grid/views/gridActions';
|
||||
import * as Services from 'sql/parts/grid/services/sharedServices';
|
||||
import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents';
|
||||
import { ResultsVisibleContext, ResultsGridFocussedContext, ResultsMessagesFocussedContext } from 'sql/parts/query/common/queryContext';
|
||||
import { ResultsVisibleContext, ResultsGridFocussedContext, ResultsMessagesFocussedContext, QueryEditorVisibleContext } from 'sql/parts/query/common/queryContext';
|
||||
import { IBootstrapService } from 'sql/services/bootstrap/bootstrapService';
|
||||
import { error } from 'sql/base/common/log';
|
||||
|
||||
@@ -74,6 +74,7 @@ export abstract class GridParentComponent {
|
||||
private resultsVisibleContextKey: IContextKey<boolean>;
|
||||
private gridFocussedContextKey: IContextKey<boolean>;
|
||||
private messagesFocussedContextKey: IContextKey<boolean>;
|
||||
private queryEditorVisible: IContextKey<boolean>;
|
||||
|
||||
// All datasets
|
||||
// Place holder data sets to buffer between data sets and rendered data sets
|
||||
@@ -164,6 +165,9 @@ export abstract class GridParentComponent {
|
||||
case GridContentEvents.SaveAsExcel:
|
||||
self.sendSaveRequest(SaveFormat.EXCEL);
|
||||
break;
|
||||
case GridContentEvents.GoToNextQueryOutputTab:
|
||||
self.goToNextQueryOutputTab();
|
||||
break;
|
||||
default:
|
||||
error('Unexpected grid content event type "' + type + '" sent');
|
||||
break;
|
||||
@@ -187,6 +191,9 @@ export abstract class GridParentComponent {
|
||||
|
||||
private bindKeys(contextKeyService: IContextKeyService): void {
|
||||
if (contextKeyService) {
|
||||
this.queryEditorVisible = QueryEditorVisibleContext.bindTo(contextKeyService);
|
||||
this.queryEditorVisible.set(true);
|
||||
|
||||
let gridContextKeyService = this._bootstrapService.contextKeyService.createScoped(this._el.nativeElement);
|
||||
this.toDispose.push(gridContextKeyService);
|
||||
this.resultsVisibleContextKey = ResultsVisibleContext.bindTo(gridContextKeyService);
|
||||
@@ -201,7 +208,7 @@ export abstract class GridParentComponent {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
|
||||
private toggleResultPane(): void {
|
||||
protected toggleResultPane(): void {
|
||||
this.resultActive = !this.resultActive;
|
||||
if (this.resultActive) {
|
||||
this.resizeGrids();
|
||||
@@ -209,7 +216,7 @@ export abstract class GridParentComponent {
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
|
||||
private toggleMessagePane(): void {
|
||||
protected toggleMessagePane(): void {
|
||||
this.messageActive = !this.messageActive;
|
||||
}
|
||||
|
||||
@@ -268,6 +275,9 @@ export abstract class GridParentComponent {
|
||||
return '';
|
||||
}
|
||||
|
||||
protected goToNextQueryOutputTab(): void {
|
||||
}
|
||||
|
||||
private initShortcutsBase(): void {
|
||||
let shortcuts = {
|
||||
'ToggleResultPane': () => {
|
||||
@@ -293,6 +303,9 @@ export abstract class GridParentComponent {
|
||||
},
|
||||
'SaveAsExcel': () => {
|
||||
this.sendSaveRequest(SaveFormat.EXCEL);
|
||||
},
|
||||
'GoToNextQueryOutputTab': () => {
|
||||
this.goToNextQueryOutputTab();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -8,28 +8,28 @@
|
||||
</div>
|
||||
<div class="angular-modal-body-content chart-viewer" style="flex:1 1 auto; border-left: 1px solid; margin: 5px;">
|
||||
<div style="position: relative; width: 100%">
|
||||
<div class="dialog-label">{{chartTypeLabel}}</div>
|
||||
<div class="input-divider" #chartTypesContainer></div>
|
||||
<div class="dialog-label" id="chartTypeLabel">{{localizedStrings.CHART_TYPE}}</div>
|
||||
<div class="input-divider" aria-labelledby="chartTypeLabel" #chartTypesContainer></div>
|
||||
<div [hidden]="chartTypesSelectBox.value === 'count' || chartTypesSelectBox.value === 'image'">
|
||||
<div [hidden]="!showDataType">
|
||||
<div class="dialog-label">{{dataTypeLabel}}</div>
|
||||
<div class="radio-indent">
|
||||
<div class="dialog-label" id="dataTypeLabel">{{localizedStrings.DATA_TYPE}}</div>
|
||||
<div class="radio-indent" role="radiogroup" aria-labelledby="dataTypeLabel">
|
||||
<div class="option">
|
||||
<input type="radio" name=data-type value="number" [(ngModel)]="dataType">{{numberLabel}}
|
||||
<input type="radio" role="radio" name="data-type" value="number" [(ngModel)]="dataType" aria-labelledby="numberLabel"><span id="numberLabel">{{localizedStrings.NUMBER}}</span>
|
||||
</div>
|
||||
<div class="option">
|
||||
<input type="radio" name=data-type value="point" [(ngModel)]="dataType">{{pointLabel}}
|
||||
<input type="radio" role="radio" name="data-type" value="point" [(ngModel)]="dataType" aria-labelledby="pointLabel"><span id="pointLabel">{{localizedStrings.POINT}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div [hidden]="!showDataDirection">
|
||||
<div class="dialog-label">{{dataDirectionLabel}}</div>
|
||||
<div class="radio-indent">
|
||||
<div class="dialog-label" id="dataDirectionLabel">{{localizedStrings.DATA_DIRECTION}}</div>
|
||||
<div class="radio-indent" role="radiogroup" aria-labelledby="dataDirectionLabel">
|
||||
<div class="option">
|
||||
<input type="radio" name=data-direction value="vertical" [(ngModel)]="dataDirection">{{verticalLabel}}
|
||||
<input type="radio" role="radio" name="data-direction" value="vertical" [(ngModel)]="dataDirection" aria-labelledby="verticalLabel"><span id="verticalLabel">{{localizedStrings.VERTICAL}}</span>
|
||||
</div>
|
||||
<div class="option">
|
||||
<input type="radio" name=data-direction value="horizontal" [(ngModel)]="dataDirection">{{horizontalLabel}}
|
||||
<input type="radio" role="radio" name="data-direction" value="horizontal" [(ngModel)]="dataDirection" aria-labelledby="horizontalLabel"><span id="horizontalLabel">{{localizedStrings.HORIZONTAL}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -41,8 +41,8 @@
|
||||
<div class="input-divider" #columnsAsLabelsContainer></div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-label">{{legendLabel}}</div>
|
||||
<div class="input-divider" #legendContainer></div>
|
||||
<div class="dialog-label" id="legendLabel">{{localizedStrings.LEGEND}}</div>
|
||||
<div class="input-divider" #legendContainer aria-labelledby="legendLabel"></div>
|
||||
|
||||
<div class="footer">
|
||||
<div class="right-footer">
|
||||
|
||||
@@ -42,6 +42,20 @@ import * as pfs from 'vs/base/node/pfs';
|
||||
|
||||
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
|
||||
|
||||
const LocalizedStrings = {
|
||||
CHART_TYPE: nls.localize('chartTypeLabel', 'Chart Type'),
|
||||
DATA_DIRECTION: nls.localize('dataDirectionLabel', 'Data Direction'),
|
||||
VERTICAL: nls.localize('verticalLabel', 'Vertical'),
|
||||
HORIZONTAL: nls.localize('horizontalLabel', 'Horizontal'),
|
||||
DATA_TYPE: nls.localize('dataTypeLabel', 'Data Type'),
|
||||
NUMBER: nls.localize('numberLabel', 'Number'),
|
||||
POINT: nls.localize('pointLabel', 'Point'),
|
||||
LABEL_FIRST_COLUMN: nls.localize('labelFirstColumnLabel', 'Use First Column as row label?'),
|
||||
COLUMNS_AS_LABELS: nls.localize('columnsAsLabelsLabel', 'Use Column names as labels?'),
|
||||
LEGEND: nls.localize('legendLabel', 'Legend Position'),
|
||||
CHART_NOT_FOUND: nls.localize('chartNotFound', 'Could not find chart to save')
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'chart-viewer',
|
||||
templateUrl: decodeURI(require.toUrl('sql/parts/grid/views/query/chartViewer.component.html'))
|
||||
@@ -54,19 +68,6 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
||||
private columnsAsLabelsCheckBox: Checkbox;
|
||||
|
||||
/* UI */
|
||||
/* tslint:disable:no-unused-variable */
|
||||
private chartTypeLabel: string = nls.localize('chartTypeLabel', 'Chart Type');
|
||||
private dataDirectionLabel: string = nls.localize('dataDirectionLabel', 'Data Direction');
|
||||
private verticalLabel: string = nls.localize('verticalLabel', 'Vertical');
|
||||
private horizontalLabel: string = nls.localize('horizontalLabel', 'Horizontal');
|
||||
private dataTypeLabel: string = nls.localize('dataTypeLabel', 'Data Type');
|
||||
private numberLabel: string = nls.localize('numberLabel', 'Number');
|
||||
private pointLabel: string = nls.localize('pointLabel', 'Point');
|
||||
private labelFirstColumnLabel: string = nls.localize('labelFirstColumnLabel', 'Use First Column as row label?');
|
||||
private columnsAsLabelsLabel: string = nls.localize('columnsAsLabelsLabel', 'Use Column names as labels?');
|
||||
private legendLabel: string = nls.localize('legendLabel', 'Legend Position');
|
||||
private chartNotFoundError: string = nls.localize('chartNotFound', 'Could not find chart to save');
|
||||
/* tslint:enable:no-unused-variable */
|
||||
|
||||
private _actionBar: Taskbar;
|
||||
private _createInsightAction: CreateInsightAction;
|
||||
@@ -78,6 +79,8 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
||||
private _executeResult: IInsightData;
|
||||
private _chartComponent: ChartInsight;
|
||||
|
||||
private localizedStrings = LocalizedStrings;
|
||||
|
||||
@ViewChild(ComponentHostDirective) private componentHost: ComponentHostDirective;
|
||||
@ViewChild('taskbarContainer', { read: ElementRef }) private taskbarContainer;
|
||||
@ViewChild('chartTypesContainer', { read: ElementRef }) private chartTypesElement;
|
||||
@@ -117,15 +120,17 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
||||
// Init label first column checkbox
|
||||
// Note: must use 'self' for callback
|
||||
this.labelFirstColumnCheckBox = new Checkbox(this.labelFirstColumnElement.nativeElement, {
|
||||
label: this.labelFirstColumnLabel,
|
||||
onChange: () => this.onLabelFirstColumnChanged()
|
||||
label: LocalizedStrings.LABEL_FIRST_COLUMN,
|
||||
onChange: () => this.onLabelFirstColumnChanged(),
|
||||
ariaLabel: LocalizedStrings.LABEL_FIRST_COLUMN
|
||||
});
|
||||
|
||||
// Init label first column checkbox
|
||||
// Note: must use 'self' for callback
|
||||
this.columnsAsLabelsCheckBox = new Checkbox(this.columnsAsLabelsElement.nativeElement, {
|
||||
label: this.columnsAsLabelsLabel,
|
||||
onChange: () => this.columnsAsLabelsChanged()
|
||||
label: LocalizedStrings.COLUMNS_AS_LABELS,
|
||||
onChange: () => this.columnsAsLabelsChanged(),
|
||||
ariaLabel: LocalizedStrings.COLUMNS_AS_LABELS
|
||||
});
|
||||
|
||||
// Init legend dropdown
|
||||
@@ -201,7 +206,7 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
||||
public copyChart(): void {
|
||||
let data = this._chartComponent.getCanvasData();
|
||||
if (!data) {
|
||||
this.showError(this.chartNotFoundError);
|
||||
this.showError(LocalizedStrings.CHART_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -212,7 +217,7 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
||||
this.promptForFilepath().then(filePath => {
|
||||
let data = this._chartComponent.getCanvasData();
|
||||
if (!data) {
|
||||
this.showError(this.chartNotFoundError);
|
||||
this.showError(LocalizedStrings.CHART_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
if (filePath) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
-->
|
||||
|
||||
<div class="fullsize vertBox" style="position: relative">
|
||||
<div #resultsPane *ngIf="dataSets.length > 0" id="resultspane" class="boxRow resultsMessageHeader resultsViewCollapsible" [class.collapsed]="!resultActive" (click)="togglePane('results')">
|
||||
<div #resultsPane role="toolbar" tabindex="0" [attr.aria-label]="LocalizedConstants.resultPaneLabel" [attr.aria-expanded]="resultActive" *ngIf="dataSets.length > 0" id="resultspane" class="boxRow resultsMessageHeader resultsViewCollapsible" [class.collapsed]="!resultActive" (click)="togglePane('results')">
|
||||
<span> {{LocalizedConstants.resultPaneLabel}} </span>
|
||||
<span class="queryResultsShortCut"> {{resultShortcut}} </span>
|
||||
</div>
|
||||
@@ -41,12 +41,12 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="messagepane" class="boxRow resultsMessageHeader resultsViewCollapsible" [class.collapsed]="!messageActive && dataSets.length !== 0" (click)="togglePane('messages')" style="position: relative">
|
||||
<div id="messagepane" role="toolbar" tabindex="0" [attr.aria-label]="LocalizedConstants.messagePaneLabel" [attr.aria-expanded]="!(!messageActive && dataSets.length !== 0)" class="boxRow resultsMessageHeader resultsViewCollapsible" [class.collapsed]="!messageActive && dataSets.length !== 0" (click)="togglePane('messages')" style="position: relative">
|
||||
<div id="messageResizeHandle" [class.hidden]="!_resultsPane || !_messageActive || !resultActive" class="resizableHandle"></div>
|
||||
<span> {{LocalizedConstants.messagePaneLabel}} </span>
|
||||
<span class="queryResultsShortCut"> {{messageShortcut}} </span>
|
||||
</div>
|
||||
<div id="messages" class="scrollable messages" [class.hidden]="!messageActive && dataSets.length !== 0"
|
||||
<div #messagesContainer id="messages" class="scrollable messages" [class.hidden]="!messageActive && dataSets.length !== 0"
|
||||
(contextmenu)="openMessagesContextMenu($event)" (focusin)="onMessagesFocus()" (focusout)="onMessagesFocusout()"
|
||||
tabindex=0>
|
||||
<div class="messagesTopSpacing"></div>
|
||||
@@ -58,7 +58,7 @@
|
||||
<ng-template ngFor let-message [ngForOf]="messages">
|
||||
<tr class='messageRow'>
|
||||
<td><span *ngIf="message.link">[{{message.time}}]</span></td>
|
||||
<td class="resultsMessageValue" [class.errorMessage]="message.isError" [class.batchMessage]="!message.link">{{message.message}} <a class="queryLink" *ngIf="message.link" (click)="onSelectionLinkClicked(message.batchId)">{{message.link.text}}</a>
|
||||
<td class="resultsMessageValue" [class.errorMessage]="message.isError" [class.batchMessage]="!message.link">{{message.message}} <a tabindex="0" #queryLink class="queryLink" *ngIf="message.link" (click)="onSelectionLinkClicked(message.batchId)" (keyup)="onKey($event, message.batchId)">{{message.link.text}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
@@ -26,10 +26,12 @@ import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/
|
||||
import { QueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
|
||||
import { error } from 'sql/base/common/log';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { clone } from 'sql/base/common/objects';
|
||||
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { clone } from 'sql/base/common/objects';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
||||
export const QUERY_SELECTOR: string = 'query-component';
|
||||
|
||||
@@ -144,13 +146,15 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
|
||||
public queryExecutionStatus: EventEmitter<string> = new EventEmitter<string>();
|
||||
public queryPlanAvailable: EventEmitter<string> = new EventEmitter<string>();
|
||||
public showChartRequested: EventEmitter<IGridDataSet> = new EventEmitter<IGridDataSet>();
|
||||
public goToNextQueryOutputTabRequested: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
@Input() public queryParameters: QueryComponentParams;
|
||||
|
||||
@ViewChildren('slickgrid') slickgrids: QueryList<SlickGrid>;
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
@ViewChild('resultsPane', { read: ElementRef }) private _resultsPane: ElementRef;
|
||||
|
||||
@ViewChild('queryLink', { read: ElementRef }) private _queryLinkElement: ElementRef;
|
||||
@ViewChild('messagesContainer', { read: ElementRef }) private _messagesContainer: ElementRef;
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef,
|
||||
@@ -404,6 +408,16 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
|
||||
this.dataService.setEditorSelection(index);
|
||||
}
|
||||
|
||||
onKey(e: Event, index: number) {
|
||||
if (DOM.isAncestor(<HTMLElement>e.target, this._queryLinkElement.nativeElement) && e instanceof KeyboardEvent) {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
if (event.equals(KeyCode.Enter)) {
|
||||
this.onSelectionLinkClicked(index);
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the resize for the messages/results panes bar
|
||||
*/
|
||||
@@ -512,7 +526,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
|
||||
*/
|
||||
navigateToGrid(targetIndex: number): boolean {
|
||||
// check if the target index is valid
|
||||
if (targetIndex >= this.renderedDataSets.length || targetIndex < 0 || !this.hasFocus()) {
|
||||
if (targetIndex >= this.renderedDataSets.length || !this.hasFocus()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -565,16 +579,36 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
|
||||
}
|
||||
}
|
||||
|
||||
protected goToNextQueryOutputTab(): void {
|
||||
this.goToNextQueryOutputTabRequested.emit();
|
||||
}
|
||||
|
||||
protected toggleResultPane(): void {
|
||||
this.resultActive = !this.resultActive;
|
||||
this._cd.detectChanges();
|
||||
if (this.resultActive) {
|
||||
this.resizeGrids();
|
||||
this.slickgrids.toArray()[this.activeGrid].setActive();
|
||||
}
|
||||
}
|
||||
|
||||
protected toggleMessagePane(): void {
|
||||
this.messageActive = !this.messageActive;
|
||||
this._cd.detectChanges();
|
||||
if (this.messageActive && this._messagesContainer) {
|
||||
let header = <HTMLElement>this._messagesContainer.nativeElement;
|
||||
header.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to toggle messages and results panes */
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
private togglePane(pane: PaneType): void {
|
||||
if (pane === 'messages') {
|
||||
this._messageActive = !this._messageActive;
|
||||
this.toggleMessagePane();
|
||||
} else if (pane === 'results') {
|
||||
this.resultActive = !this.resultActive;
|
||||
this.toggleResultPane();
|
||||
}
|
||||
this._cd.detectChanges();
|
||||
this.resizeGrids();
|
||||
}
|
||||
|
||||
layout() {
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
|
||||
<panel class="fullsize" [options]="panelOpt">
|
||||
<panel class="dashboard-panel" [options]="panelOpt">
|
||||
<tab [title]="jobsComponentTitle" class="fullsize" [identifier]="jobsTabIdentifier"
|
||||
[iconClass]="jobsIconClass">
|
||||
<div id="jobsDiv" class="fullsize" *ngIf="showHistory === false">
|
||||
<jobsview-component></jobsview-component>
|
||||
</div>
|
||||
<div id="historyDiv" class="fullsize" *ngIf="showHistory === true">
|
||||
<jobhistory-component [jobId]="jobId" [agentJobInfo]="agentJobInfo" [agentJobHistories]="agentJobHistories"></jobhistory-component>
|
||||
<jobhistory-component></jobhistory-component>
|
||||
</div>
|
||||
</tab>
|
||||
</panel>
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!../common/media/jobs';
|
||||
import 'sql/parts/dashboard/common/dashboardPanelStyles';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable } from '@angular/core';
|
||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable} from '@angular/core';
|
||||
import * as Utils from 'sql/parts/connection/common/utils';
|
||||
import { RefreshWidgetAction, EditDashboardAction } from 'sql/parts/dashboard/common/actions';
|
||||
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
@@ -18,6 +20,7 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
|
||||
import { AgentJobInfo, AgentJobHistoryInfo } from 'sqlops';
|
||||
import { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
|
||||
|
||||
|
||||
export const DASHBOARD_SELECTOR: string = 'agentview-component';
|
||||
|
||||
@Component({
|
||||
@@ -31,14 +34,10 @@ export class AgentViewComponent {
|
||||
|
||||
// tslint:disable:no-unused-variable
|
||||
private readonly jobsComponentTitle: string = nls.localize('jobview.Jobs', "Jobs");
|
||||
private readonly alertsComponentTitle: string = nls.localize('jobview.Alerts', "Alerts");
|
||||
private readonly schedulesComponentTitle: string = nls.localize('jobview.Schedules', "Schedules");
|
||||
private readonly operatorsComponentTitle: string = nls.localize('jobview.Operator', "Operators");
|
||||
private readonly jobHistoryComponentTitle: string = nls.localize('jobview.History', "History");
|
||||
private _showHistory: boolean = false;
|
||||
private _jobId: string = null;
|
||||
private _agentJobInfo: AgentJobInfo = null;
|
||||
private _agentJobHistories: AgentJobHistoryInfo[] = null;
|
||||
private _refresh: boolean = undefined;
|
||||
|
||||
public jobsIconClass: string = 'jobsview-icon';
|
||||
|
||||
@@ -68,8 +67,8 @@ export class AgentViewComponent {
|
||||
return this._agentJobInfo;
|
||||
}
|
||||
|
||||
public get agentJobHistories(): AgentJobHistoryInfo[] {
|
||||
return this._agentJobHistories;
|
||||
public get refresh(): boolean {
|
||||
return this._refresh;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,8 +90,8 @@ export class AgentViewComponent {
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
|
||||
public set agentJobHistories(value: AgentJobHistoryInfo[]) {
|
||||
this._agentJobHistories = value;
|
||||
public set refresh(value: boolean) {
|
||||
this._refresh = value;
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as vscode from 'vscode';
|
||||
import { IAgentJobCacheService } from './interfaces';
|
||||
|
||||
@Injectable()
|
||||
export class AgentJobCacheService implements IAgentJobCacheService {
|
||||
_serviceBrand: any;
|
||||
private _jobs: sqlops.AgentJobInfo[];
|
||||
private _jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = {};
|
||||
private _prevJobID: string;
|
||||
|
||||
/* Getters */
|
||||
public get jobs(): sqlops.AgentJobInfo[] {
|
||||
return this._jobs;
|
||||
}
|
||||
|
||||
public get jobHistories(): { [jobId: string]: sqlops.AgentJobHistoryInfo[] } {
|
||||
return this._jobHistories;
|
||||
}
|
||||
|
||||
public get prevJobID(): string {
|
||||
return this._prevJobID;
|
||||
}
|
||||
|
||||
public getJobHistory(jobID: string): sqlops.AgentJobHistoryInfo[] {
|
||||
return this._jobHistories[jobID];
|
||||
}
|
||||
|
||||
/* Setters */
|
||||
public set jobs(value: sqlops.AgentJobInfo[]) {
|
||||
this._jobs = value;
|
||||
}
|
||||
|
||||
public set jobHistories(value: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; }) {
|
||||
this._jobHistories = value;
|
||||
}
|
||||
|
||||
public set prevJobID(value: string) {
|
||||
this._prevJobID = value;
|
||||
}
|
||||
|
||||
public setJobHistory(jobID:string, value: sqlops.AgentJobHistoryInfo[]) {
|
||||
this._jobHistories[jobID] = value;
|
||||
}
|
||||
|
||||
}
|
||||
54
src/sql/parts/jobManagement/common/agentJobUtilities.ts
Normal file
54
src/sql/parts/jobManagement/common/agentJobUtilities.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
export class AgentJobUtilities {
|
||||
|
||||
public static convertToStatusString(status: number): string {
|
||||
switch(status) {
|
||||
case(0): return nls.localize('agentUtilities.failed','Failed');
|
||||
case(1): return nls.localize('agentUtilities.succeeded', 'Succeeded');
|
||||
case(3): return nls.localize('agentUtilities.canceled', 'Canceled');
|
||||
case(5): return nls.localize('agentUtilities.statusUnknown', 'Status Unknown');
|
||||
default: return nls.localize('agentUtilities.statusUnknown', 'Status Unknown');
|
||||
}
|
||||
}
|
||||
|
||||
public static convertToExecutionStatusString(status: number): string {
|
||||
switch(status) {
|
||||
case(1): return nls.localize('agentUtilities.executing', 'Executing');
|
||||
case(2): return nls.localize('agentUtilities.waitingForThread', 'Waiting for Thread');
|
||||
case(3): return nls.localize('agentUtilities.betweenRetries', 'Between Retries');
|
||||
case(4): return nls.localize('agentUtilities.idle', 'Idle');
|
||||
case(5): return nls.localize('agentUtilities.suspended', 'Suspended');
|
||||
case(6): return nls.localize('agentUtilities.obsolete', '[Obsolete]');
|
||||
case(7): return nls.localize('agentUtilities.performingCompletionActions', 'PerformingCompletionActions');
|
||||
default: return nls.localize('agentUtilities.statusUnknown', 'Status Unknown');
|
||||
}
|
||||
}
|
||||
|
||||
public static convertToResponse(bool: boolean) {
|
||||
return bool ? nls.localize('agentUtilities.yes', 'Yes') : nls.localize('agentUtilities.no', 'No');
|
||||
}
|
||||
|
||||
public static convertToNextRun(date: string) {
|
||||
if (date.includes('1/1/0001')) {
|
||||
return nls.localize('agentUtilities.notScheduled', 'Not Scheduled');
|
||||
} else {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
public static convertToLastRun(date: string) {
|
||||
if (date.includes('1/1/0001')) {
|
||||
return nls.localize('agentUtilities.neverRun', 'Never Run');
|
||||
} else {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,11 @@
|
||||
import * as sqlops from 'sqlops';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { JobCacheObject } from './jobManagementService';
|
||||
|
||||
export const SERVICE_ID = 'jobManagementService';
|
||||
export const CACHE_ID = 'jobCacheService';
|
||||
|
||||
export const IJobManagementService = createDecorator<IJobManagementService>(SERVICE_ID);
|
||||
export const IAgentJobCacheService = createDecorator<IAgentJobCacheService>(CACHE_ID);
|
||||
|
||||
export interface IJobManagementService {
|
||||
_serviceBrand: any;
|
||||
@@ -25,18 +24,8 @@ export interface IJobManagementService {
|
||||
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult>;
|
||||
|
||||
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult>;
|
||||
}
|
||||
|
||||
export interface IAgentJobCacheService {
|
||||
_serviceBrand: any;
|
||||
addToCache(server: string, cache: JobCacheObject);
|
||||
|
||||
jobs: sqlops.AgentJobInfo[];
|
||||
|
||||
jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; };
|
||||
|
||||
prevJobID: string;
|
||||
|
||||
getJobHistory(jobID: string): sqlops.AgentJobHistoryInfo[];
|
||||
|
||||
setJobHistory(jobID: string, value: sqlops.AgentJobHistoryInfo[]);
|
||||
jobCacheObjectMap: { [server: string]: JobCacheObject; };
|
||||
}
|
||||
@@ -6,17 +6,20 @@
|
||||
'use strict';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as vscode from 'vscode';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
|
||||
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
export class JobManagementService implements IJobManagementService {
|
||||
_serviceBrand: any;
|
||||
|
||||
private _providers: { [handle: string]: sqlops.AgentServicesProvider; } = Object.create(null);
|
||||
private _jobCacheObject : {[server: string]: JobCacheObject; } = {};
|
||||
|
||||
constructor(
|
||||
@IConnectionManagementService private _connectionService: IConnectionManagementService,
|
||||
@@ -60,4 +63,66 @@ export class JobManagementService implements IJobManagementService {
|
||||
public registerProvider(providerId: string, provider: sqlops.AgentServicesProvider): void {
|
||||
this._providers[providerId] = provider;
|
||||
}
|
||||
|
||||
public get jobCacheObjectMap(): {[server: string]: JobCacheObject;} {
|
||||
return this._jobCacheObject;
|
||||
}
|
||||
|
||||
public addToCache(server: string, cacheObject: JobCacheObject) {
|
||||
this._jobCacheObject[server] = cacheObject;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Server level caching of jobs/job histories
|
||||
*/
|
||||
export class JobCacheObject {
|
||||
_serviceBrand: any;
|
||||
private _jobs: sqlops.AgentJobInfo[] = [];
|
||||
private _jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = {};
|
||||
private _prevJobID: string;
|
||||
private _serverName: string;
|
||||
|
||||
/* Getters */
|
||||
public get jobs(): sqlops.AgentJobInfo[] {
|
||||
return this._jobs;
|
||||
}
|
||||
|
||||
public get jobHistories(): { [jobId: string]: sqlops.AgentJobHistoryInfo[] } {
|
||||
return this._jobHistories;
|
||||
}
|
||||
|
||||
public get prevJobID(): string {
|
||||
return this._prevJobID;
|
||||
}
|
||||
|
||||
public getJobHistory(jobID: string): sqlops.AgentJobHistoryInfo[] {
|
||||
return this._jobHistories[jobID];
|
||||
}
|
||||
|
||||
public get serverName(): string {
|
||||
return this._serverName;
|
||||
}
|
||||
|
||||
/* Setters */
|
||||
public set jobs(value: sqlops.AgentJobInfo[]) {
|
||||
this._jobs = value;
|
||||
}
|
||||
|
||||
public set jobHistories(value: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; }) {
|
||||
this._jobHistories = value;
|
||||
}
|
||||
|
||||
public set prevJobID(value: string) {
|
||||
this._prevJobID = value;
|
||||
}
|
||||
|
||||
public setJobHistory(jobID:string, value: sqlops.AgentJobHistoryInfo[]) {
|
||||
this._jobHistories[jobID] = value;
|
||||
}
|
||||
|
||||
public set serverName(value: string) {
|
||||
this._serverName = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1
src/sql/parts/jobManagement/common/media/failed.svg
Normal file
1
src/sql/parts/jobManagement/common/media/failed.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><defs><style>.cls-1{fill:none;clip-rule:evenodd;}.cls-2{clip-path:url(#clip-path);}.cls-3{fill:#d02e00;}</style><clipPath id="clip-path"><path class="cls-1" d="M8.88,8l2.67-2.67-.88-.88L8,7.12,5.33,4.45l-.88.88L7.12,8,4.45,10.67l.88.88L8,8.88l2.67,2.67.88-.88ZM8,0a7.92,7.92,0,0,1,4,1.09A8.15,8.15,0,0,1,14.91,4a8,8,0,0,1,0,8.07A8.15,8.15,0,0,1,12,14.91a8,8,0,0,1-8.07,0A8.15,8.15,0,0,1,1.09,12,8,8,0,0,1,1.09,4,8.15,8.15,0,0,1,4,1.09,7.92,7.92,0,0,1,8,0Z"/></clipPath></defs><title>failed</title><g class="cls-2"><rect class="cls-3" x="-5" y="-5" width="26" height="26"/></g></svg>
|
||||
|
After Width: | Height: | Size: 718 B |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user