mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-20 11:01:37 -05:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db817a7192 | ||
|
|
2c8e93cc96 | ||
|
|
44e9a97f09 | ||
|
|
298ddc4195 | ||
|
|
d9134d6085 | ||
|
|
b17b4ce880 | ||
|
|
2304c32453 | ||
|
|
2b68e4a7df | ||
|
|
8f06e72318 | ||
|
|
5fa740ead4 | ||
|
|
e5096e61e5 | ||
|
|
2a3195636e | ||
|
|
5fb9b8ccd3 | ||
|
|
7089e2299a | ||
|
|
b553cbb68c | ||
|
|
c712411e77 | ||
|
|
48d5cc554c | ||
|
|
39bfd69dc9 | ||
|
|
d690b80493 | ||
|
|
eb48a9f993 | ||
|
|
5a54abaf44 | ||
|
|
47c161f9f1 | ||
|
|
807f8e68f3 | ||
|
|
fba8536c33 | ||
|
|
36fc1bb71a | ||
|
|
ccaa96c81e | ||
|
|
ac2f279c88 | ||
|
|
91cb99b8c8 | ||
|
|
02b1a525e3 | ||
|
|
9bf4a4b18c | ||
|
|
8a01553c49 | ||
|
|
21b913845f | ||
|
|
220e4feb1d | ||
|
|
13884c0457 | ||
|
|
b3bb6ebc6e | ||
|
|
424eb90dd8 | ||
|
|
df804d0729 | ||
|
|
79269cdfd5 | ||
|
|
2a650d4d74 | ||
|
|
017b4ecdb3 | ||
|
|
9c84bf3fd5 | ||
|
|
397b54a8c3 | ||
|
|
cb3604c0a1 | ||
|
|
1a7f0673ea | ||
|
|
0d043207b9 | ||
|
|
f995dea971 | ||
|
|
49e20488bc | ||
|
|
3e47b27192 | ||
|
|
f4fa18ec05 | ||
|
|
c6d1fa2b7d | ||
|
|
c4df7667ff | ||
|
|
6a303cfa25 | ||
|
|
4ffa5cc1da | ||
|
|
172e1cf3bf | ||
|
|
e6a32e52f5 |
@@ -78,6 +78,7 @@ const sqlBuiltInExtensions = [
|
||||
// Add SQL built-in extensions here.
|
||||
// the extension will be excluded from SQLOps package and will have separate vsix packages
|
||||
'agent',
|
||||
'import',
|
||||
'profiler'
|
||||
];
|
||||
|
||||
@@ -273,7 +274,8 @@ function packageBuiltInExtensions() {
|
||||
console.info('Creating vsix for ' + element.path + ' result:' + packagePath);
|
||||
vsce.createVSIX({
|
||||
cwd: element.path,
|
||||
packagePath: packagePath
|
||||
packagePath: packagePath,
|
||||
useYarn: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ Type: files; Name: "{app}\resources\app\Credits_45.0.2454.85.html"; Check: IsNot
|
||||
Type: filesandordirs; Name: "{app}\_"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
|
||||
Name: "associatewithfiles"; Description: "{cm:AssociateWithFiles,{#NameShort}}"; GroupDescription: "{cm:Other}"; Flags: unchecked
|
||||
Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "agent",
|
||||
"displayName": "SQL Server Agent",
|
||||
"description": "Manage and troubleshoot SQL Server Agent jobs",
|
||||
"version": "0.31.4",
|
||||
"version": "0.32.4",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
||||
|
||||
1
extensions/import/.gitignore
vendored
Normal file
1
extensions/import/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
flatfileimportservice/
|
||||
2
extensions/import/.vscodeignore
Normal file
2
extensions/import/.vscodeignore
Normal file
@@ -0,0 +1,2 @@
|
||||
client/src/**
|
||||
client/tsconfig.json
|
||||
25
extensions/import/README.md
Normal file
25
extensions/import/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Microsoft SQL Server Import for SQL Operations Studio
|
||||
|
||||
|
||||
Import Flat File Wizard is a simple way to copy data from a flat file (.csv, .txt) to a destination. This overview describes the reasons for using this wizard, how to find this wizard, and a simple example to follow.
|
||||
|
||||

|
||||
|
||||
## Why would I use this wizard?
|
||||
This wizard was created to improve the current import experience leveraging an intelligent framework known as Program Synthesis using Examples ([PROSE](https://microsoft.github.io/prose/)). For a user without specialized domain knowledge, importing data can often be a complex, error prone, and tedious task. This wizard streamlines the import process as simple as selecting an input file and unique table name, and the PROSE framework handles the rest.
|
||||
|
||||
PROSE analyzes data patterns in your input file to infer column names, types, delimiters, and more. This framework learns the structure of the file and does all of the hard work so users don't have to.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement) describes the privacy statement of this software.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt).
|
||||
1
extensions/import/images/dark_icon.svg
Normal file
1
extensions/import/images/dark_icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#231f20;}.cls-3{fill:#0095d7;}</style></defs><title>importflatfile_inverse</title><path class="cls-1" d="M13.34,1.57c-2.81,0-7.52.53-7.65,2.49v3.2L7,8.54V5.66a17.11,17.11,0,0,0,6.37,1,17.1,17.1,0,0,0,6.38-1V17.58c-.17.46-2.55,1.35-6.38,1.35a19.63,19.63,0,0,1-3.43-.27V20a23.78,23.78,0,0,0,3.43.25c2.86,0,7.66-.57,7.66-2.64V4.06C20.87,2.1,16.16,1.57,13.34,1.57Zm6.38,2.55c-.2.45-2.56,1.28-6.38,1.28S7.24,4.6,7,4.14c.27-.47,2.6-1.29,6.37-1.29s6.16.85,6.38,1.25h0Z"/><polygon class="cls-2" points="18.55 3.06 18.53 3.07 18.53 3.04 18.55 3.06"/><path class="cls-1" d="M7,10,5.69,8.68,5,8H0V19.85H8.91v-8ZM5.2,9.24l.49.49L7,11l.67.67H5.2Zm3,9.86H.74V8.71H4.46v3.71H8.17Z"/><path class="cls-3" d="M16.5,15a.27.27,0,0,1-.08.2L14.2,17.4a.26.26,0,0,1-.19.08.28.28,0,0,1-.2-.08.26.26,0,0,1-.08-.2.82.82,0,0,1,0-.14l.06-.25.08-.32.08-.32.07-.27,0-.17H4.5v-1.5h9.59l0-.17L14,13.79l-.08-.32-.08-.31-.06-.25a.91.91,0,0,1,0-.14.26.26,0,0,1,.08-.2.28.28,0,0,1,.2-.08.26.26,0,0,1,.19.08l2.22,2.22A.26.26,0,0,1,16.5,15Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
extensions/import/images/light_icon.svg
Normal file
1
extensions/import/images/light_icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21"><defs><style>.cls-1{fill:#212121;}.cls-2{fill:#231f20;}.cls-3{fill:#00539c;}</style></defs><title>importflatfile</title><path class="cls-1" d="M13.34,1.3c-2.81,0-7.52.53-7.65,2.49V7L7,8.27V5.39a17.11,17.11,0,0,0,6.37,1,17.1,17.1,0,0,0,6.38-1V17.31c-.17.46-2.55,1.35-6.38,1.35a19.63,19.63,0,0,1-3.43-.27V19.7a23.78,23.78,0,0,0,3.43.25C16.2,20,21,19.38,21,17.31V3.79C20.87,1.83,16.16,1.3,13.34,1.3Zm6.38,2.55c-.2.45-2.56,1.28-6.38,1.28S7.24,4.33,7,3.87c.27-.47,2.6-1.29,6.37-1.29s6.16.85,6.38,1.25h0Z"/><polygon class="cls-2" points="18.55 2.79 18.53 2.81 18.53 2.78 18.55 2.79"/><path class="cls-1" d="M7,9.69,5.69,8.41,5,7.7H0V19.58H8.91v-8ZM5.2,9l.49.49L7,10.74l.67.67H5.2Zm3,9.86H.74V8.44H4.46v3.71H8.17Z"/><path class="cls-3" d="M16.5,14.72a.27.27,0,0,1-.08.2L14.2,17.14a.26.26,0,0,1-.19.08.28.28,0,0,1-.2-.08.26.26,0,0,1-.08-.2.82.82,0,0,1,0-.14l.06-.25.08-.32.08-.32.07-.27,0-.17H4.5V14h9.59l0-.17L14,13.53l-.08-.32-.08-.31-.06-.25a.91.91,0,0,1,0-.14.26.26,0,0,1,.08-.2.28.28,0,0,1,.2-.08.26.26,0,0,1,.19.08l2.22,2.22A.26.26,0,0,1,16.5,14.72Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
extensions/import/images/sqlserver.png
Normal file
BIN
extensions/import/images/sqlserver.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
91
extensions/import/package.json
Normal file
91
extensions/import/package.json
Normal file
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"name": "import",
|
||||
"displayName": "SQL Server Import",
|
||||
"description": "Imports data from a flat file.",
|
||||
"version": "0.0.1",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"engines": {
|
||||
"vscode": "^1.25.0",
|
||||
"sqlops": "*"
|
||||
},
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
||||
"icon": "images/sqlserver.png",
|
||||
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
|
||||
"activationEvents": [
|
||||
"*"
|
||||
],
|
||||
"main": "./out/main",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/sqlopsstudio.git"
|
||||
},
|
||||
"extensionDependencies": [
|
||||
"Microsoft.mssql"
|
||||
],
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "flatFileImport.start",
|
||||
"title": "Import wizard",
|
||||
"category": "Flat File Import",
|
||||
"icon": {
|
||||
"light": "./images/light_icon.svg",
|
||||
"dark": "./images/dark_icon.svg"
|
||||
}
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
{
|
||||
"command": "flatFileImport.start",
|
||||
"key": "ctrl+i",
|
||||
"mac": "ctrl+i"
|
||||
}
|
||||
],
|
||||
"dashboard.tabs": [
|
||||
{
|
||||
"id": "flat-file-import",
|
||||
"title": "Flat File Import",
|
||||
"description": "The flat file importer.",
|
||||
"container": {
|
||||
"flat-file-import-container": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"dashboard.containers": [
|
||||
{
|
||||
"id": "flat-file-import-container",
|
||||
"container": {
|
||||
"widgets-container": [
|
||||
{
|
||||
"name": "Tasks",
|
||||
"widget": {
|
||||
"tasks-widget": [
|
||||
"flatFileImport.start"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"objectExplorer/item/context": [
|
||||
{
|
||||
"command": "flatFileImport.start",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database",
|
||||
"group": "import"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.5",
|
||||
"opener": "^1.4.3",
|
||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
|
||||
"vscode-extension-telemetry": "^0.0.5",
|
||||
"vscode-nls": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
}
|
||||
}
|
||||
14
extensions/import/src/constants.ts
Normal file
14
extensions/import/src/constants.ts
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';
|
||||
|
||||
export const extensionConfigSectionName = 'flatFileImport';
|
||||
export const serviceName = 'Flat File Import Service';
|
||||
export const providerId = 'FlatFileImport';
|
||||
export const configLogDebugInfo = 'logDebugInfo';
|
||||
export const sqlConfigSectionName = 'sql';
|
||||
|
||||
export const serviceCrashLink = 'https://github.com/Microsoft/sqlopsstudio/issues/2090';
|
||||
|
||||
30
extensions/import/src/controllers/controllerBase.ts
Normal file
30
extensions/import/src/controllers/controllerBase.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export default abstract class ControllerBase implements vscode.Disposable {
|
||||
protected _context: vscode.ExtensionContext;
|
||||
|
||||
protected constructor(context: vscode.ExtensionContext) {
|
||||
this._context = context;
|
||||
}
|
||||
|
||||
public get extensionContext(): vscode.ExtensionContext {
|
||||
return this._context;
|
||||
}
|
||||
|
||||
abstract activate(): Promise<boolean>;
|
||||
|
||||
abstract deactivate(): void;
|
||||
|
||||
public dispose(): void {
|
||||
this.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
44
extensions/import/src/controllers/mainController.ts
Normal file
44
extensions/import/src/controllers/mainController.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 constants from '../constants';
|
||||
import * as sqlops from 'sqlops';
|
||||
import ControllerBase from './controllerBase';
|
||||
import * as vscode from 'vscode';
|
||||
import { FlatFileWizard } from '../wizard/flatFileWizard';
|
||||
import { ServiceClient } from '../services/serviceClient';
|
||||
import { ApiType, managerInstance } from '../services/serviceApiManager';
|
||||
import { FlatFileProvider } from '../services/contracts';
|
||||
|
||||
/**
|
||||
* The main controller class that initializes the extension
|
||||
*/
|
||||
export default class MainController extends ControllerBase {
|
||||
|
||||
public constructor(context: vscode.ExtensionContext) {
|
||||
super(context);
|
||||
}
|
||||
/**
|
||||
*/
|
||||
public deactivate(): void {
|
||||
}
|
||||
|
||||
public activate(): Promise<boolean> {
|
||||
const outputChannel = vscode.window.createOutputChannel(constants.serviceName);
|
||||
new ServiceClient(outputChannel).startService(this._context);
|
||||
|
||||
managerInstance.onRegisteredApi<FlatFileProvider>(ApiType.FlatFileProvider)(provider => {
|
||||
this.initializeFlatFileProvider(provider);
|
||||
});
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
private initializeFlatFileProvider(provider: FlatFileProvider) {
|
||||
sqlops.tasks.registerTask('flatFileImport.start', (profile: sqlops.IConnectionProfile, ...args: any[]) => new FlatFileWizard(provider).start(profile, args));
|
||||
}
|
||||
}
|
||||
37
extensions/import/src/main.ts
Normal file
37
extensions/import/src/main.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 ControllerBase from './controllers/controllerBase';
|
||||
import MainController from './controllers/mainController';
|
||||
|
||||
let controllers: ControllerBase[] = [];
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
let activations: Promise<boolean>[] = [];
|
||||
|
||||
// Start the main controller
|
||||
let mainController = new MainController(context);
|
||||
controllers.push(mainController);
|
||||
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() {
|
||||
for (let controller of controllers) {
|
||||
controller.deactivate();
|
||||
}
|
||||
}
|
||||
16
extensions/import/src/services/config.json
Normal file
16
extensions/import/src/services/config.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"downloadUrl": "https://sqlopsextensions.blob.core.windows.net/extensions/import/{#fileName#}",
|
||||
"useDefaultLinuxRuntime": true,
|
||||
"version": "0.0.1",
|
||||
"downloadFileNames": {
|
||||
"Windows_64": "win-x64.zip",
|
||||
"Windows_86": "win-x86.zip",
|
||||
"OSX": "osx.tar.gz",
|
||||
"Linux_64": "linux-x64.tar.gz"
|
||||
},
|
||||
"installDirectory": "flatfileimportservice/{#platform#}/{#version#}",
|
||||
"executableFiles": [
|
||||
"MicrosoftSqlToolsFlatFileImport",
|
||||
"MicrosoftSqlToolsFlatFileImport.exe"
|
||||
]
|
||||
}
|
||||
149
extensions/import/src/services/contracts.ts
Normal file
149
extensions/import/src/services/contracts.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { RequestType, NotificationType } from 'vscode-languageclient';
|
||||
|
||||
/**
|
||||
* @interface IMessage
|
||||
*/
|
||||
export interface IMessage {
|
||||
jsonrpc: string;
|
||||
}
|
||||
|
||||
// ------------------------------- < Telemetry Sent Event > ------------------------------------
|
||||
|
||||
/**
|
||||
* Event sent when the language service send a telemetry event
|
||||
*/
|
||||
export namespace TelemetryNotification {
|
||||
export const type = new NotificationType<TelemetryParams, void>('telemetry/sqlevent');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update event parameters
|
||||
*/
|
||||
export class TelemetryParams {
|
||||
public params: {
|
||||
eventName: string;
|
||||
properties: ITelemetryEventProperties;
|
||||
measures: ITelemetryEventMeasures;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ITelemetryEventProperties {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface ITelemetryEventMeasures {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contract Classes
|
||||
*/
|
||||
export interface Result {
|
||||
success: boolean;
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
export interface ColumnInfo {
|
||||
name: string;
|
||||
sqlType: string;
|
||||
isNullable: boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* PROSEDiscoveryRequest
|
||||
* Send this request to create a new PROSE session with a new file and preview it
|
||||
*/
|
||||
const proseDiscoveryRequestName = 'flatfile/proseDiscovery';
|
||||
|
||||
export interface PROSEDiscoveryParams {
|
||||
filePath: string;
|
||||
tableName: string;
|
||||
schemaName?: string;
|
||||
fileType?: string;
|
||||
}
|
||||
|
||||
export interface PROSEDiscoveryResponse {
|
||||
dataPreview: string[][];
|
||||
columnInfo: ColumnInfo[];
|
||||
}
|
||||
|
||||
/**
|
||||
* InsertDataRequest
|
||||
*/
|
||||
const insertDataRequestName = 'flatfile/insertData';
|
||||
|
||||
export interface InsertDataParams {
|
||||
connectionString: string;
|
||||
batchSize: number;
|
||||
}
|
||||
|
||||
export interface InsertDataResponse {
|
||||
result: Result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* GetColumnInfoRequest
|
||||
*/
|
||||
const getColumnInfoRequestName = 'flatfile/getColumnInfo';
|
||||
|
||||
export interface GetColumnInfoParams {
|
||||
}
|
||||
|
||||
export interface GetColumnInfoResponse {
|
||||
columnInfo: ColumnInfo[];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ChangeColumnSettingsRequest
|
||||
*/
|
||||
const changeColumnSettingsRequestName = 'flatfile/changeColumnSettings';
|
||||
|
||||
export interface ChangeColumnSettingsParams {
|
||||
index: number;
|
||||
newName?: string;
|
||||
newDataType?: string;
|
||||
newNullable?: boolean;
|
||||
newInPrimaryKey?: boolean;
|
||||
}
|
||||
|
||||
export interface ChangeColumnSettingsResponse {
|
||||
result: Result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests
|
||||
*/
|
||||
export namespace PROSEDiscoveryRequest {
|
||||
export const type = new RequestType<PROSEDiscoveryParams, PROSEDiscoveryResponse, void, void>(proseDiscoveryRequestName);
|
||||
}
|
||||
|
||||
export namespace InsertDataRequest {
|
||||
export const type = new RequestType<InsertDataParams, InsertDataResponse, void, void>(insertDataRequestName);
|
||||
}
|
||||
|
||||
export namespace GetColumnInfoRequest {
|
||||
export const type = new RequestType<GetColumnInfoParams, GetColumnInfoResponse, void, void>(getColumnInfoRequestName);
|
||||
}
|
||||
|
||||
export namespace ChangeColumnSettingsRequest {
|
||||
export const type = new RequestType<ChangeColumnSettingsParams, ChangeColumnSettingsResponse, void, void>(changeColumnSettingsRequestName);
|
||||
}
|
||||
|
||||
|
||||
export interface FlatFileProvider {
|
||||
providerId?: string;
|
||||
|
||||
sendPROSEDiscoveryRequest(params: PROSEDiscoveryParams): Thenable<PROSEDiscoveryResponse>;
|
||||
sendInsertDataRequest(params: InsertDataParams): Thenable<InsertDataResponse>;
|
||||
sendGetColumnInfoRequest(params: GetColumnInfoParams): Thenable<GetColumnInfoResponse>;
|
||||
sendChangeColumnSettingsRequest(params: ChangeColumnSettingsParams): Thenable<ChangeColumnSettingsResponse>;
|
||||
}
|
||||
96
extensions/import/src/services/features.ts
Normal file
96
extensions/import/src/services/features.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SqlOpsDataClient, SqlOpsFeature } from 'dataprotocol-client';
|
||||
import {
|
||||
ClientCapabilities,
|
||||
StaticFeature,
|
||||
RPCMessageType,
|
||||
ServerCapabilities
|
||||
} from 'vscode-languageclient';
|
||||
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
||||
import { Disposable } from 'vscode';
|
||||
|
||||
import { Telemetry } from './telemetry';
|
||||
import * as serviceUtils from './serviceUtils';
|
||||
import * as Contracts from './contracts';
|
||||
import { managerInstance, ApiType } from './serviceApiManager';
|
||||
|
||||
export class TelemetryFeature implements StaticFeature {
|
||||
|
||||
constructor(private _client: SqlOpsDataClient) {
|
||||
}
|
||||
|
||||
fillClientCapabilities(capabilities: ClientCapabilities): void {
|
||||
serviceUtils.ensure(capabilities, 'telemetry')!.telemetry = true;
|
||||
}
|
||||
|
||||
initialize(): void {
|
||||
this._client.onNotification(Contracts.TelemetryNotification.type, e => {
|
||||
Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FlatFileImportFeature extends SqlOpsFeature<undefined> {
|
||||
private static readonly messagesTypes: RPCMessageType[] = [
|
||||
Contracts.PROSEDiscoveryRequest.type
|
||||
];
|
||||
|
||||
constructor(client: SqlOpsDataClient) {
|
||||
super(client, FlatFileImportFeature.messagesTypes);
|
||||
}
|
||||
|
||||
public fillClientCapabilities(capabilities: ClientCapabilities): void {
|
||||
}
|
||||
|
||||
public initialize(capabilities: ServerCapabilities): void {
|
||||
this.register(this.messages, {
|
||||
id: UUID.generateUuid(),
|
||||
registerOptions: undefined
|
||||
});
|
||||
}
|
||||
|
||||
protected registerProvider(options: undefined): Disposable {
|
||||
const client = this._client;
|
||||
|
||||
let requestSender = (requestType, params) => {
|
||||
return client.sendRequest(requestType, params).then(
|
||||
r => {
|
||||
return r as any;
|
||||
},
|
||||
e => {
|
||||
client.logFailedRequest(requestType, e);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
let sendPROSEDiscoveryRequest = (params: Contracts.PROSEDiscoveryParams): Thenable<Contracts.PROSEDiscoveryResponse> => {
|
||||
return requestSender(Contracts.PROSEDiscoveryRequest.type, params);
|
||||
};
|
||||
|
||||
let sendInsertDataRequest = (params: Contracts.InsertDataParams): Thenable<Contracts.InsertDataResponse> => {
|
||||
return requestSender(Contracts.InsertDataRequest.type, params);
|
||||
};
|
||||
|
||||
let sendGetColumnInfoRequest = (params: Contracts.GetColumnInfoParams): Thenable<Contracts.GetColumnInfoResponse> => {
|
||||
return requestSender(Contracts.GetColumnInfoRequest.type, params);
|
||||
};
|
||||
|
||||
let sendChangeColumnSettingsRequest = (params: Contracts.ChangeColumnSettingsParams): Thenable<Contracts.ChangeColumnSettingsResponse> => {
|
||||
return requestSender(Contracts.ChangeColumnSettingsRequest.type, params);
|
||||
};
|
||||
|
||||
return managerInstance.registerApi<Contracts.FlatFileProvider>(ApiType.FlatFileProvider, {
|
||||
providerId: client.providerId,
|
||||
sendPROSEDiscoveryRequest,
|
||||
sendChangeColumnSettingsRequest,
|
||||
sendGetColumnInfoRequest,
|
||||
sendInsertDataRequest
|
||||
});
|
||||
}
|
||||
}
|
||||
64
extensions/import/src/services/serviceApiManager.ts
Normal file
64
extensions/import/src/services/serviceApiManager.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as vscode from 'vscode';
|
||||
import * as contracts from './contracts';
|
||||
import { SqlOpsDataClient } from 'dataprotocol-client/lib/main';
|
||||
|
||||
export enum ApiType {
|
||||
FlatFileProvider = 'FlatFileProvider'
|
||||
}
|
||||
|
||||
export interface IServiceApi {
|
||||
onRegisteredApi<T>(type: ApiType): vscode.Event<T>;
|
||||
registerApi<T>(type: ApiType, feature: T): vscode.Disposable;
|
||||
}
|
||||
|
||||
export interface IModelViewDefinition {
|
||||
id: string;
|
||||
modelView: sqlops.ModelView;
|
||||
}
|
||||
|
||||
export class ServiceApiManager implements IServiceApi {
|
||||
private modelViewRegistrations: { [id: string]: boolean } = {};
|
||||
private featureEventChannels: { [type: string]: vscode.EventEmitter<any> } = {};
|
||||
private _onRegisteredModelView = new vscode.EventEmitter<IModelViewDefinition>();
|
||||
|
||||
public onRegisteredApi<T>(type: ApiType): vscode.Event<T> {
|
||||
let featureEmitter = this.featureEventChannels[type];
|
||||
if (!featureEmitter) {
|
||||
featureEmitter = new vscode.EventEmitter<T>();
|
||||
this.featureEventChannels[type] = featureEmitter;
|
||||
}
|
||||
return featureEmitter.event;
|
||||
}
|
||||
|
||||
public registerApi<T>(type: ApiType, feature: T): vscode.Disposable {
|
||||
let featureEmitter = this.featureEventChannels[type];
|
||||
if (featureEmitter) {
|
||||
featureEmitter.fire(feature);
|
||||
}
|
||||
// TODO handle unregistering API on close
|
||||
return {
|
||||
dispose: () => undefined
|
||||
};
|
||||
}
|
||||
|
||||
public get onRegisteredModelView(): vscode.Event<IModelViewDefinition> {
|
||||
return this._onRegisteredModelView.event;
|
||||
}
|
||||
|
||||
public registerModelView(id: string, modelView: sqlops.ModelView): void {
|
||||
this._onRegisteredModelView.fire({
|
||||
id: id,
|
||||
modelView: modelView
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export let managerInstance = new ServiceApiManager();
|
||||
165
extensions/import/src/services/serviceClient.ts
Normal file
165
extensions/import/src/services/serviceClient.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client';
|
||||
import { IConfig, ServerProvider, Events } from 'service-downloader';
|
||||
import { ServerOptions, TransportKind } from 'vscode-languageclient';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
import * as path from 'path';
|
||||
import { EventAndListener } from 'eventemitter2';
|
||||
|
||||
import { Telemetry, LanguageClientErrorHandler } from './telemetry';
|
||||
import * as Constants from '../constants';
|
||||
import { TelemetryFeature, FlatFileImportFeature } from './features';
|
||||
import * as serviceUtils from './serviceUtils';
|
||||
|
||||
const baseConfig = require('./config.json');
|
||||
|
||||
export class ServiceClient {
|
||||
private statusView: vscode.StatusBarItem;
|
||||
|
||||
constructor(private outputChannel: vscode.OutputChannel) {
|
||||
this.statusView = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
|
||||
}
|
||||
|
||||
public startService(context: vscode.ExtensionContext): Promise<SqlOpsDataClient> {
|
||||
let config: IConfig = JSON.parse(JSON.stringify(baseConfig));
|
||||
config.installDirectory = path.join(context.extensionPath, config.installDirectory);
|
||||
config.proxy = vscode.workspace.getConfiguration('http').get('proxy');
|
||||
config.strictSSL = vscode.workspace.getConfiguration('http').get('proxyStrictSSL') || true;
|
||||
|
||||
const serverdownloader = new ServerProvider(config);
|
||||
serverdownloader.eventEmitter.onAny(this.generateHandleServerProviderEvent());
|
||||
|
||||
let clientOptions: ClientOptions = this.createClientOptions();
|
||||
|
||||
const installationStart = Date.now();
|
||||
let client: SqlOpsDataClient;
|
||||
return new Promise((resolve, reject) => {
|
||||
serverdownloader.getOrDownloadServer().then(e => {
|
||||
const installationComplete = Date.now();
|
||||
let serverOptions = this.generateServerOptions(e);
|
||||
client = new SqlOpsDataClient(Constants.serviceName, serverOptions, clientOptions);
|
||||
const processStart = Date.now();
|
||||
client.onReady().then(() => {
|
||||
const processEnd = Date.now();
|
||||
this.statusView.text = localize('serviceStarted', 'Service Started');
|
||||
setTimeout(() => {
|
||||
this.statusView.hide();
|
||||
}, 1500);
|
||||
Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', {
|
||||
installationTime: String(installationComplete - installationStart),
|
||||
processStartupTime: String(processEnd - processStart),
|
||||
totalTime: String(processEnd - installationStart),
|
||||
beginningTimestamp: String(installationStart)
|
||||
});
|
||||
});
|
||||
this.statusView.show();
|
||||
this.statusView.text = localize('serviceStarting', 'Starting service');
|
||||
let disposable = client.start();
|
||||
context.subscriptions.push(disposable);
|
||||
resolve(client);
|
||||
}, e => {
|
||||
Telemetry.sendTelemetryEvent('ServiceInitializingFailed');
|
||||
vscode.window.showErrorMessage(localize('flatFileImport.serviceStartFailed', 'Failed to start Import service{0}', e));
|
||||
// Just resolve to avoid unhandled promise. We show the error to the user.
|
||||
resolve(undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private createClientOptions(): ClientOptions {
|
||||
return {
|
||||
providerId: Constants.providerId,
|
||||
errorHandler: new LanguageClientErrorHandler(),
|
||||
synchronize: {
|
||||
configurationSection: [Constants.extensionConfigSectionName, Constants.sqlConfigSectionName]
|
||||
},
|
||||
features: [
|
||||
// we only want to add new features
|
||||
TelemetryFeature,
|
||||
FlatFileImportFeature
|
||||
],
|
||||
outputChannel: new CustomOutputChannel()
|
||||
};
|
||||
}
|
||||
|
||||
private generateServerOptions(executablePath: string): ServerOptions {
|
||||
let launchArgs = [];
|
||||
launchArgs.push('--log-dir');
|
||||
let logFileLocation = path.join(serviceUtils.getDefaultLogLocation(), 'flatfileimport');
|
||||
launchArgs.push(logFileLocation);
|
||||
let config = vscode.workspace.getConfiguration(Constants.extensionConfigSectionName);
|
||||
if (config) {
|
||||
let logDebugInfo = config[Constants.configLogDebugInfo];
|
||||
if (logDebugInfo) {
|
||||
launchArgs.push('--enable-logging');
|
||||
}
|
||||
}
|
||||
|
||||
return { command: executablePath, args: launchArgs, transport: TransportKind.stdio };
|
||||
}
|
||||
|
||||
private generateHandleServerProviderEvent(): EventAndListener {
|
||||
let dots = 0;
|
||||
return (e: string, ...args: any[]) => {
|
||||
this.outputChannel.show();
|
||||
this.statusView.show();
|
||||
switch (e) {
|
||||
case Events.INSTALL_START:
|
||||
this.outputChannel.appendLine(localize('installingServiceDetailed', 'Installing {0} service to {1}', Constants.serviceName, args[0]));
|
||||
this.statusView.text = localize('installingService', 'Installing Service');
|
||||
break;
|
||||
case Events.INSTALL_END:
|
||||
this.outputChannel.appendLine(localize('serviceInstalled', 'Installed'));
|
||||
break;
|
||||
case Events.DOWNLOAD_START:
|
||||
this.outputChannel.appendLine(localize('downloadingService', 'Downloading {0}', args[0]));
|
||||
this.outputChannel.append(`(${Math.ceil(args[1] / 1024)} KB)`);
|
||||
this.statusView.text = localize('downloadingServiceStatus', 'Downloading Service');
|
||||
break;
|
||||
case Events.DOWNLOAD_PROGRESS:
|
||||
let newDots = Math.ceil(args[0] / 5);
|
||||
if (newDots > dots) {
|
||||
this.outputChannel.append('.'.repeat(newDots - dots));
|
||||
dots = newDots;
|
||||
}
|
||||
break;
|
||||
case Events.DOWNLOAD_END:
|
||||
this.outputChannel.appendLine(localize('downloadingServiceComplete', 'Done!'));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class CustomOutputChannel implements vscode.OutputChannel {
|
||||
name: string;
|
||||
append(value: string): void {
|
||||
}
|
||||
appendLine(value: string): void {
|
||||
}
|
||||
// tslint:disable-next-line:no-empty
|
||||
clear(): void {
|
||||
}
|
||||
show(preserveFocus?: boolean): void;
|
||||
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void;
|
||||
// tslint:disable-next-line:no-empty
|
||||
show(column?: any, preserveFocus?: any): void {
|
||||
}
|
||||
// tslint:disable-next-line:no-empty
|
||||
hide(): void {
|
||||
}
|
||||
// tslint:disable-next-line:no-empty
|
||||
dispose(): void {
|
||||
}
|
||||
}
|
||||
|
||||
156
extensions/import/src/services/serviceUtils.ts
Normal file
156
extensions/import/src/services/serviceUtils.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 path from 'path';
|
||||
import * as crypto from 'crypto';
|
||||
import * as os from 'os';
|
||||
|
||||
const baseConfig = require('./config.json');
|
||||
|
||||
// The function is a duplicate of \src\paths.js. IT would be better to import path.js but it doesn't
|
||||
// work for now because the extension is running in different process.
|
||||
export function getAppDataPath(): string {
|
||||
let platform = process.platform;
|
||||
switch (platform) {
|
||||
case 'win32': return process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming');
|
||||
case 'darwin': return path.join(os.homedir(), 'Library', 'Application Support');
|
||||
case 'linux': return process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
|
||||
default: throw new Error('Platform not supported');
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultLogLocation(): string {
|
||||
return path.join(getAppDataPath(), 'sqlops');
|
||||
}
|
||||
|
||||
export function ensure(target: object, key: string): any {
|
||||
if (target[key] === void 0) {
|
||||
target[key] = {} as any;
|
||||
}
|
||||
return target[key];
|
||||
}
|
||||
|
||||
export interface IPackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
aiKey: string;
|
||||
}
|
||||
|
||||
export function getPackageInfo(packageJson: any): IPackageInfo {
|
||||
if (packageJson) {
|
||||
return {
|
||||
name: packageJson.name,
|
||||
version: packageJson.version,
|
||||
aiKey: packageJson.aiKey
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function generateUserId(): Promise<string> {
|
||||
return new Promise<string>(resolve => {
|
||||
try {
|
||||
let interfaces = os.networkInterfaces();
|
||||
let mac;
|
||||
for (let key of Object.keys(interfaces)) {
|
||||
let item = interfaces[key][0];
|
||||
if (!item.internal) {
|
||||
mac = item.mac;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mac) {
|
||||
resolve(crypto.createHash('sha256').update(mac + os.homedir(), 'utf8').digest('hex'));
|
||||
} else {
|
||||
resolve(generateGuid());
|
||||
}
|
||||
} catch (err) {
|
||||
resolve(generateGuid()); // fallback
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function generateGuid(): string {
|
||||
let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
|
||||
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
|
||||
let oct: string = '';
|
||||
let tmp: number;
|
||||
/* tslint:disable:no-bitwise */
|
||||
for (let a: number = 0; a < 4; a++) {
|
||||
tmp = (4294967296 * Math.random()) | 0;
|
||||
oct += hexValues[tmp & 0xF] +
|
||||
hexValues[tmp >> 4 & 0xF] +
|
||||
hexValues[tmp >> 8 & 0xF] +
|
||||
hexValues[tmp >> 12 & 0xF] +
|
||||
hexValues[tmp >> 16 & 0xF] +
|
||||
hexValues[tmp >> 20 & 0xF] +
|
||||
hexValues[tmp >> 24 & 0xF] +
|
||||
hexValues[tmp >> 28 & 0xF];
|
||||
}
|
||||
|
||||
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
|
||||
let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
|
||||
return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12);
|
||||
/* tslint:enable:no-bitwise */
|
||||
}
|
||||
|
||||
export function verifyPlatform(): Thenable<boolean> {
|
||||
if (os.platform() === 'darwin' && parseFloat(os.release()) < 16.0) {
|
||||
return Promise.resolve(false);
|
||||
} else {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export function getServiceInstallConfig(basePath?: string): any {
|
||||
if (!basePath) {
|
||||
basePath = __dirname;
|
||||
}
|
||||
let config = JSON.parse(JSON.stringify(baseConfig));
|
||||
config.installDirectory = path.join(basePath, config.installDirectory);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export function getResolvedServiceInstallationPath(runtime: Runtime, basePath?: string): string {
|
||||
let config = getServiceInstallConfig(basePath);
|
||||
let dir = config.installDirectory;
|
||||
dir = dir.replace('{#version#}', config.version);
|
||||
dir = dir.replace('{#platform#}', getRuntimeDisplayName(runtime));
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
export function getRuntimeDisplayName(runtime: Runtime): string {
|
||||
switch (runtime) {
|
||||
case Runtime.Windows_64:
|
||||
return 'Windows';
|
||||
case Runtime.Windows_86:
|
||||
return 'Windows';
|
||||
case Runtime.OSX:
|
||||
return 'OSX';
|
||||
case Runtime.Linux_64:
|
||||
return 'Linux';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
export enum Runtime {
|
||||
Unknown = <any>'Unknown',
|
||||
Windows_86 = <any>'Windows_86',
|
||||
Windows_64 = <any>'Windows_64',
|
||||
OSX = <any>'OSX',
|
||||
CentOS_7 = <any>'CentOS_7',
|
||||
Debian_8 = <any>'Debian_8',
|
||||
Fedora_23 = <any>'Fedora_23',
|
||||
OpenSUSE_13_2 = <any>'OpenSUSE_13_2',
|
||||
SLES_12_2 = <any>'SLES_12_2',
|
||||
RHEL_7 = <any>'RHEL_7',
|
||||
Ubuntu_14 = <any>'Ubuntu_14',
|
||||
Ubuntu_16 = <any>'Ubuntu_16',
|
||||
Linux_64 = <any>'Linux_64',
|
||||
Linux_86 = <any>'Linux-86'
|
||||
}
|
||||
216
extensions/import/src/services/telemetry.ts
Normal file
216
extensions/import/src/services/telemetry.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ErrorAction, CloseAction } from 'vscode-languageclient';
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
import { PlatformInformation } from 'service-downloader/out/platform';
|
||||
import * as opener from 'opener';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import * as constants from '../constants';
|
||||
import * as serviceUtils from './serviceUtils';
|
||||
import { IMessage, ITelemetryEventProperties, ITelemetryEventMeasures } from './contracts';
|
||||
|
||||
|
||||
/**
|
||||
* Handle Language Service client errors
|
||||
* @class LanguageClientErrorHandler
|
||||
*/
|
||||
export class LanguageClientErrorHandler {
|
||||
|
||||
/**
|
||||
* Creates an instance of LanguageClientErrorHandler.
|
||||
* @memberOf LanguageClientErrorHandler
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an error message prompt with a link to known issues wiki page
|
||||
* @memberOf LanguageClientErrorHandler
|
||||
*/
|
||||
showOnErrorPrompt(): void {
|
||||
// TODO add telemetry
|
||||
// Telemetry.sendTelemetryEvent('SqlToolsServiceCrash');
|
||||
let crashButtonText = localize('import.serviceCrashButton', 'Give Feedback');
|
||||
vscode.window.showErrorMessage(
|
||||
localize('serviceCrashMessage', 'service component could not start'),
|
||||
crashButtonText
|
||||
).then(action => {
|
||||
if (action && action === crashButtonText) {
|
||||
opener(constants.serviceCrashLink);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for language service client error
|
||||
*
|
||||
* @param {Error} error
|
||||
* @param {Message} message
|
||||
* @param {number} count
|
||||
* @returns {ErrorAction}
|
||||
*
|
||||
* @memberOf LanguageClientErrorHandler
|
||||
*/
|
||||
error(error: Error, message: IMessage, count: number): ErrorAction {
|
||||
this.showOnErrorPrompt();
|
||||
|
||||
// we don't retry running the service since crashes leave the extension
|
||||
// in a bad, unrecovered state
|
||||
return ErrorAction.Shutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for language service client closed
|
||||
*
|
||||
* @returns {CloseAction}
|
||||
*
|
||||
* @memberOf LanguageClientErrorHandler
|
||||
*/
|
||||
closed(): CloseAction {
|
||||
this.showOnErrorPrompt();
|
||||
|
||||
// we don't retry running the service since crashes leave the extension
|
||||
// in a bad, unrecovered state
|
||||
return CloseAction.DoNotRestart;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters error paths to only include source files. Exported to support testing
|
||||
*/
|
||||
export function FilterErrorPath(line: string): string {
|
||||
if (line) {
|
||||
let values: string[] = line.split('/out/');
|
||||
if (values.length <= 1) {
|
||||
// Didn't match expected format
|
||||
return line;
|
||||
} else {
|
||||
return values[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Telemetry {
|
||||
private static reporter: TelemetryReporter;
|
||||
private static userId: string;
|
||||
private static platformInformation: PlatformInformation;
|
||||
private static disabled: boolean;
|
||||
|
||||
// Get the unique ID for the current user of the extension
|
||||
public static getUserId(): Promise<string> {
|
||||
return new Promise<string>(resolve => {
|
||||
// Generate the user id if it has not been created already
|
||||
if (typeof this.userId === 'undefined') {
|
||||
let id = serviceUtils.generateUserId();
|
||||
id.then(newId => {
|
||||
this.userId = newId;
|
||||
resolve(this.userId);
|
||||
});
|
||||
} else {
|
||||
resolve(this.userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static getPlatformInformation(): Promise<PlatformInformation> {
|
||||
if (this.platformInformation) {
|
||||
return Promise.resolve(this.platformInformation);
|
||||
} else {
|
||||
return new Promise<PlatformInformation>(resolve => {
|
||||
PlatformInformation.getCurrent().then(info => {
|
||||
this.platformInformation = info;
|
||||
resolve(this.platformInformation);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable telemetry reporting
|
||||
*/
|
||||
public static disable(): void {
|
||||
this.disabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the telemetry reporter for use.
|
||||
*/
|
||||
public static initialize(): void {
|
||||
if (typeof this.reporter === 'undefined') {
|
||||
// Check if the user has opted out of telemetry
|
||||
if (!vscode.workspace.getConfiguration('telemetry').get<boolean>('enableTelemetry', true)) {
|
||||
this.disable();
|
||||
return;
|
||||
}
|
||||
let packageInfo = vscode.extensions.getExtension('Microsoft.import').packageJSON;
|
||||
this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a telemetry event for an exception
|
||||
*/
|
||||
public static sendTelemetryEventForException(
|
||||
err: any, methodName: string, extensionConfigName: string): void {
|
||||
try {
|
||||
let stackArray: string[];
|
||||
let firstLine: string = '';
|
||||
if (err !== undefined && err.stack !== undefined) {
|
||||
stackArray = err.stack.split('\n');
|
||||
if (stackArray !== undefined && stackArray.length >= 2) {
|
||||
firstLine = stackArray[1]; // The fist line is the error message and we don't want to send that telemetry event
|
||||
firstLine = FilterErrorPath(firstLine);
|
||||
}
|
||||
}
|
||||
|
||||
// Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII
|
||||
this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine });
|
||||
// Utils.logDebug('Unhandled Exception occurred. error: ' + err + ' method: ' + methodName, extensionConfigName);
|
||||
} catch (telemetryErr) {
|
||||
// If sending telemetry event fails ignore it so it won't break the extension
|
||||
// Utils.logDebug('Failed to send telemetry event. error: ' + telemetryErr, extensionConfigName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a telemetry event using application insights
|
||||
*/
|
||||
public static sendTelemetryEvent(
|
||||
eventName: string,
|
||||
properties?: ITelemetryEventProperties,
|
||||
measures?: ITelemetryEventMeasures): void {
|
||||
|
||||
if (typeof this.disabled === 'undefined') {
|
||||
this.disabled = false;
|
||||
}
|
||||
|
||||
if (this.disabled || typeof (this.reporter) === 'undefined') {
|
||||
// Don't do anything if telemetry is disabled
|
||||
return;
|
||||
}
|
||||
|
||||
if (!properties || typeof properties === 'undefined') {
|
||||
properties = {};
|
||||
}
|
||||
|
||||
// Augment the properties structure with additional common properties before sending
|
||||
Promise.all([this.getUserId(), this.getPlatformInformation()]).then(() => {
|
||||
properties['userId'] = this.userId;
|
||||
properties['distribution'] = (this.platformInformation && this.platformInformation.distribution) ?
|
||||
`${this.platformInformation.distribution.name}, ${this.platformInformation.distribution.version}` : '';
|
||||
|
||||
this.reporter.sendTelemetryEvent(eventName, properties, measures);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Telemetry.initialize();
|
||||
9
extensions/import/src/typings/ref.d.ts
vendored
Normal file
9
extensions/import/src/typings/ref.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference path='../../../../src/sql/sqlops.d.ts'/>
|
||||
/// <reference path='../../../../src/sql/sqlops.proposed.d.ts'/>
|
||||
/// <reference types='@types/node'/>
|
||||
59
extensions/import/src/wizard/api/importPage.ts
Normal file
59
extensions/import/src/wizard/api/importPage.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ImportDataModel } from './models';
|
||||
import * as sqlops from 'sqlops';
|
||||
import { FlatFileProvider } from '../../services/contracts';
|
||||
import { FlatFileWizard } from '../flatFileWizard';
|
||||
|
||||
export abstract class ImportPage {
|
||||
|
||||
protected readonly wizardPage: sqlops.window.modelviewdialog.WizardPage;
|
||||
protected readonly instance: FlatFileWizard;
|
||||
protected readonly model: ImportDataModel;
|
||||
protected readonly view: sqlops.ModelView;
|
||||
protected readonly provider: FlatFileProvider;
|
||||
|
||||
protected constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
|
||||
this.instance = instance;
|
||||
this.wizardPage = wizardPage;
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method constructs all the elements of the page.
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
public async abstract start(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* This method is called when the user is entering the page.
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
public async abstract onPageEnter(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* This method is called when the user is leaving the page.
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
public async abstract onPageLeave(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Sets up a navigation validator.
|
||||
* This will be called right before onPageEnter().
|
||||
*/
|
||||
public abstract setupNavigationValidator();
|
||||
|
||||
/**
|
||||
* Override this method to cleanup what you don't need cached in the page.
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
public async cleanup(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
33
extensions/import/src/wizard/api/models.ts
Normal file
33
extensions/import/src/wizard/api/models.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 data model that communicates between the pages.
|
||||
*/
|
||||
export interface ImportDataModel {
|
||||
ownerUri: string;
|
||||
proseColumns: ColumnMetadata[];
|
||||
proseDataPreview: string[][];
|
||||
server: sqlops.connection.Connection;
|
||||
serverId: string;
|
||||
database: string;
|
||||
table: string;
|
||||
schema: string;
|
||||
filePath: string;
|
||||
fileType: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata of a column
|
||||
*/
|
||||
export interface ColumnMetadata {
|
||||
columnName: string;
|
||||
dataType: string;
|
||||
primaryKey: boolean;
|
||||
nullable: boolean;
|
||||
}
|
||||
144
extensions/import/src/wizard/flatFileWizard.ts
Normal file
144
extensions/import/src/wizard/flatFileWizard.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as nls from 'vscode-nls';
|
||||
import * as sqlops from 'sqlops';
|
||||
import { FlatFileProvider } from '../services/contracts';
|
||||
import { ImportDataModel } from './api/models';
|
||||
import { ImportPage } from './api/importPage';
|
||||
// pages
|
||||
import { FileConfigPage } from './pages/fileConfigPage';
|
||||
import { ProsePreviewPage } from './pages/prosePreviewPage';
|
||||
import { ModifyColumnsPage } from './pages/modifyColumnsPage';
|
||||
import { SummaryPage } from './pages/summaryPage';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class FlatFileWizard {
|
||||
private readonly provider: FlatFileProvider;
|
||||
private wizard: sqlops.window.modelviewdialog.Wizard;
|
||||
|
||||
private importAnotherFileButton: sqlops.window.modelviewdialog.Button;
|
||||
|
||||
constructor(provider: FlatFileProvider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public async start(p: any, ...args: any[]) {
|
||||
let model = <ImportDataModel>{};
|
||||
|
||||
let profile = <sqlops.IConnectionProfile>p.connectionProfile;
|
||||
if (profile) {
|
||||
model.serverId = profile.id;
|
||||
model.database = profile.databaseName;
|
||||
}
|
||||
|
||||
let pages: Map<number, ImportPage> = new Map<number, ImportPage>();
|
||||
|
||||
|
||||
let connections = await sqlops.connection.getActiveConnections();
|
||||
if (!connections || connections.length === 0) {
|
||||
vscode.window.showErrorMessage(localize('import.needConnection', 'Please connect to a server before using this wizard.'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.wizard = sqlops.window.modelviewdialog.createWizard(localize('flatFileImport.wizardName', 'Import flat file wizard'));
|
||||
let page1 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page1Name', 'Specify Input File'));
|
||||
let page2 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page2Name', 'Preview Data'));
|
||||
let page3 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page3Name', 'Modify Columns'));
|
||||
let page4 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page4Name', 'Summary'));
|
||||
|
||||
let fileConfigPage: FileConfigPage;
|
||||
|
||||
page1.registerContent(async (view) => {
|
||||
fileConfigPage = new FileConfigPage(this, page1, model, view, this.provider);
|
||||
pages.set(0, fileConfigPage);
|
||||
await fileConfigPage.start().then(() => {
|
||||
fileConfigPage.setupNavigationValidator();
|
||||
fileConfigPage.onPageEnter();
|
||||
});
|
||||
});
|
||||
|
||||
let prosePreviewPage: ProsePreviewPage;
|
||||
page2.registerContent(async (view) => {
|
||||
prosePreviewPage = new ProsePreviewPage(this, page2, model, view, this.provider);
|
||||
pages.set(1, prosePreviewPage);
|
||||
await prosePreviewPage.start();
|
||||
});
|
||||
|
||||
let modifyColumnsPage: ModifyColumnsPage;
|
||||
page3.registerContent(async (view) => {
|
||||
modifyColumnsPage = new ModifyColumnsPage(this, page3, model, view, this.provider);
|
||||
pages.set(2, modifyColumnsPage);
|
||||
await modifyColumnsPage.start();
|
||||
});
|
||||
|
||||
let summaryPage: SummaryPage;
|
||||
|
||||
page4.registerContent(async (view) => {
|
||||
summaryPage = new SummaryPage(this, page4, model, view, this.provider);
|
||||
pages.set(3, summaryPage);
|
||||
await summaryPage.start();
|
||||
});
|
||||
|
||||
|
||||
this.importAnotherFileButton = sqlops.window.modelviewdialog.createButton(localize('flatFileImport.importNewFile', 'Import new file'));
|
||||
this.importAnotherFileButton.onClick(() => {
|
||||
//TODO replace this with proper cleanup for all the pages
|
||||
this.wizard.close();
|
||||
pages.forEach((page) => page.cleanup());
|
||||
this.wizard.open();
|
||||
});
|
||||
|
||||
this.importAnotherFileButton.hidden = true;
|
||||
this.wizard.customButtons = [this.importAnotherFileButton];
|
||||
|
||||
this.wizard.onPageChanged(async (event) => {
|
||||
let idx = event.newPage;
|
||||
|
||||
let page = pages.get(idx);
|
||||
|
||||
if (page) {
|
||||
page.setupNavigationValidator();
|
||||
page.onPageEnter();
|
||||
}
|
||||
});
|
||||
|
||||
this.wizard.onPageChanged(async (event) => {
|
||||
let idx = event.lastPage;
|
||||
|
||||
let page = pages.get(idx);
|
||||
if (page) {
|
||||
page.onPageLeave();
|
||||
}
|
||||
});
|
||||
|
||||
//not needed for this wizard
|
||||
this.wizard.generateScriptButton.hidden = true;
|
||||
|
||||
this.wizard.pages = [page1, page2, page3, page4];
|
||||
|
||||
this.wizard.open();
|
||||
}
|
||||
|
||||
public setImportAnotherFileVisibility(visibility: boolean) {
|
||||
this.importAnotherFileButton.hidden = !visibility;
|
||||
}
|
||||
|
||||
public registerNavigationValidator(validator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean) {
|
||||
this.wizard.registerNavigationValidator(validator);
|
||||
}
|
||||
|
||||
public changeNextButtonLabel(label: string) {
|
||||
this.wizard.nextButton.label = label;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
410
extensions/import/src/wizard/pages/fileConfigPage.ts
Normal file
410
extensions/import/src/wizard/pages/fileConfigPage.ts
Normal file
@@ -0,0 +1,410 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { ImportDataModel } from '../api/models';
|
||||
import { ImportPage } from '../api/importPage';
|
||||
import { FlatFileProvider } from '../../services/contracts';
|
||||
import { FlatFileWizard } from '../flatFileWizard';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class FileConfigPage extends ImportPage {
|
||||
|
||||
private serverDropdown: sqlops.DropDownComponent;
|
||||
private databaseDropdown: sqlops.DropDownComponent;
|
||||
private fileTextBox: sqlops.InputBoxComponent;
|
||||
private fileButton: sqlops.ButtonComponent;
|
||||
private tableNameTextBox: sqlops.InputBoxComponent;
|
||||
private schemaDropdown: sqlops.DropDownComponent;
|
||||
private form: sqlops.FormContainer;
|
||||
|
||||
private databaseLoader: sqlops.LoadingComponent;
|
||||
private schemaLoader: sqlops.LoadingComponent;
|
||||
|
||||
private tableNames: string[] = [];
|
||||
|
||||
public constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
|
||||
super(instance, wizardPage, model, view, provider);
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
let schemaComponent = await this.createSchemaDropdown();
|
||||
let tableNameComponent = await this.createTableNameBox();
|
||||
let fileBrowserComponent = await this.createFileBrowser();
|
||||
let databaseComponent = await this.createDatabaseDropdown();
|
||||
let serverComponent = await this.createServerDropdown();
|
||||
|
||||
this.form = this.view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
serverComponent,
|
||||
databaseComponent,
|
||||
fileBrowserComponent,
|
||||
tableNameComponent,
|
||||
schemaComponent
|
||||
]).component();
|
||||
|
||||
await this.view.initializeModel(this.form);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
let r1 = await this.populateServerDropdown();
|
||||
let r2 = await this.populateDatabaseDropdown();
|
||||
let r3 = await this.populateSchemaDropdown();
|
||||
return r1 && r2 && r3;
|
||||
}
|
||||
|
||||
async onPageLeave(): Promise<boolean> {
|
||||
delete this.model.serverId;
|
||||
return true;
|
||||
}
|
||||
|
||||
public async cleanup(): Promise<boolean> {
|
||||
delete this.model.filePath;
|
||||
delete this.model.table;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public setupNavigationValidator() {
|
||||
this.instance.registerNavigationValidator((info) => {
|
||||
if (this.schemaLoader.loading || this.databaseLoader.loading) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private async createServerDropdown(): Promise<sqlops.FormComponent> {
|
||||
this.serverDropdown = this.view.modelBuilder.dropDown().withProperties({
|
||||
required: true
|
||||
}).component();
|
||||
|
||||
// Handle server changes
|
||||
this.serverDropdown.onValueChanged(async (params) => {
|
||||
this.model.server = (this.serverDropdown.value as ConnectionDropdownValue).connection;
|
||||
|
||||
await this.populateDatabaseDropdown();
|
||||
await this.populateSchemaDropdown();
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.serverDropdown,
|
||||
title: localize('flatFileImport.serverDropdownTitle', 'Server the database is in')
|
||||
};
|
||||
}
|
||||
|
||||
private async populateServerDropdown(): Promise<boolean> {
|
||||
let cons = await sqlops.connection.getActiveConnections();
|
||||
// This user has no active connections ABORT MISSION
|
||||
if (!cons || cons.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
let count = -1;
|
||||
let idx = -1;
|
||||
|
||||
|
||||
let values = cons.map(c => {
|
||||
// Handle the code to remember what the user's choice was from before
|
||||
count++;
|
||||
if (idx === -1) {
|
||||
if (this.model.server && c.connectionId === this.model.server.connectionId) {
|
||||
idx = count;
|
||||
} else if (this.model.serverId && c.connectionId === this.model.serverId) {
|
||||
idx = count;
|
||||
}
|
||||
}
|
||||
|
||||
let db = c.options.databaseDisplayName;
|
||||
let usr = c.options.user;
|
||||
let srv = c.options.server;
|
||||
|
||||
if (!db) {
|
||||
db = '<default>';
|
||||
}
|
||||
|
||||
if (!usr) {
|
||||
usr = 'default';
|
||||
}
|
||||
|
||||
let finalName = `${srv}, ${db} (${usr})`;
|
||||
return {
|
||||
connection: c,
|
||||
displayName: finalName,
|
||||
name: c.connectionId
|
||||
};
|
||||
});
|
||||
|
||||
if (idx >= 0) {
|
||||
let tmp = values[0];
|
||||
values[0] = values[idx];
|
||||
values[idx] = tmp;
|
||||
} else {
|
||||
delete this.model.server;
|
||||
delete this.model.serverId;
|
||||
delete this.model.database;
|
||||
delete this.model.schema;
|
||||
}
|
||||
|
||||
this.model.server = values[0].connection;
|
||||
|
||||
|
||||
this.serverDropdown.updateProperties({
|
||||
values: values
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private async createDatabaseDropdown(): Promise<sqlops.FormComponent> {
|
||||
this.databaseDropdown = this.view.modelBuilder.dropDown().withProperties({
|
||||
required: true
|
||||
}).component();
|
||||
|
||||
// Handle database changes
|
||||
this.databaseDropdown.onValueChanged(async (db) => {
|
||||
this.model.database = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
|
||||
//this.populateTableNames();
|
||||
this.populateSchemaDropdown();
|
||||
});
|
||||
|
||||
this.databaseLoader = this.view.modelBuilder.loadingComponent().withItem(this.databaseDropdown).component();
|
||||
|
||||
return {
|
||||
component: this.databaseLoader,
|
||||
title: localize('flatFileImport.databaseDropdownTitle', 'Database the table is created in')
|
||||
};
|
||||
}
|
||||
|
||||
private async populateDatabaseDropdown(): Promise<boolean> {
|
||||
this.databaseLoader.loading = true;
|
||||
this.databaseDropdown.updateProperties({ values: [] });
|
||||
this.schemaDropdown.updateProperties({ values: [] });
|
||||
|
||||
if (!this.model.server) {
|
||||
//TODO handle error case
|
||||
this.databaseLoader.loading = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
let idx = -1;
|
||||
let count = -1;
|
||||
let values = (await sqlops.connection.listDatabases(this.model.server.connectionId)).map(db => {
|
||||
count++;
|
||||
if (this.model.database && db === this.model.database) {
|
||||
idx = count;
|
||||
}
|
||||
|
||||
return {
|
||||
displayName: db,
|
||||
name: db
|
||||
};
|
||||
});
|
||||
|
||||
if (idx >= 0) {
|
||||
let tmp = values[0];
|
||||
values[0] = values[idx];
|
||||
values[idx] = tmp;
|
||||
} else {
|
||||
delete this.model.database;
|
||||
delete this.model.schema;
|
||||
}
|
||||
|
||||
this.model.database = values[0].name;
|
||||
|
||||
this.databaseDropdown.updateProperties({
|
||||
values: values
|
||||
});
|
||||
this.databaseLoader.loading = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async createFileBrowser(): Promise<sqlops.FormComponent> {
|
||||
this.fileTextBox = this.view.modelBuilder.inputBox().withProperties({
|
||||
required: true
|
||||
}).component();
|
||||
this.fileButton = this.view.modelBuilder.button().withProperties({
|
||||
label: localize('flatFileImport.browseFiles', 'Browse'),
|
||||
}).component();
|
||||
|
||||
this.fileButton.onDidClick(async (click) => {
|
||||
let fileUris = await vscode.window.showOpenDialog(
|
||||
{
|
||||
canSelectFiles: true,
|
||||
canSelectFolders: false,
|
||||
canSelectMany: false,
|
||||
openLabel: localize('flatFileImport.openFile', 'Open'),
|
||||
filters: {
|
||||
'Files': ['csv', 'txt']
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!fileUris || fileUris.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fileUri = fileUris[0];
|
||||
this.fileTextBox.value = fileUri.fsPath;
|
||||
|
||||
// Get the name of the file.
|
||||
let nameStart = fileUri.path.lastIndexOf('/');
|
||||
let nameEnd = fileUri.path.lastIndexOf('.');
|
||||
|
||||
// Handle files without extensions
|
||||
if (nameEnd === 0) {
|
||||
nameEnd = fileUri.path.length;
|
||||
}
|
||||
this.model.fileType = 'TXT';
|
||||
let extension = fileUri.path.substring(nameEnd + 1, fileUri.path.length);
|
||||
|
||||
if (extension.toLowerCase() === 'json') {
|
||||
this.model.fileType = 'JSON';
|
||||
}
|
||||
|
||||
this.tableNameTextBox.value = fileUri.path.substring(nameStart + 1, nameEnd);
|
||||
this.model.table = this.tableNameTextBox.value;
|
||||
this.tableNameTextBox.validate();
|
||||
|
||||
// Let then model know about the file path
|
||||
this.model.filePath = fileUri.fsPath;
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.fileTextBox,
|
||||
title: localize('flatFileImport.fileTextboxTitle', 'Location of the file to be imported'),
|
||||
actions: [this.fileButton]
|
||||
};
|
||||
}
|
||||
|
||||
private async createTableNameBox(): Promise<sqlops.FormComponent> {
|
||||
this.tableNameTextBox = this.view.modelBuilder.inputBox().withValidation((name) => {
|
||||
let tableName = name.value;
|
||||
|
||||
if (!tableName || tableName.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This won't actually do anything until table names are brought back in.
|
||||
if (this.tableNames.indexOf(tableName) !== -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).withProperties({
|
||||
required: true,
|
||||
}).component();
|
||||
|
||||
this.tableNameTextBox.onTextChanged((tableName) => {
|
||||
this.model.table = tableName;
|
||||
});
|
||||
|
||||
return {
|
||||
component: this.tableNameTextBox,
|
||||
title: localize('flatFileImport.tableTextboxTitle', 'New table name'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private async createSchemaDropdown(): Promise<sqlops.FormComponent> {
|
||||
this.schemaDropdown = this.view.modelBuilder.dropDown().withProperties({
|
||||
required: true
|
||||
}).component();
|
||||
this.schemaLoader = this.view.modelBuilder.loadingComponent().withItem(this.schemaDropdown).component();
|
||||
|
||||
this.schemaDropdown.onValueChanged(() => {
|
||||
this.model.schema = (<sqlops.CategoryValue>this.schemaDropdown.value).name;
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
component: this.schemaLoader,
|
||||
title: localize('flatFileImport.schemaTextboxTitle', 'Table schema'),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private async populateSchemaDropdown(): Promise<boolean> {
|
||||
this.schemaLoader.loading = true;
|
||||
let connectionUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
|
||||
let queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(this.model.server.providerName, sqlops.DataProviderType.QueryProvider);
|
||||
|
||||
let query = `SELECT name FROM sys.schemas`;
|
||||
|
||||
let results = await queryProvider.runQueryAndReturn(connectionUri, query);
|
||||
|
||||
let idx = -1;
|
||||
let count = -1;
|
||||
|
||||
let values = results.rows.map(row => {
|
||||
let schemaName = row[0].displayValue;
|
||||
count++;
|
||||
if (this.model.schema && schemaName === this.model.schema) {
|
||||
idx = count;
|
||||
}
|
||||
let val = row[0].displayValue;
|
||||
|
||||
return {
|
||||
name: val,
|
||||
displayName: val
|
||||
};
|
||||
});
|
||||
|
||||
if (idx >= 0) {
|
||||
let tmp = values[0];
|
||||
values[0] = values[idx];
|
||||
values[idx] = tmp;
|
||||
}
|
||||
|
||||
this.model.schema = values[0].name;
|
||||
|
||||
this.schemaDropdown.updateProperties({
|
||||
values: values
|
||||
});
|
||||
|
||||
this.schemaLoader.loading = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// private async populateTableNames(): Promise<boolean> {
|
||||
// this.tableNames = [];
|
||||
// let databaseName = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
|
||||
//
|
||||
// if (!databaseName || databaseName.length === 0) {
|
||||
// this.tableNames = [];
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// let connectionUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
|
||||
// let queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(this.model.server.providerName, sqlops.DataProviderType.QueryProvider);
|
||||
// let results: sqlops.SimpleExecuteResult;
|
||||
//
|
||||
// try {
|
||||
// //let query = sqlstring.format('USE ?; SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = \'BASE TABLE\'', [databaseName]);
|
||||
// //results = await queryProvider.runQueryAndReturn(connectionUri, query);
|
||||
// } catch (e) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// this.tableNames = results.rows.map(row => {
|
||||
// return row[0].displayValue;
|
||||
// });
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
interface ConnectionDropdownValue extends sqlops.CategoryValue {
|
||||
connection: sqlops.connection.Connection;
|
||||
}
|
||||
171
extensions/import/src/wizard/pages/modifyColumnsPage.ts
Normal file
171
extensions/import/src/wizard/pages/modifyColumnsPage.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 nls from 'vscode-nls';
|
||||
import { ColumnMetadata, ImportDataModel } from '../api/models';
|
||||
import { ImportPage } from '../api/importPage';
|
||||
import { FlatFileProvider } from '../../services/contracts';
|
||||
import { FlatFileWizard } from '../flatFileWizard';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class ModifyColumnsPage extends ImportPage {
|
||||
private readonly categoryValues = [
|
||||
{ name: 'bigint', displayName: 'bigint' },
|
||||
{ name: 'binary(50)', displayName: 'binary(50)' },
|
||||
{ name: 'bit', displayName: 'bit' },
|
||||
{ name: 'char(10)', displayName: 'char(10)' },
|
||||
{ name: 'date', displayName: 'date' },
|
||||
{ name: 'datetime', displayName: 'datetime' },
|
||||
{ name: 'datetime2(7)', displayName: 'datetime2(7)' },
|
||||
{ name: 'datetimeoffset(7)', displayName: 'datetimeoffset(7)' },
|
||||
{ name: 'decimal(18, 10)', displayName: 'decimal(18, 10)' },
|
||||
{ name: 'float', displayName: 'float' },
|
||||
{ name: 'geography', displayName: 'geography' },
|
||||
{ name: 'geometry', displayName: 'geometry' },
|
||||
{ name: 'hierarchyid', displayName: 'hierarchyid' },
|
||||
{ name: 'int', displayName: 'int' },
|
||||
{ name: 'money', displayName: 'money' },
|
||||
{ name: 'nchar(10)', displayName: 'nchar(10)' },
|
||||
{ name: 'ntext', displayName: 'ntext' },
|
||||
{ name: 'numeric(18, 0)', displayName: 'numeric(18, 0)' },
|
||||
{ name: 'nvarchar(50)', displayName: 'nvarchar(50)' },
|
||||
{ name: 'nvarchar(MAX)', displayName: 'nvarchar(MAX)' },
|
||||
{ name: 'real', displayName: 'real' },
|
||||
{ name: 'smalldatetime', displayName: 'smalldatetime' },
|
||||
{ name: 'smallint', displayName: 'smallint' },
|
||||
{ name: 'smallmoney', displayName: 'smallmoney' },
|
||||
{ name: 'sql_variant', displayName: 'sql_variant' },
|
||||
{ name: 'text', displayName: 'text' },
|
||||
{ name: 'time(7)', displayName: 'time(7)' },
|
||||
{ name: 'timestamp', displayName: 'timestamp' },
|
||||
{ name: 'tinyint', displayName: 'tinyint' },
|
||||
{ name: 'uniqueidentifier', displayName: 'uniqueidentifier' },
|
||||
{ name: 'varbinary(50)', displayName: 'varbinary(50)' },
|
||||
{ name: 'varbinary(MAX)', displayName: 'varbinary(MAX)' },
|
||||
{ name: 'varchar(50)', displayName: 'varchar(50)' },
|
||||
{ name: 'varchar(MAX)', displayName: 'varchar(MAX)' }
|
||||
];
|
||||
private table: sqlops.DeclarativeTableComponent;
|
||||
private loading: sqlops.LoadingComponent;
|
||||
private text: sqlops.TextComponent;
|
||||
private form: sqlops.FormContainer;
|
||||
|
||||
public constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
|
||||
super(instance, wizardPage, model, view, provider);
|
||||
}
|
||||
|
||||
|
||||
private static convertMetadata(column: ColumnMetadata): any[] {
|
||||
return [column.columnName, column.dataType, false, column.nullable];
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
this.loading = this.view.modelBuilder.loadingComponent().component();
|
||||
this.table = this.view.modelBuilder.declarativeTable().component();
|
||||
this.text = this.view.modelBuilder.text().component();
|
||||
|
||||
this.table.onDataChanged((e) => {
|
||||
this.model.proseColumns = [];
|
||||
this.table.data.forEach((row) => {
|
||||
this.model.proseColumns.push({
|
||||
columnName: row[0],
|
||||
dataType: row[1],
|
||||
primaryKey: row[2],
|
||||
nullable: row[3]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
this.form = this.view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
{
|
||||
component: this.text,
|
||||
title: ''
|
||||
},
|
||||
{
|
||||
component: this.table,
|
||||
title: ''
|
||||
}
|
||||
], {
|
||||
horizontal: false,
|
||||
componentWidth: '100%'
|
||||
}).component();
|
||||
|
||||
this.loading.component = this.form;
|
||||
await this.view.initializeModel(this.form);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
this.loading.loading = true;
|
||||
await this.populateTable();
|
||||
this.instance.changeNextButtonLabel(localize('flatFileImport.importData', 'Import Data'));
|
||||
this.loading.loading = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageLeave(): Promise<boolean> {
|
||||
this.instance.changeNextButtonLabel(localize('flatFileImport.next', 'Next'));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async cleanup(): Promise<boolean> {
|
||||
delete this.model.proseColumns;
|
||||
this.instance.changeNextButtonLabel(localize('flatFileImport.next', 'Next'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public setupNavigationValidator() {
|
||||
this.instance.registerNavigationValidator((info) => {
|
||||
return !this.loading.loading;
|
||||
});
|
||||
}
|
||||
|
||||
private async populateTable() {
|
||||
let data: any[][] = [];
|
||||
|
||||
this.model.proseColumns.forEach((column) => {
|
||||
data.push(ModifyColumnsPage.convertMetadata(column));
|
||||
});
|
||||
|
||||
this.table.updateProperties({
|
||||
height: 400,
|
||||
columns: [{
|
||||
displayName: localize('flatFileImport.columnName', 'Column Name'),
|
||||
valueType: sqlops.DeclarativeDataType.string,
|
||||
width: '150px',
|
||||
isReadOnly: false
|
||||
}, {
|
||||
displayName: localize('flatFileImport.dataType', 'Data Type'),
|
||||
valueType: sqlops.DeclarativeDataType.editableCategory,
|
||||
width: '150px',
|
||||
isReadOnly: false,
|
||||
categoryValues: this.categoryValues
|
||||
}, {
|
||||
displayName: localize('flatFileImport.primaryKey', 'Primary Key'),
|
||||
valueType: sqlops.DeclarativeDataType.boolean,
|
||||
width: '100px',
|
||||
isReadOnly: false
|
||||
}, {
|
||||
displayName: localize('flatFileImport.allowNulls', 'Allow Nulls'),
|
||||
valueType: sqlops.DeclarativeDataType.boolean,
|
||||
isReadOnly: false,
|
||||
width: '100px'
|
||||
}],
|
||||
data: data
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
123
extensions/import/src/wizard/pages/prosePreviewPage.ts
Normal file
123
extensions/import/src/wizard/pages/prosePreviewPage.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 nls from 'vscode-nls';
|
||||
import { ImportDataModel } from '../api/models';
|
||||
import { ImportPage } from '../api/importPage';
|
||||
import { FlatFileProvider } from '../../services/contracts';
|
||||
import { FlatFileWizard } from '../flatFileWizard';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class ProsePreviewPage extends ImportPage {
|
||||
private table: sqlops.TableComponent;
|
||||
private loading: sqlops.LoadingComponent;
|
||||
private form: sqlops.FormContainer;
|
||||
private refresh: sqlops.ButtonComponent;
|
||||
|
||||
public constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
|
||||
super(instance, wizardPage, model, view, provider);
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
this.table = this.view.modelBuilder.table().component();
|
||||
this.refresh = this.view.modelBuilder.button().withProperties({
|
||||
label: localize('flatFileImport.refresh', 'Refresh'),
|
||||
isFile: false
|
||||
}).component();
|
||||
|
||||
this.refresh.onDidClick(async () => {
|
||||
this.onPageEnter();
|
||||
});
|
||||
|
||||
this.loading = this.view.modelBuilder.loadingComponent().component();
|
||||
|
||||
this.form = this.view.modelBuilder.formContainer().withFormItems([
|
||||
{
|
||||
component: this.table,
|
||||
title: localize('flatFileImport.prosePreviewMessage', 'This operation analyzed the input file structure to generate the preview below for up to the first 50 rows.'),
|
||||
actions: [this.refresh]
|
||||
}
|
||||
]).component();
|
||||
|
||||
this.loading.component = this.form;
|
||||
|
||||
await this.view.initializeModel(this.loading);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
this.loading.loading = true;
|
||||
await this.handleProse();
|
||||
await this.populateTable(this.model.proseDataPreview, this.model.proseColumns.map(c => c.columnName));
|
||||
this.loading.loading = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageLeave(): Promise<boolean> {
|
||||
await this.emptyTable();
|
||||
return true;
|
||||
}
|
||||
|
||||
async cleanup(): Promise<boolean> {
|
||||
delete this.model.proseDataPreview;
|
||||
return true;
|
||||
}
|
||||
|
||||
public setupNavigationValidator() {
|
||||
this.instance.registerNavigationValidator((info) => {
|
||||
return !this.loading.loading;
|
||||
});
|
||||
}
|
||||
|
||||
private async handleProse() {
|
||||
await this.provider.sendPROSEDiscoveryRequest({
|
||||
filePath: this.model.filePath,
|
||||
tableName: this.model.table,
|
||||
schemaName: this.model.schema,
|
||||
fileType: this.model.fileType
|
||||
}).then((result) => {
|
||||
this.model.proseDataPreview = result.dataPreview;
|
||||
this.model.proseColumns = [];
|
||||
result.columnInfo.forEach((column) => {
|
||||
this.model.proseColumns.push({
|
||||
columnName: column.name,
|
||||
dataType: column.sqlType,
|
||||
primaryKey: false,
|
||||
nullable: column.isNullable
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async populateTable(tableData: string[][], columnHeaders: string[]) {
|
||||
let rows;
|
||||
let rowsLength = tableData.length;
|
||||
|
||||
if (rowsLength > 50) {
|
||||
rows = tableData;
|
||||
}
|
||||
else {
|
||||
rows = tableData.slice(0, rowsLength);
|
||||
}
|
||||
|
||||
this.table.updateProperties({
|
||||
data: rows,
|
||||
columns: columnHeaders,
|
||||
height: 400,
|
||||
width: '700',
|
||||
});
|
||||
}
|
||||
|
||||
private async emptyTable() {
|
||||
this.table.updateProperties([]);
|
||||
}
|
||||
|
||||
}
|
||||
173
extensions/import/src/wizard/pages/summaryPage.ts
Normal file
173
extensions/import/src/wizard/pages/summaryPage.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 nls from 'vscode-nls';
|
||||
|
||||
import { ImportDataModel } from '../api/models';
|
||||
import { ImportPage } from '../api/importPage';
|
||||
import { FlatFileProvider, InsertDataResponse } from '../../services/contracts';
|
||||
import { FlatFileWizard } from '../flatFileWizard';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
||||
export class SummaryPage extends ImportPage {
|
||||
private table: sqlops.TableComponent;
|
||||
private statusText: sqlops.TextComponent;
|
||||
private loading: sqlops.LoadingComponent;
|
||||
private form: sqlops.FormContainer;
|
||||
|
||||
public constructor(instance: FlatFileWizard, wizardPage: sqlops.window.modelviewdialog.WizardPage, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
|
||||
super(instance, wizardPage, model, view, provider);
|
||||
}
|
||||
|
||||
async start(): Promise<boolean> {
|
||||
this.table = this.view.modelBuilder.table().component();
|
||||
this.statusText = this.view.modelBuilder.text().component();
|
||||
this.loading = this.view.modelBuilder.loadingComponent().withItem(this.statusText).component();
|
||||
|
||||
this.form = this.view.modelBuilder.formContainer().withFormItems(
|
||||
[
|
||||
{
|
||||
component: this.table,
|
||||
title: localize('flatFileImport.importInformation', 'Import information')
|
||||
},
|
||||
{
|
||||
component: this.loading,
|
||||
title: localize('flatFileImport.importStatus', 'Import status')
|
||||
}
|
||||
]
|
||||
).component();
|
||||
|
||||
await this.view.initializeModel(this.form);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageEnter(): Promise<boolean> {
|
||||
this.loading.loading = true;
|
||||
this.populateTable();
|
||||
await this.handleImport();
|
||||
this.loading.loading = false;
|
||||
this.instance.setImportAnotherFileVisibility(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async onPageLeave(): Promise<boolean> {
|
||||
this.instance.setImportAnotherFileVisibility(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
public setupNavigationValidator() {
|
||||
this.instance.registerNavigationValidator((info) => {
|
||||
return !this.loading.loading;
|
||||
});
|
||||
}
|
||||
|
||||
private populateTable() {
|
||||
this.table.updateProperties({
|
||||
data: [
|
||||
[localize('flatFileImport.serverName', 'Server name'), this.model.server.providerName],
|
||||
[localize('flatFileImport.databaseName', 'Database name'), this.model.database],
|
||||
[localize('flatFileImport.tableName', 'Table name'), this.model.table],
|
||||
[localize('flatFileImport.tableSchema', 'Table schema'), this.model.schema],
|
||||
[localize('flatFileImport.fileImport', 'File to be imported'), this.model.filePath]],
|
||||
columns: ['Object type', 'Name'],
|
||||
width: 600,
|
||||
height: 200
|
||||
});
|
||||
}
|
||||
|
||||
private async handleImport(): Promise<boolean> {
|
||||
let changeColumnResults = [];
|
||||
this.model.proseColumns.forEach((val, i, arr) => {
|
||||
let columnChangeParams = {
|
||||
index: i,
|
||||
newName: val.columnName,
|
||||
newDataType: val.dataType,
|
||||
newNullable: val.nullable,
|
||||
newInPrimaryKey: val.primaryKey
|
||||
};
|
||||
changeColumnResults.push(this.provider.sendChangeColumnSettingsRequest(columnChangeParams));
|
||||
});
|
||||
|
||||
let result: InsertDataResponse;
|
||||
let err;
|
||||
try {
|
||||
result = await this.provider.sendInsertDataRequest({
|
||||
connectionString: await this.getConnectionString(),
|
||||
//TODO check what SSMS uses as batch size
|
||||
batchSize: 500
|
||||
});
|
||||
} catch (e) {
|
||||
err = e.toString();
|
||||
}
|
||||
|
||||
let updateText: string;
|
||||
if (!result || !result.result.success) {
|
||||
updateText = '✗ ';
|
||||
if (!result) {
|
||||
updateText += err;
|
||||
} else {
|
||||
updateText += result.result.errorMessage;
|
||||
}
|
||||
} else {
|
||||
// TODO: When sql statements are in, implement this.
|
||||
//let rows = await this.getCountRowsInserted();
|
||||
//if (rows < 0) {
|
||||
updateText = localize('flatFileImport.success.norows', '✔ You have successfully inserted the data into a table.');
|
||||
//} else {
|
||||
//updateText = localize('flatFileImport.success.rows', '✔ You have successfully inserted {0} rows.', rows);
|
||||
//}
|
||||
}
|
||||
this.statusText.updateProperties({
|
||||
value: updateText
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the connection string to send to the middleware
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
private async getConnectionString(): Promise<string> {
|
||||
let options = this.model.server.options;
|
||||
let connectionString: string;
|
||||
|
||||
if (options.authenticationType === 'Integrated') {
|
||||
connectionString = `Data Source=${options.server + (options.port ? `,${options.port}` : '')};Initial Catalog=${this.model.database};Integrated Security=True`;
|
||||
} else {
|
||||
let credentials = await sqlops.connection.getCredentials(this.model.server.connectionId);
|
||||
connectionString = `Data Source=${options.server + (options.port ? `,${options.port}` : '')};Initial Catalog=${this.model.database};Integrated Security=False;User Id=${options.user};Password=${credentials.password}`;
|
||||
}
|
||||
|
||||
// TODO: Fix this, it's returning undefined string.
|
||||
//await sqlops.connection.getConnectionString(this.model.server.connectionId, true);
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
// private async getCountRowsInserted(): Promise<Number> {
|
||||
// let connectionUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
|
||||
// let queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(this.model.server.providerName, sqlops.DataProviderType.QueryProvider);
|
||||
// try {
|
||||
// let query = sqlstring.format('USE ?; SELECT COUNT(*) FROM ?', [this.model.database, this.model.table]);
|
||||
// let results = await queryProvider.runQueryAndReturn(connectionUri, query);
|
||||
// let cell = results.rows[0][0];
|
||||
// if (!cell || cell.isNull) {
|
||||
// return -1;
|
||||
// }
|
||||
// let numericCell = Number(cell.displayValue);
|
||||
// if (isNaN(numericCell)) {
|
||||
// return -1;
|
||||
// }
|
||||
// return numericCell;
|
||||
// } catch (e) {
|
||||
// return -1;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
19
extensions/import/tsconfig.json
Normal file
19
extensions/import/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "./out",
|
||||
"lib": [
|
||||
"es6", "es2015.promise"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"declaration": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
409
extensions/import/yarn.lock
Normal file
409
extensions/import/yarn.lock
Normal file
@@ -0,0 +1,409 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
agent-base@4, agent-base@^4.1.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
applicationinsights@0.15.6:
|
||||
version "0.15.6"
|
||||
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-0.15.6.tgz#201a0682c0704fe4bdd9a92d0b2cbe34d2ae5972"
|
||||
|
||||
base64-js@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978"
|
||||
|
||||
bl@^1.0.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
|
||||
dependencies:
|
||||
readable-stream "^2.3.5"
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
buffer-alloc-unsafe@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
|
||||
|
||||
buffer-alloc@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
|
||||
dependencies:
|
||||
buffer-alloc-unsafe "^1.1.0"
|
||||
buffer-fill "^1.0.0"
|
||||
|
||||
buffer-crc32@~0.2.3:
|
||||
version "0.2.13"
|
||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
|
||||
buffer-fill@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
|
||||
|
||||
buffer@^3.0.1:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb"
|
||||
dependencies:
|
||||
base64-js "0.0.8"
|
||||
ieee754 "^1.1.4"
|
||||
isarray "^1.0.0"
|
||||
|
||||
commander@~2.8.1:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
|
||||
dependencies:
|
||||
graceful-readlink ">= 1.0.0"
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
|
||||
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/6aca9c6850d2b2e1e59f09d67e15922f5acec5ea"
|
||||
dependencies:
|
||||
vscode-languageclient "3.5.1"
|
||||
|
||||
debug@3.1.0, debug@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
|
||||
dependencies:
|
||||
file-type "^5.2.0"
|
||||
is-stream "^1.1.0"
|
||||
tar-stream "^1.5.2"
|
||||
|
||||
decompress-tarbz2@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
|
||||
dependencies:
|
||||
decompress-tar "^4.1.0"
|
||||
file-type "^6.1.0"
|
||||
is-stream "^1.1.0"
|
||||
seek-bzip "^1.0.5"
|
||||
unbzip2-stream "^1.0.9"
|
||||
|
||||
decompress-targz@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
|
||||
dependencies:
|
||||
decompress-tar "^4.1.1"
|
||||
file-type "^5.2.0"
|
||||
is-stream "^1.1.0"
|
||||
|
||||
decompress-unzip@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
|
||||
dependencies:
|
||||
file-type "^3.8.0"
|
||||
get-stream "^2.2.0"
|
||||
pify "^2.3.0"
|
||||
yauzl "^2.4.2"
|
||||
|
||||
decompress@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d"
|
||||
dependencies:
|
||||
decompress-tar "^4.0.0"
|
||||
decompress-tarbz2 "^4.0.0"
|
||||
decompress-targz "^4.0.0"
|
||||
decompress-unzip "^4.0.1"
|
||||
graceful-fs "^4.1.10"
|
||||
make-dir "^1.0.0"
|
||||
pify "^2.3.0"
|
||||
strip-dirs "^2.0.0"
|
||||
|
||||
end-of-stream@^1.0.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
es6-promise@^4.0.3:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
|
||||
|
||||
es6-promisify@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
|
||||
dependencies:
|
||||
es6-promise "^4.0.3"
|
||||
|
||||
eventemitter2@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452"
|
||||
|
||||
fd-slicer@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
file-type@^3.8.0:
|
||||
version "3.9.0"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
|
||||
|
||||
file-type@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
|
||||
|
||||
file-type@^6.1.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
|
||||
|
||||
fs-constants@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||
|
||||
get-stream@^2.2.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
|
||||
dependencies:
|
||||
object-assign "^4.0.1"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
graceful-fs@^4.1.10:
|
||||
version "4.1.11"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
|
||||
|
||||
"graceful-readlink@>= 1.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
|
||||
|
||||
http-proxy-agent@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
|
||||
dependencies:
|
||||
agent-base "4"
|
||||
debug "3.1.0"
|
||||
|
||||
https-proxy-agent@^2.1.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
|
||||
dependencies:
|
||||
agent-base "^4.1.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
ieee754@^1.1.4:
|
||||
version "1.1.12"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"
|
||||
|
||||
inherits@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
|
||||
is-natural-number@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
|
||||
|
||||
is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
|
||||
isarray@^1.0.0, isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
|
||||
make-dir@^1.0.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
|
||||
dependencies:
|
||||
pify "^3.0.0"
|
||||
|
||||
minimist@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
|
||||
mkdirp@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
||||
object-assign@^4.0.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
|
||||
once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
opener@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
|
||||
|
||||
os-tmpdir@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
|
||||
pend@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
|
||||
pify@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||
|
||||
pify@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||
|
||||
pinkie-promise@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
|
||||
dependencies:
|
||||
pinkie "^2.0.0"
|
||||
|
||||
pinkie@^2.0.0:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
|
||||
|
||||
readable-stream@^2.3.0, readable-stream@^2.3.5:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
|
||||
seek-bzip@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
|
||||
dependencies:
|
||||
commander "~2.8.1"
|
||||
|
||||
"service-downloader@github:anthonydresser/service-downloader#0.1.4":
|
||||
version "0.1.4"
|
||||
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/3c0abdf8603aca85d2eacfac3c547173e41bf0c7"
|
||||
dependencies:
|
||||
decompress "^4.2.0"
|
||||
eventemitter2 "^5.0.1"
|
||||
http-proxy-agent "^2.0.0"
|
||||
https-proxy-agent "^2.1.1"
|
||||
mkdirp "^0.5.1"
|
||||
tmp "^0.0.33"
|
||||
|
||||
string_decoder@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
strip-dirs@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
|
||||
dependencies:
|
||||
is-natural-number "^4.0.1"
|
||||
|
||||
tar-stream@^1.5.2:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
|
||||
dependencies:
|
||||
bl "^1.0.0"
|
||||
buffer-alloc "^1.1.0"
|
||||
end-of-stream "^1.0.0"
|
||||
fs-constants "^1.0.0"
|
||||
readable-stream "^2.3.0"
|
||||
to-buffer "^1.1.0"
|
||||
xtend "^4.0.0"
|
||||
|
||||
through@^2.3.6:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
dependencies:
|
||||
os-tmpdir "~1.0.2"
|
||||
|
||||
to-buffer@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
|
||||
|
||||
unbzip2-stream@^1.0.9:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz#73a033a567bbbde59654b193c44d48a7e4f43c47"
|
||||
dependencies:
|
||||
buffer "^3.0.1"
|
||||
through "^2.3.6"
|
||||
|
||||
util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
|
||||
vscode-extension-telemetry@^0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.5.tgz#21e2abb4cbce3326e469ddbb322123b3702f3f85"
|
||||
dependencies:
|
||||
applicationinsights "0.15.6"
|
||||
winreg "0.0.13"
|
||||
|
||||
vscode-jsonrpc@3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
|
||||
|
||||
vscode-languageclient@3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-3.5.1.tgz#c78e582459c24e58f88020dfa34065e976186a98"
|
||||
dependencies:
|
||||
vscode-languageserver-protocol "3.5.1"
|
||||
|
||||
vscode-languageserver-protocol@3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.1.tgz#5144a3a9eeccbd83fe2745bd4ed75fad6cc45f0d"
|
||||
dependencies:
|
||||
vscode-jsonrpc "3.5.0"
|
||||
vscode-languageserver-types "3.5.0"
|
||||
|
||||
vscode-languageserver-types@3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
winreg@0.0.13:
|
||||
version "0.0.13"
|
||||
resolved "https://registry.yarnpkg.com/winreg/-/winreg-0.0.13.tgz#76bfe02e1dd0c9c8275fb9fdf17a9f36846e3483"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
|
||||
xtend@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
||||
|
||||
yauzl@^2.4.2:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
dependencies:
|
||||
buffer-crc32 "~0.2.3"
|
||||
fd-slicer "~1.1.0"
|
||||
@@ -658,17 +658,11 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.0",
|
||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.5",
|
||||
"opener": "^1.4.3",
|
||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
|
||||
"vscode-extension-telemetry": "^0.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
"resolutions": {
|
||||
"vscode-jsonrpc": "3.5.0",
|
||||
"vscode-languageclient": "3.5.0",
|
||||
"vscode-languageserver-protocol": "3.5.0",
|
||||
"vscode-languageserver-types": "3.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||
"version": "1.5.0-alpha.14",
|
||||
"version": "1.5.0-alpha.22",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
||||
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
|
||||
agent-base@4, agent-base@^4.1.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce"
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
@@ -27,10 +27,25 @@ bl@^1.0.0:
|
||||
readable-stream "^2.3.5"
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
buffer-alloc-unsafe@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
|
||||
|
||||
buffer-alloc@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
|
||||
dependencies:
|
||||
buffer-alloc-unsafe "^1.1.0"
|
||||
buffer-fill "^1.0.0"
|
||||
|
||||
buffer-crc32@~0.2.3:
|
||||
version "0.2.13"
|
||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
|
||||
buffer-fill@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
|
||||
|
||||
buffer@^3.0.1:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb"
|
||||
@@ -49,11 +64,11 @@ core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
|
||||
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/c7a691c7fc5ad54a147090784a7f11efda301f4b"
|
||||
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/6aca9c6850d2b2e1e59f09d67e15922f5acec5ea"
|
||||
dependencies:
|
||||
vscode-languageclient "3.5.0"
|
||||
vscode-languageclient "3.5.1"
|
||||
|
||||
debug@3.1.0, debug@^3.1.0:
|
||||
version "3.1.0"
|
||||
@@ -139,9 +154,9 @@ eventemitter2@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452"
|
||||
|
||||
fd-slicer@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
|
||||
fd-slicer@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
@@ -157,6 +172,10 @@ file-type@^6.1.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
|
||||
|
||||
fs-constants@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||
|
||||
get-stream@^2.2.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
|
||||
@@ -180,15 +199,15 @@ http-proxy-agent@^2.0.0:
|
||||
debug "3.1.0"
|
||||
|
||||
https-proxy-agent@^2.1.1:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.0.tgz#7fbba856be8cd677986f42ebd3664f6317257887"
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
|
||||
dependencies:
|
||||
agent-base "^4.1.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
ieee754@^1.1.4:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
|
||||
version "1.1.12"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"
|
||||
|
||||
inherits@~2.0.3:
|
||||
version "2.0.3"
|
||||
@@ -207,8 +226,8 @@ isarray@^1.0.0, isarray@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
|
||||
make-dir@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b"
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
|
||||
dependencies:
|
||||
pify "^3.0.0"
|
||||
|
||||
@@ -270,21 +289,21 @@ process-nextick-args@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
|
||||
|
||||
readable-stream@^2.0.0, readable-stream@^2.3.5:
|
||||
version "2.3.5"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d"
|
||||
readable-stream@^2.3.0, readable-stream@^2.3.5:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.0.3"
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
|
||||
seek-bzip@^1.0.5:
|
||||
version "1.0.5"
|
||||
@@ -307,9 +326,9 @@ semver@^5.3.0:
|
||||
mkdirp "^0.5.1"
|
||||
tmp "^0.0.33"
|
||||
|
||||
string_decoder@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
|
||||
string_decoder@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
@@ -320,12 +339,15 @@ strip-dirs@^2.0.0:
|
||||
is-natural-number "^4.0.1"
|
||||
|
||||
tar-stream@^1.5.2:
|
||||
version "1.5.5"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.5.tgz#5cad84779f45c83b1f2508d96b09d88c7218af55"
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
|
||||
dependencies:
|
||||
bl "^1.0.0"
|
||||
buffer-alloc "^1.1.0"
|
||||
end-of-stream "^1.0.0"
|
||||
readable-stream "^2.0.0"
|
||||
fs-constants "^1.0.0"
|
||||
readable-stream "^2.3.0"
|
||||
to-buffer "^1.1.0"
|
||||
xtend "^4.0.0"
|
||||
|
||||
through@^2.3.6:
|
||||
@@ -338,6 +360,10 @@ tmp@^0.0.33:
|
||||
dependencies:
|
||||
os-tmpdir "~1.0.2"
|
||||
|
||||
to-buffer@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
|
||||
|
||||
unbzip2-stream@^1.0.9:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz#73a033a567bbbde59654b193c44d48a7e4f43c47"
|
||||
@@ -355,24 +381,24 @@ vscode-extension-telemetry@^0.0.15:
|
||||
dependencies:
|
||||
applicationinsights "1.0.1"
|
||||
|
||||
vscode-jsonrpc@3.5.0, vscode-jsonrpc@^3.5.0:
|
||||
vscode-jsonrpc@3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
|
||||
|
||||
vscode-languageclient@3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-3.5.0.tgz#36d02cc186a8365a4467719a290fb200a9ae490a"
|
||||
vscode-languageclient@3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-3.5.1.tgz#c78e582459c24e58f88020dfa34065e976186a98"
|
||||
dependencies:
|
||||
vscode-languageserver-protocol "^3.5.0"
|
||||
vscode-languageserver-protocol "3.5.1"
|
||||
|
||||
vscode-languageserver-protocol@3.5.0, vscode-languageserver-protocol@^3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0.tgz#067c5cbe27709795398d119692c97ebba1452209"
|
||||
vscode-languageserver-protocol@3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.1.tgz#5144a3a9eeccbd83fe2745bd4ed75fad6cc45f0d"
|
||||
dependencies:
|
||||
vscode-jsonrpc "^3.5.0"
|
||||
vscode-languageserver-types "^3.5.0"
|
||||
vscode-jsonrpc "3.5.0"
|
||||
vscode-languageserver-types "3.5.0"
|
||||
|
||||
vscode-languageserver-types@3.5.0, vscode-languageserver-types@^3.5.0:
|
||||
vscode-languageserver-types@3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374"
|
||||
|
||||
@@ -385,11 +411,11 @@ xtend@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
||||
|
||||
yauzl@^2.4.2:
|
||||
version "2.9.1"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f"
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
dependencies:
|
||||
buffer-crc32 "~0.2.3"
|
||||
fd-slicer "~1.0.1"
|
||||
fd-slicer "~1.1.0"
|
||||
|
||||
zone.js@0.7.6:
|
||||
version "0.7.6"
|
||||
|
||||
26
extensions/profiler/client/src/data/createSessionData.ts
Normal file
26
extensions/profiler/client/src/data/createSessionData.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
export class CreateSessionData {
|
||||
public ownerUri: string;
|
||||
public sessionName: string;
|
||||
public templates: Array<sqlops.ProfilerSessionTemplate> = new Array<sqlops.ProfilerSessionTemplate>();
|
||||
|
||||
constructor(ownerUri: string, templates: Array<sqlops.ProfilerSessionTemplate>) {
|
||||
this.ownerUri = ownerUri;
|
||||
this.templates = templates;
|
||||
}
|
||||
|
||||
public getTemplateNames(): string[] {
|
||||
return this.templates.map(e => e.name);
|
||||
}
|
||||
|
||||
public selectTemplate(name: string): sqlops.ProfilerSessionTemplate {
|
||||
return this.templates.find((t) => { return t.name === name; });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'vscode-nls';
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as vscode from 'vscode';
|
||||
import { CreateSessionData } from '../data/createSessionData';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class CreateSessionDialog {
|
||||
|
||||
// Top level
|
||||
private readonly DialogTitle: string = localize('createSessionDialog.newSession', 'New Session');
|
||||
private readonly CancelButtonText: string = localize('createSessionDialog.cancel', 'Cancel');
|
||||
private readonly CreateButtonText: string = localize('createSessionDialog.create', 'Create');
|
||||
private readonly DialogTitleText: string = localize('createSessionDialog.title', 'Create New Profiler Session');
|
||||
|
||||
// UI Components
|
||||
private dialog: sqlops.window.modelviewdialog.Dialog;
|
||||
private templatesBox: sqlops.DropDownComponent;
|
||||
private sessionNameBox: sqlops.InputBoxComponent;
|
||||
|
||||
private model: CreateSessionData;
|
||||
|
||||
private _onSuccess: vscode.EventEmitter<CreateSessionData> = new vscode.EventEmitter<CreateSessionData>();
|
||||
public readonly onSuccess: vscode.Event<CreateSessionData> = this._onSuccess.event;
|
||||
|
||||
|
||||
constructor(ownerUri: string, templates: Array<sqlops.ProfilerSessionTemplate>) {
|
||||
if (typeof (templates) === 'undefined' || templates === null) {
|
||||
throw new Error(localize('createSessionDialog.templatesInvalid', "Invalid templates list, cannot open dialog"));
|
||||
}
|
||||
if (typeof (ownerUri) === 'undefined' || ownerUri === null) {
|
||||
throw new Error(localize('createSessionDialog.dialogOwnerInvalid', "Invalid dialog owner, cannot open dialog"));
|
||||
}
|
||||
this.model = new CreateSessionData(ownerUri, templates);
|
||||
}
|
||||
|
||||
public async showDialog(): Promise<void> {
|
||||
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
|
||||
this.initializeContent();
|
||||
this.dialog.okButton.onClick(() => this.execute());
|
||||
this.dialog.cancelButton.onClick(() => { });
|
||||
this.dialog.okButton.label = this.CreateButtonText;
|
||||
this.dialog.cancelButton.label = this.CancelButtonText;
|
||||
|
||||
sqlops.window.modelviewdialog.openDialog(this.dialog);
|
||||
}
|
||||
|
||||
private initializeContent(): void {
|
||||
this.dialog.registerContent(async view => {
|
||||
this.templatesBox = view.modelBuilder.dropDown()
|
||||
.withProperties({
|
||||
values: []
|
||||
}).component();
|
||||
|
||||
this.sessionNameBox = view.modelBuilder.inputBox()
|
||||
.withProperties({
|
||||
required: true,
|
||||
multiline: false,
|
||||
value: ''
|
||||
}).component();
|
||||
|
||||
let formModel = view.modelBuilder.formContainer()
|
||||
.withFormItems([{
|
||||
components: [{
|
||||
component: this.templatesBox,
|
||||
title: localize('createSessionDialog.selectTemplates', "Select session template:")
|
||||
},
|
||||
{
|
||||
component: this.sessionNameBox,
|
||||
|
||||
title: localize('createSessionDialog.enterSessionName', "Enter session name:")
|
||||
}],
|
||||
title: this.DialogTitleText
|
||||
}]).withLayout({ width: '100%' }).component();
|
||||
|
||||
await view.initializeModel(formModel);
|
||||
|
||||
if (this.model.templates) {
|
||||
this.templatesBox.values = this.model.getTemplateNames();
|
||||
}
|
||||
|
||||
this.sessionNameBox.onTextChanged(() => {
|
||||
if (this.sessionNameBox.value.length > 0) {
|
||||
this.model.sessionName = this.sessionNameBox.value;
|
||||
this.dialog.okButton.enabled = true;
|
||||
} else {
|
||||
this.dialog.okButton.enabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async execute(): Promise<void> {
|
||||
let currentConnection = await sqlops.connection.getCurrentConnection();
|
||||
let profilerService = sqlops.dataprotocol.getProvider<sqlops.ProfilerProvider>(currentConnection.providerName, sqlops.DataProviderType.ProfilerProvider);
|
||||
|
||||
let name = this.sessionNameBox.value;
|
||||
let selected = this.templatesBox.value.toString();
|
||||
let temp = this.model.selectTemplate(selected);
|
||||
profilerService.createSession(this.model.ownerUri, this.sessionNameBox.value, temp);
|
||||
}
|
||||
}
|
||||
@@ -5,28 +5,33 @@
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as data from 'sqlops';
|
||||
import * as sqlops from 'sqlops';
|
||||
import { ApiWrapper } from './apiWrapper';
|
||||
import { CreateSessionDialog } from './dialogs/profilerCreateSessionDialog';
|
||||
|
||||
/**
|
||||
* The main controller class that initializes the extension
|
||||
*/
|
||||
export class MainController {
|
||||
protected _apiWrapper: ApiWrapper;
|
||||
protected _context: vscode.ExtensionContext;
|
||||
export class MainController {
|
||||
protected _apiWrapper: ApiWrapper;
|
||||
protected _context: vscode.ExtensionContext;
|
||||
|
||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||
public constructor(context: vscode.ExtensionContext, apiWrapper?: ApiWrapper) {
|
||||
this._apiWrapper = apiWrapper || new ApiWrapper();
|
||||
this._context = context;
|
||||
}
|
||||
// PUBLIC METHODS
|
||||
public constructor(context: vscode.ExtensionContext, apiWrapper?: ApiWrapper) {
|
||||
this._apiWrapper = apiWrapper || new ApiWrapper();
|
||||
this._context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates the extension
|
||||
*/
|
||||
public deactivate(): void {
|
||||
}
|
||||
public deactivate(): void {
|
||||
}
|
||||
|
||||
public activate(): void {
|
||||
}
|
||||
public activate(): void {
|
||||
vscode.commands.registerCommand('profiler.openCreateSessionDialog', (ownerUri: string, templates: Array<sqlops.ProfilerSessionTemplate>) => {
|
||||
let dialog = new CreateSessionDialog(ownerUri, templates);
|
||||
dialog.showDialog();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,5 @@
|
||||
|
||||
/// <reference path='../../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference path='../../../../../src/sql/sqlops.d.ts'/>
|
||||
/// <reference path='../../../../../src/sql/sqlops.proposed.d.ts'/>
|
||||
/// <reference types='@types/node'/>
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "profiler",
|
||||
"displayName": "SQL Server Profiler",
|
||||
"description": "SQL Server Profiler for SQL Operations Studio",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
||||
@@ -27,7 +27,7 @@
|
||||
],
|
||||
"contributes": {
|
||||
|
||||
"commands": [
|
||||
"commands": [
|
||||
{
|
||||
"command": "profiler.newProfiler",
|
||||
"title": "New Profiler",
|
||||
@@ -42,12 +42,20 @@
|
||||
"command": "profiler.stop",
|
||||
"title": "Stop",
|
||||
"category": "Profiler"
|
||||
},
|
||||
{
|
||||
"command": "profiler.openCreateSessionDialog",
|
||||
"title": "Create Profiler Session",
|
||||
"category": "Profiler"
|
||||
}
|
||||
],
|
||||
"outputChannels": [
|
||||
"sqlprofiler"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-nls": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vscode": "1.0.1"
|
||||
}
|
||||
|
||||
@@ -2035,6 +2035,10 @@ vinyl@~2.0.1:
|
||||
remove-trailing-separator "^1.0.1"
|
||||
replace-ext "^1.0.0"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398"
|
||||
|
||||
vscode@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.0.1.tgz#3d161200615fe2af1d92ddc650751159411a513b"
|
||||
|
||||
@@ -936,11 +936,11 @@
|
||||
},
|
||||
"_shell_light": {
|
||||
"fontCharacter": "\\E073",
|
||||
"fontColor": "#455155"
|
||||
"fontColor": "#bfc2c1"
|
||||
},
|
||||
"_shell": {
|
||||
"fontCharacter": "\\E073",
|
||||
"fontColor": "#4d5a5e"
|
||||
"fontColor": "#d4d7d6"
|
||||
},
|
||||
"_slim_light": {
|
||||
"fontCharacter": "\\E074",
|
||||
@@ -1374,7 +1374,9 @@
|
||||
"cmakelists.txt": "_makefile_3",
|
||||
"procfile": "_heroku",
|
||||
"todo": "_todo",
|
||||
"npm-debug.log": "_npm_ignored"
|
||||
"npm-debug.log": "_npm_ignored",
|
||||
"dashboard": "_shell",
|
||||
"profiler": "_csv"
|
||||
},
|
||||
"languageIds": {
|
||||
"bat": "_windows",
|
||||
@@ -1409,7 +1411,7 @@
|
||||
"rust": "_rust",
|
||||
"scss": "_sass",
|
||||
"shellscript": "_shell",
|
||||
"sql": "_db",
|
||||
"sql": "_default",
|
||||
"swift": "_swift",
|
||||
"typescript": "_typescript",
|
||||
"typescriptreact": "_react",
|
||||
@@ -1614,7 +1616,7 @@
|
||||
"rust": "_rust_light",
|
||||
"scss": "_sass_light",
|
||||
"shellscript": "_shell_light",
|
||||
"sql": "_db_light",
|
||||
"sql": "_default_light",
|
||||
"swift": "_swift_light",
|
||||
"typescript": "_typescript_light",
|
||||
"typescriptreact": "_react_light",
|
||||
@@ -1660,7 +1662,9 @@
|
||||
"omakefile": "_makefile_2_light",
|
||||
"cmakelists.txt": "_makefile_3_light",
|
||||
"procfile": "_heroku_light",
|
||||
"npm-debug.log": "_npm_ignored_light"
|
||||
"npm-debug.log": "_npm_ignored_light",
|
||||
"dashboard": "_shell_light",
|
||||
"profiler": "_csv_light"
|
||||
}
|
||||
},
|
||||
"version": "https://github.com/jesseweed/seti-ui/commit/188dda34a56b9555c7d363771264c24f4693983d"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sqlops",
|
||||
"version": "0.32.2",
|
||||
"version": "0.32.4",
|
||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
@@ -34,7 +34,7 @@
|
||||
"@angular/router": "~4.1.3",
|
||||
"@angular/upgrade": "~4.1.3",
|
||||
"angular2-grid": "2.0.6",
|
||||
"angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.3.12",
|
||||
"angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.4.1",
|
||||
"applicationinsights": "0.18.0",
|
||||
"chart.js": "^2.6.0",
|
||||
"fast-plist": "0.1.2",
|
||||
@@ -127,7 +127,7 @@
|
||||
"istanbul": "^0.3.17",
|
||||
"jsdom-no-contextify": "^3.1.0",
|
||||
"lazy.js": "^0.4.2",
|
||||
"mime": "1.2.11",
|
||||
"mime": "^1.4.1",
|
||||
"minimatch": "^2.0.10",
|
||||
"mkdirp": "^0.5.0",
|
||||
"mocha": "^2.2.5",
|
||||
@@ -149,7 +149,7 @@
|
||||
"underscore": "^1.8.3",
|
||||
"vinyl": "^0.4.5",
|
||||
"vinyl-fs": "^2.4.3",
|
||||
"vsce": "1.33.2",
|
||||
"vsce": "1.46.0",
|
||||
"vscode-nls-dev": "3.0.7"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"date": "2017-12-15T12:00:00.000Z",
|
||||
"recommendedExtensions": [
|
||||
"Microsoft.agent",
|
||||
"Microsoft.import",
|
||||
"Microsoft.profiler",
|
||||
"Microsoft.server-report",
|
||||
"Microsoft.whoisactive",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Name=@@NAME_LONG@@
|
||||
Comment=SQL Operations Studio
|
||||
Comment=Data Management Tool that enables you to work with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
||||
GenericName=Text Editor
|
||||
Exec=/usr/share/@@NAME@@/@@NAME@@ --unity-launch %F
|
||||
Icon=@@ICON@@
|
||||
|
||||
@@ -37,6 +37,10 @@ See [Paul Randal's wait types library] for more information about each wait type
|
||||
|
||||
We would like to thank all our users who raised issues, and in particular the following users who helped contribute fixes:
|
||||
* flyfishingdba for Add square brackets for ms_foreachdb call (#1023)
|
||||
* Peter-Schneider for Changed the stored procedure call to work on case sensitive instances (#1809)
|
||||
|
||||
## What's new in Server Reports v1.3?
|
||||
* Changed the stored procedure call to work on case sensitive instances
|
||||
|
||||
## What's new in Server Reports v1.2?
|
||||
* Created left nav bar and added 2 categories for insight widgets: monitor and performance
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "server-report",
|
||||
"displayName": "Server Reports",
|
||||
"description": "Server Reports",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"engines": {
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
"command": "sqlservices.openDialog",
|
||||
"title": "sqlservices.openDialog"
|
||||
},
|
||||
{
|
||||
"command": "sqlservices.openConnectionDialog",
|
||||
"title": "sqlservices.openConnectionDialog"
|
||||
},
|
||||
{
|
||||
"command": "sqlservices.openEditor",
|
||||
"title": "sqlservices.openEditor"
|
||||
@@ -100,7 +104,7 @@
|
||||
"sqlops": "github:anthonydresser/sqlops-extension-sqlops",
|
||||
"tslint": "^3.14.0",
|
||||
"typescript": "^2.6.1",
|
||||
"vscode": "^1.1.14",
|
||||
"vscode": "^1.1.18",
|
||||
"@types/handlebars": "^4.0.11",
|
||||
"vsce": "1.36.2"
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ import * as vscode from 'vscode';
|
||||
import SplitPropertiesPanel from './splitPropertiesPanel';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {TreeNode, TreeDataProvider} from './treeDataProvider';
|
||||
|
||||
/**
|
||||
* The main controller class that initializes the extension
|
||||
@@ -48,6 +49,13 @@ export default class MainController implements vscode.Disposable {
|
||||
this.openDialog();
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand('sqlservices.openConnectionDialog', async () => {
|
||||
let connection = await sqlops.connection.openConnectionDialog();
|
||||
if (connection) {
|
||||
console.info('Connection Opened: ' + connection.options['server']);
|
||||
}
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand('sqlservices.openEditor', () => {
|
||||
this.openEditor();
|
||||
});
|
||||
@@ -67,6 +75,77 @@ export default class MainController implements vscode.Disposable {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
private async getTab3Content(view: sqlops.ModelView): Promise<void> {
|
||||
let treeData = {
|
||||
label: '1',
|
||||
children: [
|
||||
{
|
||||
label: '11',
|
||||
id: '11',
|
||||
children: [
|
||||
{
|
||||
label: '111',
|
||||
id: '111',
|
||||
checked: false
|
||||
},
|
||||
{
|
||||
label: '112',
|
||||
id: '112',
|
||||
children: [
|
||||
{
|
||||
label: '1121',
|
||||
id: '1121',
|
||||
checked: true
|
||||
},
|
||||
{
|
||||
label: '1122',
|
||||
id: '1122',
|
||||
checked: false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '12',
|
||||
id: '12',
|
||||
checked: true
|
||||
}
|
||||
],
|
||||
id: '1'
|
||||
};
|
||||
let root = TreeNode.createTree(treeData);
|
||||
|
||||
let treeDataProvider = new TreeDataProvider(root);
|
||||
|
||||
let tree: sqlops.TreeComponent<TreeNode> = view.modelBuilder.tree<TreeNode>().withProperties({
|
||||
'withCheckbox': true
|
||||
}).component();
|
||||
let treeView = tree.registerDataProvider(treeDataProvider);
|
||||
treeView.onNodeCheckedChanged(item => {
|
||||
if (item && item.element) {
|
||||
item.element.changeNodeCheckedState(item.checked);
|
||||
}
|
||||
});
|
||||
treeView.onDidChangeSelection(selectedNodes => {
|
||||
selectedNodes.forEach(node => {
|
||||
console.info('tree node selected: ' + node.label);
|
||||
});
|
||||
});
|
||||
let formModel = view.modelBuilder.formContainer()
|
||||
.withFormItems([{
|
||||
component: tree,
|
||||
title: 'Tree'
|
||||
}], {
|
||||
horizontal: false,
|
||||
componentWidth: 800,
|
||||
componentHeight: 800
|
||||
}).component();
|
||||
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
||||
formWrapper.loading = false;
|
||||
|
||||
await view.initializeModel(formWrapper);
|
||||
}
|
||||
private async getTabContent(view: sqlops.ModelView, customButton1: sqlops.window.modelviewdialog.Button, customButton2: sqlops.window.modelviewdialog.Button, componentWidth: number | string): Promise<void> {
|
||||
let inputBox = view.modelBuilder.inputBox()
|
||||
.withProperties({
|
||||
@@ -88,7 +167,7 @@ export default class MainController implements vscode.Disposable {
|
||||
})
|
||||
.component();
|
||||
checkbox.onChanged(e => {
|
||||
console.info("inputBox.enabled " + inputBox.enabled);
|
||||
console.info('inputBox.enabled ' + inputBox.enabled);
|
||||
inputBox.enabled = !inputBox.enabled;
|
||||
});
|
||||
let button = view.modelBuilder.button()
|
||||
@@ -271,8 +350,9 @@ export default class MainController implements vscode.Disposable {
|
||||
let tab1 = sqlops.window.modelviewdialog.createTab('Test tab 1');
|
||||
|
||||
let tab2 = sqlops.window.modelviewdialog.createTab('Test tab 2');
|
||||
let tab3 = sqlops.window.modelviewdialog.createTab('Test tab 3');
|
||||
tab2.content = 'sqlservices';
|
||||
dialog.content = [tab1, tab2];
|
||||
dialog.content = [tab1, tab2, tab3];
|
||||
dialog.okButton.onClick(() => console.log('ok clicked!'));
|
||||
dialog.cancelButton.onClick(() => console.log('cancel clicked!'));
|
||||
dialog.okButton.label = 'ok';
|
||||
@@ -285,6 +365,10 @@ export default class MainController implements vscode.Disposable {
|
||||
tab1.registerContent(async (view) => {
|
||||
await this.getTabContent(view, customButton1, customButton2, 400);
|
||||
});
|
||||
|
||||
tab3.registerContent(async (view) => {
|
||||
await this.getTab3Content(view);
|
||||
});
|
||||
sqlops.window.modelviewdialog.openDialog(dialog);
|
||||
}
|
||||
|
||||
@@ -301,18 +385,20 @@ export default class MainController implements vscode.Disposable {
|
||||
page1.registerContent(async (view) => {
|
||||
await this.getTabContent(view, customButton1, customButton2, 800);
|
||||
});
|
||||
/*
|
||||
|
||||
wizard.registerOperation({
|
||||
displayName: 'test task',
|
||||
description: 'task description',
|
||||
isCancelable: true
|
||||
}, op => {
|
||||
op.updateStatus(sqlops.TaskStatus.InProgress);
|
||||
op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running');
|
||||
setTimeout(() => {
|
||||
op.updateStatus(sqlops.TaskStatus.Succeeded);
|
||||
}, 5000);
|
||||
});*/
|
||||
isCancelable: true,
|
||||
connection: undefined,
|
||||
operation: op => {
|
||||
op.updateStatus(sqlops.TaskStatus.InProgress);
|
||||
op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running');
|
||||
setTimeout(() => {
|
||||
op.updateStatus(sqlops.TaskStatus.Succeeded);
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
wizard.pages = [page1, page2];
|
||||
wizard.open();
|
||||
}
|
||||
|
||||
303
samples/sqlservices/src/controllers/treeDataProvider.ts
Normal file
303
samples/sqlservices/src/controllers/treeDataProvider.ts
Normal file
@@ -0,0 +1,303 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as sqlops from 'sqlops';
|
||||
import * as path from 'path';
|
||||
|
||||
export enum TreeCheckboxState {
|
||||
Intermediate = 0,
|
||||
Checked = 1,
|
||||
Unchecked = 2
|
||||
}
|
||||
|
||||
export interface TreeComponentDataModel {
|
||||
label?: string;
|
||||
children?: TreeComponentDataModel[];
|
||||
id?: string;
|
||||
checked?: boolean;
|
||||
}
|
||||
|
||||
export class TreeNode implements sqlops.TreeComponentItem {
|
||||
private _onNodeChange = new vscode.EventEmitter<void>();
|
||||
private _onTreeChange = new vscode.EventEmitter<TreeNode>();
|
||||
private _data: TreeComponentDataModel;
|
||||
private _parent?: TreeNode;
|
||||
private _root: TreeNode;
|
||||
private _isAlwaysLeaf: boolean;
|
||||
private _nodeMap: Map<string, TreeNode>;
|
||||
private _children: TreeNode[];
|
||||
|
||||
public readonly onNodeChange: vscode.Event<void> = this._onNodeChange.event;
|
||||
public readonly onTreeChange: vscode.Event<TreeNode> = this._onTreeChange.event;
|
||||
|
||||
|
||||
/**
|
||||
* Creates new instance of tree node
|
||||
* @param data the underlining data that's bind to the tree node, any change in the tree will affect the same node in data
|
||||
* @param root the root node of the tree. If passed null, the current node will be the root
|
||||
*/
|
||||
constructor(data: TreeComponentDataModel, root: TreeNode) {
|
||||
if (!data) {
|
||||
throw new Error(`Invalid tree node data`);
|
||||
}
|
||||
if (root === undefined) {
|
||||
root = this;
|
||||
root._nodeMap = new Map<string, TreeNode>();
|
||||
}
|
||||
|
||||
this._root = root;
|
||||
if (this.findNode(data.id)) {
|
||||
throw new Error(`tree node with id: '${data.id}' already exists`);
|
||||
}
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* id for TreeNode
|
||||
*/
|
||||
public get id(): string {
|
||||
return this.data.id;
|
||||
}
|
||||
|
||||
public set id(value: string) {
|
||||
this.data.id = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Label to display to the user, describing this node
|
||||
*/
|
||||
public set label(value: string) {
|
||||
this.data.label = value;
|
||||
}
|
||||
|
||||
public get collapsibleState(): vscode.TreeItemCollapsibleState {
|
||||
if (!this._isAlwaysLeaf) {
|
||||
return vscode.TreeItemCollapsibleState.Expanded;
|
||||
} else {
|
||||
vscode.TreeItemCollapsibleState.None;
|
||||
}
|
||||
}
|
||||
|
||||
public get label(): string {
|
||||
return this.data.label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a leaf node (in which case no children can be generated) or is it expandable?
|
||||
*/
|
||||
public get isAlwaysLeaf(): boolean {
|
||||
return this._isAlwaysLeaf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent of this node
|
||||
*/
|
||||
public get parent(): TreeNode {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
public get root(): TreeNode {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Path identifying this node
|
||||
*/
|
||||
public get nodePath(): string {
|
||||
return `${this.parent ? this.parent.nodePath + '-' : ''}${this.id}`;
|
||||
}
|
||||
|
||||
public get data(): TreeComponentDataModel {
|
||||
if (this._data === undefined) {
|
||||
this._data = {
|
||||
label: undefined
|
||||
};
|
||||
}
|
||||
return this._data;
|
||||
}
|
||||
|
||||
public changeNodeCheckedState(value: boolean, fromParent?: boolean): void {
|
||||
if (value !== this.checked) {
|
||||
if (value !== undefined && this.children) {
|
||||
this.children.forEach(child => {
|
||||
child.changeNodeCheckedState(value, true);
|
||||
});
|
||||
}
|
||||
|
||||
this.checked = value;
|
||||
if (!fromParent && this.parent) {
|
||||
this.parent.refreshState();
|
||||
}
|
||||
|
||||
this.onValueChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public set checked(value: boolean) {
|
||||
this.data.checked = value;
|
||||
}
|
||||
|
||||
public refreshState(): void {
|
||||
if (this.hasChildren) {
|
||||
if (this.children.every(c => c.checked)) {
|
||||
this.changeNodeCheckedState(true);
|
||||
} else if (this.children.every(c => c.checked !== undefined && !c.checked)) {
|
||||
this.changeNodeCheckedState(false);
|
||||
} else {
|
||||
this.changeNodeCheckedState(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get hasChildren(): boolean {
|
||||
return this.children !== undefined && this.children.length > 0;
|
||||
}
|
||||
|
||||
public get checked(): boolean {
|
||||
return this.data.checked;
|
||||
}
|
||||
|
||||
private onValueChanged(): void {
|
||||
this._onNodeChange.fire();
|
||||
if (this.root) {
|
||||
this.root._onTreeChange.fire(this);
|
||||
}
|
||||
}
|
||||
|
||||
public get checkboxState(): TreeCheckboxState {
|
||||
if (this.checked === undefined) {
|
||||
return TreeCheckboxState.Intermediate;
|
||||
} else {
|
||||
return this.checked ? TreeCheckboxState.Checked : TreeCheckboxState.Unchecked;
|
||||
}
|
||||
}
|
||||
|
||||
public findNode(id: string): TreeNode {
|
||||
if (this.id === id) {
|
||||
return this;
|
||||
} else if (this.root) {
|
||||
return this.root._nodeMap.has(id) ? this.root._nodeMap.get(id) : undefined;
|
||||
} else {
|
||||
let node: TreeNode;
|
||||
if (this.children) {
|
||||
this.children.forEach(child => {
|
||||
node = child.findNode(id);
|
||||
if (node) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Children of this node
|
||||
*/
|
||||
public get children(): TreeNode[] {
|
||||
return this._children;
|
||||
}
|
||||
|
||||
public addChildNode(node: TreeNode): void {
|
||||
|
||||
if (node) {
|
||||
if (!node.root) {
|
||||
node._root = this.root;
|
||||
}
|
||||
if (!node.parent) {
|
||||
node._parent = this;
|
||||
}
|
||||
if (node.root) {
|
||||
node.root._nodeMap.set(node.id, node);
|
||||
}
|
||||
this._children.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
public static createNode(nodeData: TreeComponentDataModel, parent?: TreeNode, root?: TreeNode): TreeNode {
|
||||
let rootNode = root || (parent !== undefined ? parent.root : undefined);
|
||||
let treeNode = new TreeNode(nodeData, rootNode);
|
||||
|
||||
treeNode._parent = parent;
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
public static createTree(nodeData: TreeComponentDataModel, parent?: TreeNode, root?: TreeNode): TreeNode {
|
||||
if (nodeData) {
|
||||
let treeNode = TreeNode.createNode(nodeData, parent, root);
|
||||
|
||||
if (nodeData.children && nodeData.children.length > 0) {
|
||||
treeNode._isAlwaysLeaf = false;
|
||||
treeNode._children = [];
|
||||
nodeData.children.forEach(childNode => {
|
||||
if (childNode) {
|
||||
let childTreeNode = TreeNode.createTree(childNode, treeNode, root || treeNode.root);
|
||||
treeNode.addChildNode(childTreeNode);
|
||||
}
|
||||
});
|
||||
treeNode.refreshState();
|
||||
} else {
|
||||
treeNode._isAlwaysLeaf = true;
|
||||
}
|
||||
return treeNode;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TreeDataProvider implements sqlops.TreeComponentDataProvider<TreeNode> {
|
||||
private _onDidChangeTreeData = new vscode.EventEmitter<TreeNode>();
|
||||
constructor(private _root: TreeNode) {
|
||||
if(this._root) {
|
||||
this._root.onTreeChange(node => {
|
||||
this._onDidChangeTreeData.fire(node);
|
||||
});
|
||||
}
|
||||
}
|
||||
onDidChangeTreeData?: vscode.Event<TreeNode | undefined | null> = this._onDidChangeTreeData.event ;
|
||||
|
||||
/**
|
||||
* Get [TreeItem](#TreeItem) representation of the `element`
|
||||
*
|
||||
* @param element The element for which [TreeItem](#TreeItem) representation is asked for.
|
||||
* @return [TreeItem](#TreeItem) representation of the element
|
||||
*/
|
||||
getTreeItem(element: TreeNode): sqlops.TreeComponentItem | Thenable<sqlops.TreeComponentItem> {
|
||||
let item: sqlops.TreeComponentItem = {};
|
||||
item.label = element.label;
|
||||
item.checked = element.checked;
|
||||
item.collapsibleState = element.collapsibleState;
|
||||
item.iconPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg'));
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the children of `element` or root if no element is passed.
|
||||
*
|
||||
* @param element The element from which the provider gets children. Can be `undefined`.
|
||||
* @return Children of `element` or root if no element is passed.
|
||||
*/
|
||||
getChildren(element?: TreeNode): vscode.ProviderResult<TreeNode[]> {
|
||||
if (element) {
|
||||
return Promise.resolve(element.children);
|
||||
} else {
|
||||
return Promise.resolve(this._root.children);
|
||||
}
|
||||
}
|
||||
|
||||
getParent(element?: TreeNode): vscode.ProviderResult<TreeNode> {
|
||||
if (element) {
|
||||
return Promise.resolve(element.parent);
|
||||
} else {
|
||||
return Promise.resolve(this._root);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -233,8 +233,8 @@ export class Dropdown extends Disposable {
|
||||
this._layoutTree();
|
||||
return { dispose: () => { } };
|
||||
},
|
||||
onDOMEvent: (e, activeElement) => {
|
||||
if (!DOM.isAncestor(activeElement, this.$el.getHTMLElement()) && !DOM.isAncestor(activeElement, this.$treeContainer.getHTMLElement())) {
|
||||
onDOMEvent: e => {
|
||||
if (!DOM.isAncestor(e.srcElement, this.$el.getHTMLElement()) && !DOM.isAncestor(e.srcElement, this.$treeContainer.getHTMLElement())) {
|
||||
this._input.validate();
|
||||
this._onBlur.fire();
|
||||
this._contextView.hide();
|
||||
|
||||
@@ -228,7 +228,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
}
|
||||
|
||||
// The builder builds the dialog. It append header, body and footer sections.
|
||||
this._builder = $().div({ class: builderClass, 'role': 'dialog' }, (dialogContainer) => {
|
||||
this._builder = $().div({ class: builderClass, 'role': 'dialog', 'aria-label': this._title }, (dialogContainer) => {
|
||||
this._modalDialog = dialogContainer.div({ class: 'modal-dialog ', role: 'document' }, (modalDialog) => {
|
||||
modalDialog.div({ class: 'modal-content' }, (modelContent) => {
|
||||
parts.forEach((part) => {
|
||||
@@ -357,6 +357,21 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a footer button matching the provided label
|
||||
* @param label Label to show on the button
|
||||
* @param onSelect The callback to call when the button is selected
|
||||
*/
|
||||
protected findFooterButton(label: string): Button {
|
||||
return this._footerButtons.find(e => {
|
||||
try {
|
||||
return e && e.element.innerText === label;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an error in the error message element
|
||||
* @param err Text to show in the error message
|
||||
|
||||
@@ -84,6 +84,7 @@ export class SelectBox extends vsSelectBox {
|
||||
this.inputValidationWarningBackground = styles.inputValidationWarningBackground;
|
||||
this.inputValidationErrorBorder = styles.inputValidationErrorBorder;
|
||||
this.inputValidationErrorBackground = styles.inputValidationErrorBackground;
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
public selectWithOptionName(optionName: string): void {
|
||||
|
||||
@@ -63,7 +63,9 @@ export class RowNumberColumn<T> implements Slick.Plugin<T> {
|
||||
|
||||
private formatter(row, cell, value, columnDef: Slick.Column<T>, dataContext): string {
|
||||
if (dataContext) {
|
||||
return `<span>${row}</span>`;
|
||||
// row is zero-based, we need make it 1 based for display in the result grid
|
||||
//
|
||||
return `<span>${row + 1}</span>`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
1
src/sql/base/browser/ui/taskbar/media/add.svg
Normal file
1
src/sql/base/browser/ui/taskbar/media/add.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>add</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,16H0V0H16Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14,6v4H10v4H6V10H2V6H6V2h4V6Z"/></g><g id="iconBg"><path class="icon-vs-bg" d="M13,7V9H9v4H7V9H3V7H7V3H9V7Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 486 B |
@@ -18,6 +18,12 @@
|
||||
background-image: url('start.svg');
|
||||
}
|
||||
|
||||
.vs .icon.add,
|
||||
.vs-dark .icon.add,
|
||||
.hc-black .icon.add {
|
||||
background-image: url('add.svg');
|
||||
}
|
||||
|
||||
.vs .icon.stop,
|
||||
.vs-dark .icon.stop,
|
||||
.hc-black .icon.stop {
|
||||
|
||||
@@ -42,7 +42,10 @@ export function resolveFilePath(uri: string, filePath: string, rootPath: string)
|
||||
export function getRootPath(contextService: IWorkspaceContextService): string {
|
||||
let isWorkspace = contextService.getWorkbenchState() === WorkbenchState.WORKSPACE;
|
||||
if (isWorkspace) {
|
||||
return contextService.getWorkspace().folders[0].uri.fsPath;
|
||||
let folder = contextService.getWorkspace().folders[0];
|
||||
if (folder && folder.uri) {
|
||||
return folder.uri.fsPath;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IExtensionGalleryService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions';
|
||||
import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
@@ -13,10 +13,9 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { DashboardEditor } from 'sql/parts/dashboard/dashboardEditor';
|
||||
import { DashboardInput } from 'sql/parts/dashboard/dashboardInput';
|
||||
import { AddServerGroupAction, AddServerAction } from 'sql/parts/objectExplorer/viewlet/connectionTreeAction';
|
||||
import { ClearRecentConnectionsAction } from 'sql/parts/connection/common/connectionActions';
|
||||
import { ClearRecentConnectionsAction, GetCurrentConnectionStringAction } from 'sql/parts/connection/common/connectionActions';
|
||||
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
|
||||
import { EditorDescriptor } from 'vs/workbench/browser/editor';
|
||||
import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService';
|
||||
import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService';
|
||||
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
|
||||
@@ -49,6 +48,7 @@ actionRegistry.registerWorkbenchAction(
|
||||
),
|
||||
ClearRecentConnectionsAction.LABEL
|
||||
);
|
||||
|
||||
actionRegistry.registerWorkbenchAction(
|
||||
new SyncActionDescriptor(
|
||||
AddServerGroupAction,
|
||||
@@ -67,6 +67,15 @@ actionRegistry.registerWorkbenchAction(
|
||||
AddServerAction.LABEL
|
||||
);
|
||||
|
||||
actionRegistry.registerWorkbenchAction(
|
||||
new SyncActionDescriptor(
|
||||
GetCurrentConnectionStringAction,
|
||||
GetCurrentConnectionStringAction.ID,
|
||||
GetCurrentConnectionStringAction.LABEL
|
||||
),
|
||||
GetCurrentConnectionStringAction.LABEL
|
||||
);
|
||||
|
||||
let configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigExtensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': 'connection',
|
||||
|
||||
@@ -13,6 +13,11 @@ import { IConnectionManagementService } from 'sql/parts/connection/common/connec
|
||||
import { INotificationService, INotificationActions } from 'vs/platform/notification/common/notification';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IObjectExplorerService } from '../../objectExplorer/common/objectExplorerService';
|
||||
import { QueryInput } from 'sql/parts/query/common/queryInput';
|
||||
import { EditDataInput } from 'sql/parts/editData/common/editDataInput';
|
||||
import { DashboardInput } from 'sql/parts/dashboard/dashboardInput';
|
||||
|
||||
/**
|
||||
* Workbench action to clear the recent connnections list
|
||||
@@ -126,4 +131,44 @@ export class ClearSingleRecentConnectionAction extends Action {
|
||||
this._onRecentConnectionRemoved.fire();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to retrieve the current connection string
|
||||
*/
|
||||
export class GetCurrentConnectionStringAction extends Action {
|
||||
|
||||
public static ID = 'getCurrentConnectionStringAction';
|
||||
public static LABEL = nls.localize('connectionAction.GetCurrentConnectionString', "Get Current Connection String");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IWorkbenchEditorService private _editorService: IWorkbenchEditorService,
|
||||
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
|
||||
@INotificationService private readonly _notificationService: INotificationService
|
||||
) {
|
||||
super(GetCurrentConnectionStringAction.ID, GetCurrentConnectionStringAction.LABEL);
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
public run(): TPromise<void> {
|
||||
return new TPromise<void>((resolve, reject) => {
|
||||
let activeInput = this._editorService.getActiveEditorInput();
|
||||
if (activeInput && (activeInput instanceof QueryInput || activeInput instanceof EditDataInput || activeInput instanceof DashboardInput)
|
||||
&& this._connectionManagementService.isConnected(activeInput.uri)) {
|
||||
let includePassword = false;
|
||||
this._connectionManagementService.getConnectionString(activeInput.uri, includePassword).then(result => {
|
||||
let message = result
|
||||
? result
|
||||
: nls.localize('connectionAction.connectionString', "Connection string not available");
|
||||
this._notificationService.info(message);
|
||||
});
|
||||
} else {
|
||||
let message = nls.localize('connectionAction.noConnection', "No active connection available");
|
||||
this._notificationService.info(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ export interface IConnectionResult {
|
||||
errorCode: number;
|
||||
callStack: string;
|
||||
errorHandled?: boolean;
|
||||
connectionProfile?: IConnectionProfile;
|
||||
}
|
||||
|
||||
export interface IConnectionCallbacks {
|
||||
@@ -130,15 +131,15 @@ export interface IConnectionManagementService {
|
||||
|
||||
onConnectionChangedNotification(handle: number, changedConnInfo: sqlops.ChangedConnectionInfo);
|
||||
|
||||
getConnectionGroups(): ConnectionProfileGroup[];
|
||||
getConnectionGroups(providers?: string[]): ConnectionProfileGroup[];
|
||||
|
||||
getRecentConnections(): ConnectionProfile[];
|
||||
getRecentConnections(providers?: string[]): ConnectionProfile[];
|
||||
|
||||
clearRecentConnectionsList(): void;
|
||||
|
||||
clearRecentConnection(connectionProfile: IConnectionProfile): void;
|
||||
|
||||
getActiveConnections(): ConnectionProfile[];
|
||||
getActiveConnections(providers?: string[]): ConnectionProfile[];
|
||||
|
||||
saveProfileGroup(profile: IConnectionProfileGroup): Promise<string>;
|
||||
|
||||
@@ -260,12 +261,34 @@ export interface IConnectionManagementService {
|
||||
* in the connection profile's options dictionary, or undefined if the profile is not connected
|
||||
*/
|
||||
getActiveConnectionCredentials(profileId: string): { [name: string]: string };
|
||||
|
||||
/**
|
||||
* Get the connection string for the provided connection profile
|
||||
*/
|
||||
getConnectionString(ownerUri: string, includePassword: boolean): Thenable<string>;
|
||||
}
|
||||
|
||||
export const IConnectionDialogService = createDecorator<IConnectionDialogService>('connectionDialogService');
|
||||
export interface IConnectionDialogService {
|
||||
_serviceBrand: any;
|
||||
/**
|
||||
* Opens the connection dialog and returns the promise for successfully opening the dialog
|
||||
* @param connectionManagementService
|
||||
* @param params
|
||||
* @param model
|
||||
* @param connectionResult
|
||||
*/
|
||||
showDialog(connectionManagementService: IConnectionManagementService, params: INewConnectionParams, model: IConnectionProfile, connectionResult?: IConnectionResult): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Opens the connection dialog and returns the promise when connection is made
|
||||
* or dialog is closed
|
||||
* @param connectionManagementService
|
||||
* @param params
|
||||
* @param model
|
||||
* @param connectionResult
|
||||
*/
|
||||
openDialogAndWait(connectionManagementService: IConnectionManagementService, params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Thenable<IConnectionProfile>;
|
||||
}
|
||||
|
||||
export interface IServerGroupDialogCallbacks {
|
||||
@@ -299,6 +322,7 @@ export interface INewConnectionParams {
|
||||
runQueryOnCompletion?: RunQueryOnConnectionMode;
|
||||
querySelection?: sqlops.ISelectionData;
|
||||
showDashboard?: boolean;
|
||||
providers?: string[];
|
||||
}
|
||||
|
||||
export interface IConnectableInput {
|
||||
|
||||
@@ -627,12 +627,12 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
}
|
||||
}
|
||||
|
||||
public getConnectionGroups(): ConnectionProfileGroup[] {
|
||||
return this._connectionStore.getConnectionProfileGroups();
|
||||
public getConnectionGroups(providers?: string[]): ConnectionProfileGroup[] {
|
||||
return this._connectionStore.getConnectionProfileGroups(false, providers);
|
||||
}
|
||||
|
||||
public getRecentConnections(): ConnectionProfile[] {
|
||||
return this._connectionStore.getRecentlyUsedConnections();
|
||||
public getRecentConnections(providers?: string[]): ConnectionProfile[] {
|
||||
return this._connectionStore.getRecentlyUsedConnections(providers);
|
||||
}
|
||||
|
||||
|
||||
@@ -644,7 +644,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
this._connectionStore.removeConnectionToMemento(connectionProfile, Constants.recentConnections);
|
||||
}
|
||||
|
||||
public getActiveConnections(): ConnectionProfile[] {
|
||||
public getActiveConnections(providers?: string[]): ConnectionProfile[] {
|
||||
return this._connectionStatusManager.getActiveConnectionProfiles();
|
||||
}
|
||||
|
||||
@@ -1002,14 +1002,14 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
let connectionMngInfo = this._connectionStatusManager.findConnection(uri);
|
||||
if (connectionMngInfo && connectionMngInfo.deleted) {
|
||||
this._connectionStatusManager.deleteConnection(uri);
|
||||
resolve({ connected: connectResult, errorMessage: undefined, errorCode: undefined, callStack: undefined, errorHandled: true });
|
||||
resolve({ connected: connectResult, errorMessage: undefined, errorCode: undefined, callStack: undefined, errorHandled: true, connectionProfile: connection });
|
||||
} else {
|
||||
if (errorMessage) {
|
||||
// Connection to the server failed
|
||||
this._connectionStatusManager.deleteConnection(uri);
|
||||
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack });
|
||||
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack, connectionProfile: connection });
|
||||
} else {
|
||||
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack });
|
||||
resolve({ connected: connectResult, errorMessage: errorMessage, errorCode: errorCode, callStack: callStack, connectionProfile: connection });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1120,7 +1120,8 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
resolve(result);
|
||||
});
|
||||
} else {
|
||||
resolve(self.disconnectEditor(owner));
|
||||
// If the editor is connected then there is nothing to cancel
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1345,4 +1346,24 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
credentials[passwordOption.name] = profile.options[passwordOption.name];
|
||||
return credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connection string for the provided connection profile
|
||||
*/
|
||||
public getConnectionString(ownerUri: string, includePassword: boolean = false): Thenable<string> {
|
||||
if (!ownerUri) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
let providerId = this.getProviderIdFromUri(ownerUri);
|
||||
if (!providerId) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return this._providers.get(providerId).onReady.then(provider => {
|
||||
return provider.getConnectionString(ownerUri, includePassword).then(connectionString => {
|
||||
return connectionString;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,9 +193,14 @@ export class ConnectionStatusManager {
|
||||
/**
|
||||
* Get a list of the active connection profiles managed by the status manager
|
||||
*/
|
||||
public getActiveConnectionProfiles(): ConnectionProfile[] {
|
||||
public getActiveConnectionProfiles(providers?: string[]): ConnectionProfile[] {
|
||||
let profiles = Object.values(this._connections).map((connectionInfo: ConnectionManagementInfo) => connectionInfo.connectionProfile);
|
||||
// Remove duplicate profiles that may be listed multiple times under different URIs by filtering for profiles that don't have the same ID as an earlier profile in the list
|
||||
return profiles.filter((profile, index) => profiles.findIndex(otherProfile => otherProfile.id === profile.id) === index);
|
||||
profiles = profiles.filter((profile, index) => profiles.findIndex(otherProfile => otherProfile.id === profile.id) === index);
|
||||
|
||||
if (providers) {
|
||||
profiles = profiles.filter(f => providers.includes(f.providerName));
|
||||
}
|
||||
return profiles;
|
||||
}
|
||||
}
|
||||
@@ -213,13 +213,16 @@ export class ConnectionStore {
|
||||
*
|
||||
* @returns {sqlops.ConnectionInfo} the array of connections, empty if none are found
|
||||
*/
|
||||
public getRecentlyUsedConnections(): ConnectionProfile[] {
|
||||
public getRecentlyUsedConnections(providers?: string[]): ConnectionProfile[] {
|
||||
let configValues: IConnectionProfile[] = this._memento[Constants.recentConnections];
|
||||
if (!configValues) {
|
||||
configValues = [];
|
||||
}
|
||||
|
||||
configValues = configValues.filter(c => !!(c));
|
||||
if (providers && providers.length > 0) {
|
||||
configValues = configValues.filter(c => providers.includes(c.providerName));
|
||||
}
|
||||
return this.convertConfigValuesToConnectionProfiles(configValues);
|
||||
}
|
||||
|
||||
@@ -429,10 +432,13 @@ export class ConnectionStore {
|
||||
});
|
||||
}
|
||||
|
||||
public getConnectionProfileGroups(withoutConnections?: boolean): ConnectionProfileGroup[] {
|
||||
public getConnectionProfileGroups(withoutConnections?: boolean, providers?: string[]): ConnectionProfileGroup[] {
|
||||
let profilesInConfiguration: ConnectionProfile[];
|
||||
if (!withoutConnections) {
|
||||
profilesInConfiguration = this._connectionConfig.getConnections(true);
|
||||
if (providers && providers.length > 0) {
|
||||
profilesInConfiguration = profilesInConfiguration.filter(x => providers.includes(x.providerName));
|
||||
}
|
||||
}
|
||||
let groups = this._connectionConfig.getAllGroups();
|
||||
|
||||
|
||||
@@ -132,8 +132,8 @@ export class ConnectionController implements IConnectionComponentController {
|
||||
}
|
||||
}
|
||||
|
||||
private getAllServerGroups(): IConnectionProfileGroup[] {
|
||||
var connectionGroupRoot = this._connectionManagementService.getConnectionGroups();
|
||||
private getAllServerGroups(providers?: string[]): IConnectionProfileGroup[] {
|
||||
var connectionGroupRoot = this._connectionManagementService.getConnectionGroups(providers);
|
||||
var connectionGroupNames: IConnectionProfileGroup[] = [];
|
||||
if (connectionGroupRoot && connectionGroupRoot.length > 0) {
|
||||
this.getServerGroupHelper(connectionGroupRoot[0], connectionGroupNames);
|
||||
@@ -149,8 +149,8 @@ export class ConnectionController implements IConnectionComponentController {
|
||||
return connectionGroupNames;
|
||||
}
|
||||
|
||||
public initDialog(connectionInfo: IConnectionProfile): void {
|
||||
this._connectionWidget.updateServerGroup(this.getAllServerGroups());
|
||||
public initDialog(providers: string[], connectionInfo: IConnectionProfile): void {
|
||||
this._connectionWidget.updateServerGroup(this.getAllServerGroups(providers));
|
||||
this._model = connectionInfo;
|
||||
this._model.providerName = this._providerName;
|
||||
let appNameOption = this._providerOptions.find(option => option.specialValueType === ConnectionOptionSpecialType.appName);
|
||||
|
||||
@@ -33,6 +33,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { trim } from 'vs/base/common/strings';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
|
||||
export interface IConnectionValidateResult {
|
||||
isValid: boolean;
|
||||
@@ -49,7 +50,7 @@ export interface IConnectionComponentCallbacks {
|
||||
|
||||
export interface IConnectionComponentController {
|
||||
showUiComponent(container: HTMLElement): void;
|
||||
initDialog(model: IConnectionProfile): void;
|
||||
initDialog(providers: string[], model: IConnectionProfile): void;
|
||||
validateConnection(): IConnectionValidateResult;
|
||||
fillInConnectionInputs(connectionInfo: IConnectionProfile): void;
|
||||
handleOnConnecting(): void;
|
||||
@@ -75,6 +76,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
||||
private _currentProviderType: string = 'Microsoft SQL Server';
|
||||
private _connecting: boolean = false;
|
||||
private _connectionErrorTitle = localize('connectionError', 'Connection error');
|
||||
private _dialogDeferredPromise: Deferred<IConnectionProfile>;
|
||||
|
||||
constructor(
|
||||
@IPartService private _partService: IPartService,
|
||||
@@ -82,17 +84,24 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
||||
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
|
||||
@IWindowsService private _windowsService: IWindowsService,
|
||||
@IClipboardService private _clipboardService: IClipboardService,
|
||||
@ICommandService private _commandService: ICommandService
|
||||
) { }
|
||||
|
||||
private getDefaultProviderName() {
|
||||
if (this._workspaceConfigurationService) {
|
||||
let defaultProvider = WorkbenchUtils.getSqlConfigValue<string>(this._workspaceConfigurationService, Constants.defaultEngine);
|
||||
let defaultProvider: string;
|
||||
if (this._providerNameToDisplayNameMap) {
|
||||
let keys = Object.keys(this._providerNameToDisplayNameMap);
|
||||
if (keys && keys.length > 0) {
|
||||
defaultProvider = keys[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!defaultProvider && this._workspaceConfigurationService) {
|
||||
defaultProvider = WorkbenchUtils.getSqlConfigValue<string>(this._workspaceConfigurationService, Constants.defaultEngine);
|
||||
}
|
||||
// as a fallback, default to MSSQL if the value from settings is not available
|
||||
return Constants.mssqlProviderName;
|
||||
return defaultProvider || Constants.mssqlProviderName;
|
||||
}
|
||||
|
||||
private handleOnConnect(params: INewConnectionParams, profile?: IConnectionProfile): void {
|
||||
@@ -144,33 +153,36 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
||||
} else {
|
||||
this._connectionManagementService.cancelConnection(this._model);
|
||||
}
|
||||
if (params && params.input && params.input.onConnectReject) {
|
||||
params.input.onConnectReject();
|
||||
}
|
||||
this._connectionDialog.resetConnection();
|
||||
this._connecting = false;
|
||||
}
|
||||
this.uiController.databaseDropdownExpanded = false;
|
||||
if (this._dialogDeferredPromise) {
|
||||
this._dialogDeferredPromise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
private handleDefaultOnConnect(params: INewConnectionParams, connection: IConnectionProfile): Thenable<void> {
|
||||
let fromEditor = params && params.connectionType === ConnectionType.editor;
|
||||
let uri: string = undefined;
|
||||
if (fromEditor && params.input) {
|
||||
if (fromEditor && params && params.input) {
|
||||
uri = params.input.uri;
|
||||
}
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: params,
|
||||
saveTheConnection: !fromEditor,
|
||||
showDashboard: params.showDashboard !== undefined ? params.showDashboard : !fromEditor,
|
||||
showDashboard: params && params.showDashboard !== undefined ? params.showDashboard : !fromEditor,
|
||||
showConnectionDialogOnError: false,
|
||||
showFirewallRuleOnError: true
|
||||
};
|
||||
|
||||
return this._connectionManagementService.connectAndSaveProfile(connection, uri, options, params.input).then(connectionResult => {
|
||||
return this._connectionManagementService.connectAndSaveProfile(connection, uri, options, params && params.input).then(connectionResult => {
|
||||
this._connecting = false;
|
||||
if (connectionResult && connectionResult.connected) {
|
||||
this._connectionDialog.close();
|
||||
if (this._dialogDeferredPromise) {
|
||||
this._dialogDeferredPromise.resolve(connectionResult.connectionProfile);
|
||||
}
|
||||
} else if (connectionResult && connectionResult.errorHandled) {
|
||||
this._connectionDialog.resetConnection();
|
||||
} else {
|
||||
@@ -216,7 +228,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
||||
}
|
||||
|
||||
private handleInitDialog() {
|
||||
this.uiController.initDialog(this._model);
|
||||
this.uiController.initDialog(this._params && this._params.providers, this._model);
|
||||
}
|
||||
|
||||
private handleFillInConnectionInputs(connectionInfo: IConnectionProfile): void {
|
||||
@@ -268,9 +280,25 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
||||
});
|
||||
}
|
||||
|
||||
public openDialogAndWait(connectionManagementService: IConnectionManagementService,
|
||||
params?: INewConnectionParams,
|
||||
model?: IConnectionProfile,
|
||||
connectionResult?: IConnectionResult): Thenable<IConnectionProfile> {
|
||||
this._dialogDeferredPromise = new Deferred<IConnectionProfile>();
|
||||
|
||||
this.showDialog(connectionManagementService,
|
||||
params,
|
||||
model,
|
||||
connectionResult).then(() => {
|
||||
}, error => {
|
||||
this._dialogDeferredPromise.reject(error);
|
||||
});
|
||||
return this._dialogDeferredPromise;
|
||||
}
|
||||
|
||||
public showDialog(
|
||||
connectionManagementService: IConnectionManagementService,
|
||||
params: INewConnectionParams,
|
||||
params?: INewConnectionParams,
|
||||
model?: IConnectionProfile,
|
||||
connectionResult?: IConnectionResult): Thenable<void> {
|
||||
|
||||
@@ -300,11 +328,12 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private doShowDialog(params: INewConnectionParams): TPromise<void> {
|
||||
if (!this._connectionDialog) {
|
||||
let container = document.getElementById(this._partService.getWorkbenchElementId()).parentElement;
|
||||
this._container = container;
|
||||
this._connectionDialog = this._instantiationService.createInstance(ConnectionDialogWidget, this._providerTypes, this._providerNameToDisplayNameMap[this._model.providerName]);
|
||||
this._connectionDialog = this._instantiationService.createInstance(ConnectionDialogWidget, this._providerTypes, this._providerNameToDisplayNameMap[this._model.providerName], this._providerNameToDisplayNameMap);
|
||||
this._connectionDialog.onCancel(() => {
|
||||
this._connectionDialog.databaseDropdownExpanded = this.uiController.databaseDropdownExpanded;
|
||||
this.handleOnCancel(this._connectionDialog.newConnectionParams);
|
||||
@@ -319,7 +348,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
||||
this._connectionDialog.newConnectionParams = params;
|
||||
|
||||
return new TPromise<void>(() => {
|
||||
this._connectionDialog.open(this._connectionManagementService.getRecentConnections().length > 0);
|
||||
this._connectionDialog.open(this._connectionManagementService.getRecentConnections(params.providers).length > 0);
|
||||
this.uiController.focusOnOpen();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ export class ConnectionDialogWidget extends Modal {
|
||||
private $connectionUIContainer: Builder;
|
||||
private _databaseDropdownExpanded: boolean;
|
||||
private _actionbar: ActionBar;
|
||||
private _providers: string[];
|
||||
|
||||
private _panel: TabbedPanel;
|
||||
private _recentConnectionTabId: PanelTabIdentifier;
|
||||
@@ -84,6 +85,7 @@ export class ConnectionDialogWidget extends Modal {
|
||||
constructor(
|
||||
private providerTypeOptions: string[],
|
||||
private selectedProviderType: string,
|
||||
private providerNameToDisplayNameMap: { [providerDisplayName: string]: string },
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IWorkbenchThemeService private _themeService: IWorkbenchThemeService,
|
||||
@@ -96,6 +98,22 @@ export class ConnectionDialogWidget extends Modal {
|
||||
super(localize('connection', 'Connection'), TelemetryKeys.Connection, _partService, telemetryService, contextKeyService, { hasSpinner: true, hasErrors: true });
|
||||
}
|
||||
|
||||
public refresh(): void {
|
||||
let filteredProviderTypes = this.providerTypeOptions;
|
||||
|
||||
if (this._newConnectionParams && this._newConnectionParams.providers) {
|
||||
let validProviderNames = Object.keys(this.providerNameToDisplayNameMap).filter(x => this.includeProvider(x, this._newConnectionParams));
|
||||
if (validProviderNames && validProviderNames.length > 0) {
|
||||
filteredProviderTypes = filteredProviderTypes.filter(x => validProviderNames.find( v => this.providerNameToDisplayNameMap[v] === x) !== undefined);
|
||||
}
|
||||
}
|
||||
this._providerTypeSelectBox.setOptions(filteredProviderTypes);
|
||||
}
|
||||
|
||||
private includeProvider(providerName: string, params?: INewConnectionParams): Boolean {
|
||||
return params === undefined || params.providers === undefined || params.providers.find(x => x === providerName) !== undefined;
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement): void {
|
||||
let connectionContainer = $('.connection-dialog');
|
||||
container.appendChild(connectionContainer.getHTMLElement());
|
||||
@@ -147,7 +165,7 @@ export class ConnectionDialogWidget extends Modal {
|
||||
this._panel.onTabChange(c => {
|
||||
if (c === savedConnectionTabId && this._savedConnectionTree.getContentHeight() === 0) {
|
||||
// Update saved connection tree
|
||||
TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this._connectionManagementService);
|
||||
TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this._connectionManagementService, this._providers);
|
||||
|
||||
if (this._savedConnectionTree.getContentHeight() > 0) {
|
||||
this._noSavedConnectionBuilder.hide();
|
||||
@@ -366,7 +384,7 @@ export class ConnectionDialogWidget extends Modal {
|
||||
this._recentConnectionBuilder.hide();
|
||||
this._noRecentConnectionBuilder.show();
|
||||
}
|
||||
TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService);
|
||||
TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this._connectionManagementService, this._providers);
|
||||
|
||||
// reset saved connection tree
|
||||
this._savedConnectionTree.setInput([]);
|
||||
@@ -415,6 +433,8 @@ export class ConnectionDialogWidget extends Modal {
|
||||
|
||||
public set newConnectionParams(params: INewConnectionParams) {
|
||||
this._newConnectionParams = params;
|
||||
this._providers = params && params.providers;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
public updateProvider(displayName: string) {
|
||||
|
||||
@@ -57,7 +57,7 @@ import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsV
|
||||
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
|
||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
|
||||
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editableDropdown.component';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
||||
|
||||
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
||||
|
||||
@@ -231,7 +231,7 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget,
|
||||
if (componentInstance.setConfig) {
|
||||
componentInstance.setConfig(this.insightConfig.type[this._typeKey]);
|
||||
}
|
||||
componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => item.displayValue)) };
|
||||
componentInstance.data = { columns: result.columnInfo.map(item => item.columnName), rows: result.rows.map(row => row.map(item => (item.invariantCultureDisplayValue === null || item.invariantCultureDisplayValue === undefined) ? item.displayValue : item.invariantCultureDisplayValue)) };
|
||||
|
||||
if (componentInstance.init) {
|
||||
componentInstance.init();
|
||||
|
||||
@@ -16,6 +16,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||
import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput';
|
||||
import { IEditorViewState } from 'vs/editor/common/editorCommon';
|
||||
|
||||
/**
|
||||
* Input for the EditDataEditor.
|
||||
@@ -37,6 +38,8 @@ export class EditDataInput extends EditorInput implements IConnectableInput {
|
||||
private _css: HTMLStyleElement;
|
||||
private _useQueryFilter: boolean;
|
||||
|
||||
public savedViewState: IEditorViewState;
|
||||
|
||||
constructor(
|
||||
private _uri: URI,
|
||||
private _schemaName,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
/**
|
||||
* Input for the EditDataResultsEditor. This input helps with logic for the viewing and editing of
|
||||
@@ -25,6 +26,9 @@ export class EditDataResultsInput extends EditorInput {
|
||||
private _editorContainer: HTMLElement;
|
||||
public css: HTMLStyleElement;
|
||||
|
||||
public readonly onRestoreViewStateEmitter = new Emitter<void>();
|
||||
public readonly onSaveViewStateEmitter = new Emitter<void>();
|
||||
|
||||
constructor(private _uri: string) {
|
||||
super();
|
||||
this._visible = false;
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Builder } from 'vs/base/browser/builder';
|
||||
|
||||
import { EditorOptions, EditorInput } from 'vs/workbench/common/editor';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { Position, IEditorControl, IEditor } from 'vs/platform/editor/common/editor';
|
||||
import { Position, IEditorControl, IEditor, IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
@@ -43,6 +43,8 @@ import { IFlexibleSash, VerticalFlexibleSash, HorizontalFlexibleSash } from 'sql
|
||||
import { Orientation } from 'vs/base/browser/ui/sash/sash';
|
||||
import { EditDataResultsEditor } from 'sql/parts/editData/editor/editDataResultsEditor';
|
||||
import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput';
|
||||
import { IEditorViewState } from 'vs/editor/common/editorCommon';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
/**
|
||||
* Editor that hosts an action bar and a resultSetInput for an edit data session
|
||||
@@ -96,6 +98,14 @@ export class EditDataEditor extends BaseEditor {
|
||||
if (contextKeyService) {
|
||||
this._queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
if (_editorGroupService) {
|
||||
_editorGroupService.onEditorOpening(e => {
|
||||
if (this.isVisible() && (e.input !== this.input || e.position !== this.position)) {
|
||||
this.saveEditorViewState();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC METHODS ////////////////////////////////////////////////////////////
|
||||
@@ -594,7 +604,15 @@ export class EditDataEditor extends BaseEditor {
|
||||
// Run all three steps synchronously
|
||||
return createEditors()
|
||||
.then(onEditorsCreated)
|
||||
.then(doLayout);
|
||||
.then(doLayout)
|
||||
.then(() => {
|
||||
if (newInput.results) {
|
||||
newInput.results.onRestoreViewStateEmitter.fire();
|
||||
}
|
||||
if (newInput.savedViewState) {
|
||||
this._sqlEditor.getControl().restoreViewState(newInput.savedViewState);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _setSashDimension(): void {
|
||||
@@ -631,7 +649,6 @@ export class EditDataEditor extends BaseEditor {
|
||||
* has been opened with the same editor, or we are opening the editor for the first time).
|
||||
*/
|
||||
private _updateInput(oldInput: EditDataInput, newInput: EditDataInput, options?: EditorOptions): TPromise<void> {
|
||||
|
||||
if (this._sqlEditor) {
|
||||
this._sqlEditor.clearInput();
|
||||
}
|
||||
@@ -727,4 +744,16 @@ export class EditDataEditor extends BaseEditor {
|
||||
public queryPaneEnabled(): boolean {
|
||||
return this.editDataInput.queryPaneEnabled;
|
||||
}
|
||||
|
||||
private saveEditorViewState(): void {
|
||||
let editDataInput = this.input as EditDataInput;
|
||||
if (editDataInput) {
|
||||
if (this._sqlEditor) {
|
||||
editDataInput.savedViewState = this._sqlEditor.getControl().saveViewState();
|
||||
}
|
||||
if (editDataInput.results) {
|
||||
editDataInput.results.onSaveViewStateEmitter.fire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { IEditDataComponentParams } from 'sql/services/bootstrap/bootstrapParams
|
||||
import { EditDataModule } from 'sql/parts/grid/views/editData/editData.module';
|
||||
import { EDITDATA_SELECTOR } from 'sql/parts/grid/views/editData/editData.component';
|
||||
import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export class EditDataResultsEditor extends BaseEditor {
|
||||
|
||||
@@ -109,7 +110,11 @@ export class EditDataResultsEditor extends BaseEditor {
|
||||
// Otherwise many components will be left around and be subscribed
|
||||
// to events from the backing data service
|
||||
const parent = input.container;
|
||||
let params: IEditDataComponentParams = { dataService: dataService };
|
||||
let params: IEditDataComponentParams = {
|
||||
dataService: dataService,
|
||||
onSaveViewState: input.onSaveViewStateEmitter.event,
|
||||
onRestoreViewState: input.onRestoreViewStateEmitter.event
|
||||
};
|
||||
bootstrapAngular(this._instantiationService,
|
||||
EditDataModule,
|
||||
parent,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
export class FileBrowserController extends treedefaults.DefaultController {
|
||||
|
||||
constructor() {
|
||||
super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN });
|
||||
super({ clickBehavior: treedefaults.ClickBehavior.ON_MOUSE_DOWN, openMode: treedefaults.OpenMode.SINGLE_CLICK });
|
||||
}
|
||||
|
||||
protected onLeftClick(tree: ITree, element: any, event: IMouseEvent, origin: string = 'mouse'): boolean {
|
||||
|
||||
@@ -18,12 +18,9 @@
|
||||
showHeader="true"
|
||||
[resized]="dataSet.resized"
|
||||
[plugins]="plugins[i]"
|
||||
(activeCellChanged)="onActiveCellChanged($event)"
|
||||
(cellEditBegin)="onCellEditBegin($event)"
|
||||
(cellEditExit)="onCellEditEnd($event)"
|
||||
(rowEditBegin)="onRowEditBegin($event)"
|
||||
(rowEditExit)="onRowEditEnd($event)"
|
||||
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
|
||||
(onActiveCellChanged)="onActiveCellChanged($event)"
|
||||
(onCellChange)="onCellEditEnd($event)"
|
||||
(onContextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
|
||||
[isCellEditValid]="onIsCellEditValid"
|
||||
[overrideCellFn]="overrideCellFn"
|
||||
enableEditing="true"
|
||||
|
||||
@@ -13,7 +13,7 @@ import 'vs/css!sql/parts/grid/media/slickGrid';
|
||||
import 'vs/css!./media/editData';
|
||||
|
||||
import { ElementRef, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, forwardRef, EventEmitter } from '@angular/core';
|
||||
import { IGridDataRow, VirtualizedCollection } from 'angular2-slickgrid';
|
||||
import { IGridDataRow, VirtualizedCollection, ISlickRange } from 'angular2-slickgrid';
|
||||
|
||||
import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
|
||||
import * as Services from 'sql/parts/grid/services/sharedServices';
|
||||
@@ -71,16 +71,19 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
||||
protected plugins = new Array<Array<Slick.Plugin<any>>>();
|
||||
|
||||
// Edit Data functions
|
||||
public onActiveCellChanged: (event: { row: number, column: number }) => void;
|
||||
public onCellEditEnd: (event: { row: number, column: number, newValue: any }) => void;
|
||||
public onCellEditBegin: (event: { row: number, column: number }) => void;
|
||||
public onRowEditBegin: (event: { row: number }) => void;
|
||||
public onRowEditEnd: (event: { row: number }) => void;
|
||||
public onActiveCellChanged: (event: Slick.OnActiveCellChangedEventArgs<any>) => void;
|
||||
public onCellEditEnd: (event: Slick.OnCellChangeEventArgs<any>) => void;
|
||||
public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean;
|
||||
public onIsColumnEditable: (column: number) => boolean;
|
||||
public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
|
||||
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
|
||||
|
||||
private savedViewState: {
|
||||
gridSelections: ISlickRange[];
|
||||
scrollTop;
|
||||
scrollLeft;
|
||||
};
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef,
|
||||
@@ -98,6 +101,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
||||
this._el.nativeElement.className = 'slickgridContainer';
|
||||
this.dataService = params.dataService;
|
||||
this.actionProvider = this.instantiationService.createInstance(EditDataGridActionProvider, this.dataService, this.onGridSelectAll(), this.onDeleteRow(), this.onRevertRow());
|
||||
params.onRestoreViewState(() => this.restoreViewState());
|
||||
params.onSaveViewState(() => this.saveViewState());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,17 +164,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
||||
|
||||
this.onActiveCellChanged = this.onCellSelect;
|
||||
|
||||
this.onCellEditEnd = (event: { row: number, column: number, newValue: any }): void => {
|
||||
this.onCellEditEnd = (event: Slick.OnCellChangeEventArgs<any>): void => {
|
||||
// Store the value that was set
|
||||
self.currentEditCellValue = event.newValue;
|
||||
self.currentEditCellValue = event.item[event.cell - 1];
|
||||
};
|
||||
|
||||
this.onCellEditBegin = (event: { row: number, column: number }): void => { };
|
||||
|
||||
this.onRowEditBegin = (event: { row: number }): void => { };
|
||||
|
||||
this.onRowEditEnd = (event: { row: number }): void => { };
|
||||
|
||||
this.overrideCellFn = (rowNumber, columnId, value?, data?): string => {
|
||||
let returnVal = '';
|
||||
if (Services.DBCellValue.isDBCellValue(value)) {
|
||||
@@ -225,10 +224,10 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
||||
};
|
||||
}
|
||||
|
||||
onCellSelect(event: { row: number, column: number }): void {
|
||||
onCellSelect(event: Slick.OnActiveCellChangedEventArgs<any>): void {
|
||||
let self = this;
|
||||
let row = event.row;
|
||||
let column = event.column;
|
||||
let column = event.cell;
|
||||
|
||||
// Skip processing if the newly selected cell is undefined or we don't have column
|
||||
// definition for the column (ie, the selection was reset)
|
||||
@@ -578,4 +577,25 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
||||
? (this._defaultNumShowingRows + 1) * this._rowHeight + 10
|
||||
: this.getMaxHeight(rowCount);
|
||||
}
|
||||
|
||||
private saveViewState(): void {
|
||||
let gridSelections = this.slickgrids.toArray()[0].getSelectedRanges();
|
||||
let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement;
|
||||
|
||||
this.savedViewState = {
|
||||
gridSelections,
|
||||
scrollTop: viewport.scrollTop,
|
||||
scrollLeft: viewport.scrollLeft
|
||||
};
|
||||
}
|
||||
|
||||
private restoreViewState(): void {
|
||||
if (this.savedViewState) {
|
||||
this.slickgrids.toArray()[0].selection = this.savedViewState.gridSelections;
|
||||
let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement;
|
||||
viewport.scrollLeft = this.savedViewState.scrollLeft;
|
||||
viewport.scrollTop = this.savedViewState.scrollTop;
|
||||
this.savedViewState = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,16 +80,6 @@ export abstract class GridParentComponent {
|
||||
|
||||
@ViewChildren('slickgrid') slickgrids: QueryList<SlickGrid>;
|
||||
|
||||
// Edit Data functions
|
||||
public onActiveCellChanged: (event: { row: number, column: number }) => void;
|
||||
public onCellEditEnd: (event: { row: number, column: number, newValue: any }) => void;
|
||||
public onCellEditBegin: (event: { row: number, column: number }) => void;
|
||||
public onRowEditBegin: (event: { row: number }) => void;
|
||||
public onRowEditEnd: (event: { row: number }) => void;
|
||||
public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean;
|
||||
public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
|
||||
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
|
||||
|
||||
set messageActive(input: boolean) {
|
||||
this._messageActive = input;
|
||||
if (this.resultActive) {
|
||||
|
||||
@@ -329,7 +329,7 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
||||
this._executeResult = <IInsightData>{};
|
||||
this._executeResult.columns = dataSet.columnDefinitions.map(def => def.name);
|
||||
this._executeResult.rows = dataSet.dataRows.getRange(0, dataSet.dataRows.getLength()).map(gridRow => {
|
||||
return gridRow.values.map(cell => cell.displayValue);
|
||||
return gridRow.values.map(cell => (cell.invariantCultureDisplayValue === null || cell.invariantCultureDisplayValue === undefined) ? cell.displayValue : cell.invariantCultureDisplayValue);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
enableAsyncPostRender="true"
|
||||
showHeader="true"
|
||||
[resized]="dataSet.resized"
|
||||
(onActiveCellChanged)="onActiveCellChanged(i)"
|
||||
(mousedown)="navigateToGrid(i)"
|
||||
[selectionModel]="selectionModel"
|
||||
[plugins]="plugins[i]"
|
||||
|
||||
@@ -160,6 +160,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
|
||||
public queryPlanAvailable: EventEmitter<string> = new EventEmitter<string>();
|
||||
public showChartRequested: EventEmitter<IGridDataSet> = new EventEmitter<IGridDataSet>();
|
||||
public goToNextQueryOutputTabRequested: EventEmitter<void> = new EventEmitter<void>();
|
||||
public onActiveCellChanged: (gridIndex: number) => void;
|
||||
|
||||
private savedViewState: {
|
||||
gridSelections: ISlickRange[][];
|
||||
@@ -266,6 +267,8 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
|
||||
self.complete = false;
|
||||
self.activeGrid = 0;
|
||||
|
||||
this.onActiveCellChanged = this.onCellSelect;
|
||||
|
||||
// reset query plan info and send notification to subscribers
|
||||
self.hasQueryPlan = false;
|
||||
self.sentPlans = new Map<number, string>();
|
||||
@@ -402,6 +405,10 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
|
||||
self.onScroll(0);
|
||||
}
|
||||
|
||||
onCellSelect(gridIndex: number): void {
|
||||
this.activeGrid = gridIndex;
|
||||
}
|
||||
|
||||
openMessagesContextMenu(event: any): void {
|
||||
let self = this;
|
||||
event.preventDefault();
|
||||
|
||||
@@ -295,8 +295,9 @@ export class InsightsDialogView extends Modal {
|
||||
for (let action of this._insight.actions.types) {
|
||||
let task = tasks.includes(action);
|
||||
let commandAction = MenuRegistry.getCommand(action);
|
||||
if (task) {
|
||||
let button = this.addFooterButton(types.isString(commandAction.title) ? commandAction.title : commandAction.title.value, () => {
|
||||
let commandLabel = types.isString(commandAction.title) ? commandAction.title : commandAction.title.value;
|
||||
if (task && !this.findFooterButton(commandLabel)) {
|
||||
let button = this.addFooterButton(commandLabel, () => {
|
||||
let element = this._topTable.getSelectedRows();
|
||||
let resource: ListResource;
|
||||
if (element && element.length > 0) {
|
||||
|
||||
@@ -421,3 +421,7 @@ jobsview-component .jobview-grid .slick-cell.error-row {
|
||||
.jobproxiesview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
|
||||
border-left: 1px dotted #444444;
|
||||
}
|
||||
|
||||
.overview-container > .overview-tab > label {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
@@ -120,7 +120,11 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
|
||||
} else {
|
||||
tree.setFocus(element, payload);
|
||||
tree.setSelection([element], payload);
|
||||
self.setStepsTree(element);
|
||||
if (element.rowID) {
|
||||
self.setStepsTree(element);
|
||||
} else {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -71,6 +71,12 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
||||
|
||||
abstract setLayout(layout: any): void;
|
||||
|
||||
public setDataProvider(handle: number, componentId: string, context: any): void {
|
||||
}
|
||||
|
||||
public refreshDataProvider(item: any): void {
|
||||
}
|
||||
|
||||
public setProperties(properties: { [key: string]: any; }): void {
|
||||
if (!properties) {
|
||||
this.properties = {};
|
||||
|
||||
@@ -14,6 +14,7 @@ import DeclarativeTableComponent from './declarativeTable.component';
|
||||
import ListBoxComponent from './listbox.component';
|
||||
import ButtonComponent from './button.component';
|
||||
import CheckBoxComponent from './checkbox.component';
|
||||
import TreeComponent from './tree/tree.component';
|
||||
import RadioButtonComponent from './radioButton.component';
|
||||
import WebViewComponent from './webview.component';
|
||||
import TableComponent from './table.component';
|
||||
@@ -73,6 +74,9 @@ registerComponentType(TABLE_COMPONENT, ModelComponentTypes.Table, TableComponent
|
||||
export const LOADING_COMPONENT = 'loading-component';
|
||||
registerComponentType(LOADING_COMPONENT, ModelComponentTypes.LoadingComponent, LoadingComponent);
|
||||
|
||||
export const TREE_COMPONENT = 'tree-component';
|
||||
registerComponentType(TREE_COMPONENT, ModelComponentTypes.TreeComponent, TreeComponent);
|
||||
|
||||
export const FILEBROWSERTREE_COMPONENT = 'filebrowsertree-component';
|
||||
registerComponentType(FILEBROWSERTREE_COMPONENT, ModelComponentTypes.FileBrowserTree, FileBrowserTreeComponent);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
|
||||
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editableDropdown.component';
|
||||
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
@@ -26,7 +26,7 @@ class FlexItem {
|
||||
template: `
|
||||
<div *ngIf="items" class="flexContainer" [style.flexFlow]="flexFlow" [style.justifyContent]="justifyContent"
|
||||
[style.alignItems]="alignItems" [style.alignContent]="alignContent" [style.height]="height" [style.width]="width">
|
||||
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.textAlign]="textAlign" [style.order]="getItemOrder(item)" >
|
||||
<div *ngFor="let item of items" [style.flex]="getItemFlex(item)" [style.textAlign]="textAlign" [style.order]="getItemOrder(item)" [ngStyle]="getItemStyles(item)">
|
||||
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
|
||||
</model-component-wrapper>
|
||||
</div>
|
||||
@@ -108,4 +108,7 @@ export default class FlexContainer extends ContainerBase<FlexItemLayout> impleme
|
||||
private getItemOrder(item: FlexItem): number {
|
||||
return item.config ? item.config.order : 0;
|
||||
}
|
||||
private getItemStyles(item: FlexItem): { [key: string]: string } {
|
||||
return item.config && item.config.CSSStyles ? item.config.CSSStyles : {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ export interface IComponent {
|
||||
enabled: boolean;
|
||||
readonly valid?: boolean;
|
||||
validate(): Thenable<boolean>;
|
||||
setDataProvider(handle: number, componentId: string, context: any): void;
|
||||
refreshDataProvider(item: any): void;
|
||||
}
|
||||
|
||||
export const COMPONENT_CONFIG = new InjectionToken<IComponentConfig>('component_config');
|
||||
|
||||
@@ -25,7 +25,7 @@ import * as sqlops from 'sqlops';
|
||||
@Component({
|
||||
selector: 'modelview-content',
|
||||
template: `
|
||||
<div *ngIf="rootDescriptor" style="width: 100%; height: 100%;">
|
||||
<div *ngIf="rootDescriptor" style="width: 100%; height: 100%; position: absolute;">
|
||||
<model-component-wrapper style="display: block; height: 100%" [descriptor]="rootDescriptor" [modelStore]="modelStore">
|
||||
</model-component-wrapper>
|
||||
</div>
|
||||
|
||||
160
src/sql/parts/modelComponents/tree/tree.component.ts
Normal file
160
src/sql/parts/modelComponents/tree/tree.component.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!sql/parts/modelComponents/tree/treeComponent';
|
||||
import 'vs/css!sql/media/icons/common-icons';
|
||||
|
||||
import {
|
||||
Component, Input, Inject, ChangeDetectorRef, forwardRef,
|
||||
ViewChild, ElementRef, OnDestroy, AfterViewInit
|
||||
} from '@angular/core';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||
import { TreeComponentRenderer } from 'sql/parts/modelComponents/tree/treeComponentRenderer';
|
||||
import { TreeComponentDataSource } from 'sql/parts/modelComponents/tree/treeDataSource';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { DefaultFilter, DefaultAccessibilityProvider, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITreeComponentItem } from 'sql/workbench/common/views';
|
||||
import { TreeViewDataProvider } from './treeViewDataProvider';
|
||||
import { getContentHeight, getContentWidth } from 'vs/base/browser/dom';
|
||||
|
||||
class Root implements ITreeComponentItem {
|
||||
label = 'root';
|
||||
handle = '0';
|
||||
parentHandle = null;
|
||||
collapsibleState = 2;
|
||||
children = void 0;
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'modelview-tree',
|
||||
template: `
|
||||
<div #input style="width: 100%;height:100%"></div>
|
||||
`
|
||||
})
|
||||
export default class TreeComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||
@Input() descriptor: IComponentDescriptor;
|
||||
@Input() modelStore: IModelStore;
|
||||
private _tree: Tree;
|
||||
private _treeRenderer: TreeComponentRenderer;
|
||||
private _dataProvider: TreeViewDataProvider;
|
||||
|
||||
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||
constructor(
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
@Inject(IContextViewService) private contextViewService: IContextViewService,
|
||||
@Inject(IInstantiationService) private _instantiationService: IInstantiationService) {
|
||||
super(changeRef);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.baseInit();
|
||||
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
if (this._inputContainer) {
|
||||
this.createTreeControl();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.baseDestroy();
|
||||
}
|
||||
|
||||
public setDataProvider(handle: number, componentId: string, context: any): any {
|
||||
this._dataProvider = new TreeViewDataProvider(handle, componentId, context);
|
||||
this.createTreeControl();
|
||||
}
|
||||
|
||||
public refreshDataProvider(itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeComponentItem }): void {
|
||||
if (this._dataProvider) {
|
||||
this._dataProvider.refresh(itemsToRefreshByHandle);
|
||||
}
|
||||
if (this._tree) {
|
||||
for (const item of Object.values(itemsToRefreshByHandle)) {
|
||||
this._tree.refresh(<ITreeComponentItem>item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private createTreeControl(): void {
|
||||
if (!this._tree && this._dataProvider) {
|
||||
const dataSource = this._instantiationService.createInstance(TreeComponentDataSource, this._dataProvider);
|
||||
const renderer = this._instantiationService.createInstance(TreeComponentRenderer, this._dataProvider, this.themeService, { withCheckbox: this.withCheckbox });
|
||||
this._treeRenderer = renderer;
|
||||
const controller = new DefaultController();
|
||||
const filter = new DefaultFilter();
|
||||
const sorter = undefined;
|
||||
const dnd = undefined;
|
||||
const accessibilityProvider = new DefaultAccessibilityProvider();
|
||||
|
||||
this._tree = new Tree(this._inputContainer.nativeElement,
|
||||
{ dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider },
|
||||
{
|
||||
indentPixels: 10,
|
||||
twistiePixels: 20,
|
||||
ariaLabel: 'Tree Node'
|
||||
});
|
||||
this._tree.setInput(new Root());
|
||||
this._tree.domFocus();
|
||||
this._register(this._tree);
|
||||
this._register(attachListStyler(this._tree, this.themeService));
|
||||
this._register(this._tree.onDidChangeSelection( e => {
|
||||
this._dataProvider.onNodeSelected(e.selection);
|
||||
}));
|
||||
this._tree.refresh();
|
||||
this.layout();
|
||||
}
|
||||
}
|
||||
|
||||
/// IComponent implementation
|
||||
|
||||
public layout(): void {
|
||||
this._changeRef.detectChanges();
|
||||
if (this._tree) {
|
||||
|
||||
this.layoutTree();
|
||||
this._tree.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private layoutTree(): void {
|
||||
let width: number = this.convertSizeToNumber(this.width);
|
||||
let height: number = this.convertSizeToNumber(this.height);
|
||||
this._tree.layout(
|
||||
height && height > 0 ? height : getContentHeight(this._inputContainer.nativeElement),
|
||||
width && width > 0 ? width : getContentWidth(this._inputContainer.nativeElement));
|
||||
}
|
||||
|
||||
public setLayout(layout: any): void {
|
||||
// TODO allow configuring the look and feel
|
||||
|
||||
this.layout();
|
||||
}
|
||||
|
||||
public setProperties(properties: { [key: string]: any; }): void {
|
||||
super.setProperties(properties);
|
||||
this._treeRenderer.options.withCheckbox = this.withCheckbox;
|
||||
}
|
||||
|
||||
public get withCheckbox(): boolean {
|
||||
return this.getPropertyOrDefault<sqlops.TreeProperties, boolean>((props) => props.withCheckbox, false);
|
||||
}
|
||||
|
||||
public set withCheckbox(newValue: boolean) {
|
||||
this.setPropertyFromUI<sqlops.TreeProperties, boolean>((properties, value) => { properties.withCheckbox = value; }, newValue);
|
||||
}
|
||||
}
|
||||
8
src/sql/parts/modelComponents/tree/treeComponent.css
Normal file
8
src/sql/parts/modelComponents/tree/treeComponent.css
Normal file
@@ -0,0 +1,8 @@
|
||||
.tree-component-node-tile {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tree-component-node-tile .model-view-tree-node-item-icon{
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
173
src/sql/parts/modelComponents/tree/treeComponentRenderer.ts
Normal file
173
src/sql/parts/modelComponents/tree/treeComponentRenderer.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!sql/media/icons/common-icons';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { LIGHT } from 'vs/platform/theme/common/themeService';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ITreeComponentItem } from 'sql/workbench/common/views';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { TreeViewDataProvider } from './treeViewDataProvider';
|
||||
|
||||
export enum TreeCheckboxState {
|
||||
Intermediate = 0,
|
||||
Checked = 1,
|
||||
Unchecked = 2
|
||||
}
|
||||
|
||||
export class TreeDataTemplate extends Disposable {
|
||||
root: HTMLElement;
|
||||
label: HTMLSpanElement;
|
||||
icon: HTMLElement;
|
||||
private _checkbox: HTMLInputElement;
|
||||
model: ITreeComponentItem;
|
||||
private _onChange = new Emitter<boolean>();
|
||||
|
||||
public readonly onChange: Event<boolean> = this._onChange.event;
|
||||
|
||||
public set checkbox(input: HTMLInputElement) {
|
||||
this._checkbox = input;
|
||||
this.handleOnChange(this._checkbox, () => {
|
||||
this._onChange.fire(this._checkbox.checked);
|
||||
if (this.model && this.model.onCheckedChanged) {
|
||||
this.model.onCheckedChanged(this._checkbox.checked);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public get checkboxState(): TreeCheckboxState {
|
||||
if (this._checkbox.indeterminate) {
|
||||
return TreeCheckboxState.Intermediate;
|
||||
} else {
|
||||
return this.checkbox.checked ? TreeCheckboxState.Checked : TreeCheckboxState.Unchecked;
|
||||
}
|
||||
}
|
||||
|
||||
public set checkboxState(value: TreeCheckboxState) {
|
||||
if (this.checkboxState !== value) {
|
||||
switch (value) {
|
||||
case TreeCheckboxState.Checked:
|
||||
this._checkbox.indeterminate = false;
|
||||
this._checkbox.checked = true;
|
||||
break;
|
||||
case TreeCheckboxState.Unchecked:
|
||||
this._checkbox.indeterminate = false;
|
||||
this._checkbox.checked = false;
|
||||
break;
|
||||
case TreeCheckboxState.Intermediate:
|
||||
this._checkbox.indeterminate = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get checkbox(): HTMLInputElement {
|
||||
return this._checkbox;
|
||||
}
|
||||
|
||||
protected handleOnChange(domNode: HTMLElement, listener: (e: Event<void>) => void): void {
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.CHANGE, listener));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the tree items.
|
||||
* Uses the dom template to render connection groups and connections.
|
||||
*/
|
||||
export class TreeComponentRenderer extends Disposable implements IRenderer {
|
||||
|
||||
public static DEFAULT_TEMPLATE = 'DEFAULT_TEMPLATE';
|
||||
public static DEFAULT_HEIGHT = 20;
|
||||
|
||||
|
||||
constructor(
|
||||
private _dataProvider: TreeViewDataProvider,
|
||||
private themeService: IWorkbenchThemeService,
|
||||
public options?: { withCheckbox: boolean }
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element's height in the tree, in pixels.
|
||||
*/
|
||||
public getHeight(tree: ITree, element: any): number {
|
||||
return TreeComponentRenderer.DEFAULT_HEIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a template ID for a given element.
|
||||
*/
|
||||
public getTemplateId(tree: ITree, element: any): string {
|
||||
|
||||
return TreeComponentRenderer.DEFAULT_TEMPLATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render template in a dom element based on template id
|
||||
*/
|
||||
public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
|
||||
|
||||
if (templateId === TreeComponentRenderer.DEFAULT_TEMPLATE) {
|
||||
const nodeTemplate: TreeDataTemplate = new TreeDataTemplate();
|
||||
nodeTemplate.root = dom.append(container, dom.$('.tree-component-node-tile'));
|
||||
nodeTemplate.icon = dom.append(nodeTemplate.root, dom.$('div.model-view-tree-node-item-icon'));
|
||||
if (this.options && this.options.withCheckbox) {
|
||||
let checkboxWrapper = dom.append(nodeTemplate.root, dom.$('div.checkboxWrapper'));
|
||||
nodeTemplate.checkbox = dom.append(checkboxWrapper, dom.$<HTMLInputElement>('input.checkbox', { type: 'checkbox' }));
|
||||
}
|
||||
|
||||
nodeTemplate.label = dom.append(nodeTemplate.root, dom.$('div.model-view-tree-node-item-label'));
|
||||
return nodeTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a element, given an object bag returned by the template
|
||||
*/
|
||||
public renderElement(tree: ITree, element: ITreeComponentItem, templateId: string, templateData: TreeDataTemplate): void {
|
||||
const icon = this.themeService.getTheme().type === LIGHT ? element.icon : element.iconDark;
|
||||
templateData.icon.style.backgroundImage = icon ? `url('${icon}')` : '';
|
||||
dom.toggleClass(templateData.icon, 'model-view-tree-node-item-icon', !!icon);
|
||||
if (element) {
|
||||
element.onCheckedChanged = (checked: boolean) => {
|
||||
this._dataProvider.onNodeCheckedChanged(element.handle, checked);
|
||||
}
|
||||
templateData.model = element;
|
||||
}
|
||||
if (templateId === TreeComponentRenderer.DEFAULT_TEMPLATE) {
|
||||
this.renderNode(element, templateData);
|
||||
}
|
||||
}
|
||||
|
||||
private renderNode(treeNode: ITreeComponentItem, templateData: TreeDataTemplate): void {
|
||||
let label = treeNode.label;
|
||||
templateData.label.textContent = label;
|
||||
templateData.root.title = label;
|
||||
templateData.checkboxState = this.getCheckboxState(treeNode);
|
||||
}
|
||||
|
||||
private getCheckboxState(treeNode: ITreeComponentItem): TreeCheckboxState {
|
||||
if (treeNode.checked === undefined) {
|
||||
return TreeCheckboxState.Intermediate;
|
||||
} else {
|
||||
return treeNode.checked ? TreeCheckboxState.Checked : TreeCheckboxState.Unchecked;
|
||||
}
|
||||
}
|
||||
|
||||
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
|
||||
this.dispose();
|
||||
// no op
|
||||
}
|
||||
}
|
||||
|
||||
62
src/sql/parts/modelComponents/tree/treeDataSource.ts
Normal file
62
src/sql/parts/modelComponents/tree/treeDataSource.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IModelViewTreeViewDataProvider, ITreeComponentItem } from 'sql/workbench/common/views';
|
||||
import { TreeItemCollapsibleState } from 'vs/workbench/common/views';
|
||||
|
||||
/**
|
||||
* Implements the DataSource(that returns a parent/children of an element) for the recent connection tree
|
||||
*/
|
||||
export class TreeComponentDataSource implements IDataSource {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
private _dataProvider: IModelViewTreeViewDataProvider) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique identifier of the given element.
|
||||
* No more than one element may use a given identifier.
|
||||
*/
|
||||
public getId(tree: ITree, node: ITreeComponentItem): string {
|
||||
return node.handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean value indicating whether the element has children.
|
||||
*/
|
||||
public hasChildren(tree: ITree, node: ITreeComponentItem): boolean {
|
||||
return this._dataProvider !== undefined && node.collapsibleState !== TreeItemCollapsibleState.None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element's children as an array in a promise.
|
||||
*/
|
||||
public getChildren(tree: ITree, node: ITreeComponentItem): TPromise<any> {
|
||||
if (this._dataProvider) {
|
||||
if (node && node.handle === '0') {
|
||||
return this._dataProvider.getChildren(undefined);
|
||||
} else {
|
||||
return this._dataProvider.getChildren(node);
|
||||
}
|
||||
}
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
public getParent(tree: ITree, node: any): TPromise<any> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
public shouldAutoexpand(tree: ITree, node: ITreeComponentItem): boolean {
|
||||
return node.collapsibleState === TreeItemCollapsibleState.Expanded;
|
||||
}
|
||||
}
|
||||
31
src/sql/parts/modelComponents/tree/treeViewDataProvider.ts
Normal file
31
src/sql/parts/modelComponents/tree/treeViewDataProvider.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ExtHostModelViewTreeViewsShape, SqlExtHostContext } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { IModelViewTreeViewDataProvider, ITreeComponentItem } from 'sql/workbench/common/views';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import * as vsTreeView from 'vs/workbench/api/electron-browser/mainThreadTreeViews';
|
||||
|
||||
|
||||
export class TreeViewDataProvider extends vsTreeView.TreeViewDataProvider implements IModelViewTreeViewDataProvider {
|
||||
constructor(handle: number, treeViewId: string,
|
||||
context: IExtHostContext,
|
||||
notificationService?: INotificationService
|
||||
) {
|
||||
super(`${handle}-${treeViewId}`, context.getProxy(SqlExtHostContext.ExtHostModelViewTreeViews), notificationService);
|
||||
}
|
||||
|
||||
onNodeCheckedChanged(treeItemHandle?: string, checked?: boolean) {
|
||||
(<ExtHostModelViewTreeViewsShape>this._proxy).$onNodeCheckedChanged(this.treeViewId, treeItemHandle, checked);
|
||||
}
|
||||
|
||||
onNodeSelected(items: ITreeComponentItem[]) {
|
||||
if (items) {
|
||||
(<ExtHostModelViewTreeViewsShape>this._proxy).$onNodeSelected(this.treeViewId, items.map(i => i.handle));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import nls = require('vs/nls');
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
import { IModelStore, IComponentDescriptor, IComponent, IComponentEventArgs } from './interfaces';
|
||||
import { IModelStore, IComponentDescriptor, IComponent } from './interfaces';
|
||||
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IModelView, IModelViewEventArgs } from 'sql/services/model/modelViewService';
|
||||
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||
@@ -97,6 +97,10 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
||||
this.queueAction(componentId, (component) => component.setProperties(properties));
|
||||
}
|
||||
|
||||
refreshDataProvider(componentId: string, item: any): void {
|
||||
this.queueAction(componentId, (component) => component.refreshDataProvider(item));
|
||||
}
|
||||
|
||||
private queueAction<T>(componentId: string, action: (component: IComponent) => T): void {
|
||||
this.modelStore.eventuallyRunOnComponent(componentId, action).catch(err => {
|
||||
// TODO add error handling
|
||||
@@ -122,4 +126,8 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
||||
public validate(componentId: string): Thenable<boolean> {
|
||||
return new Promise(resolve => this.modelStore.eventuallyRunOnComponent(componentId, component => resolve(component.validate())));
|
||||
}
|
||||
|
||||
public setDataProvider(handle: number, componentId: string, context: any): any {
|
||||
return this.queueAction(componentId, (component) => component.setDataProvider(handle, componentId, context));
|
||||
}
|
||||
}
|
||||
@@ -96,24 +96,24 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
|
||||
let targetConnectionProfileGroup: ConnectionProfileGroup = this.getTargetGroup(targetElement);
|
||||
|
||||
const source = data.getData()[0];
|
||||
let oldParent: ConnectionProfileGroup = source.getParent();
|
||||
const self = this;
|
||||
if (this.isDropAllowed(targetConnectionProfileGroup, oldParent, source)) {
|
||||
if (source && source.getParent) {
|
||||
let oldParent: ConnectionProfileGroup = source.getParent();
|
||||
const self = this;
|
||||
if (this.isDropAllowed(targetConnectionProfileGroup, oldParent, source)) {
|
||||
|
||||
if (source instanceof ConnectionProfile) {
|
||||
// Change group id of profile
|
||||
this._connectionManagementService.changeGroupIdForConnection(source, targetConnectionProfileGroup.id).then(() => {
|
||||
TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService, targetConnectionProfileGroup);
|
||||
});
|
||||
} else if (source instanceof ConnectionProfileGroup) {
|
||||
// Change parent id of group
|
||||
this._connectionManagementService.changeGroupIdForConnectionGroup(source, targetConnectionProfileGroup).then(() => {
|
||||
TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService);
|
||||
});
|
||||
if (source instanceof ConnectionProfile) {
|
||||
// Change group id of profile
|
||||
this._connectionManagementService.changeGroupIdForConnection(source, targetConnectionProfileGroup.id).then(() => {
|
||||
TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService, targetConnectionProfileGroup);
|
||||
});
|
||||
} else if (source instanceof ConnectionProfileGroup) {
|
||||
// Change parent id of group
|
||||
this._connectionManagementService.changeGroupIdForConnectionGroup(source, targetConnectionProfileGroup).then(() => {
|
||||
TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public dropAbort(tree: ITree, data: IDragAndDropData): void {
|
||||
|
||||
@@ -15,11 +15,13 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
|
||||
static SubType = new RawContextKey<string>('nodeSubType', undefined);
|
||||
static Status = new RawContextKey<string>('nodeStatus', undefined);
|
||||
static TreeNode = new RawContextKey<TreeNode>('treeNode', undefined);
|
||||
static NodeLabel = new RawContextKey<string>('nodeLabel', undefined);
|
||||
|
||||
private _nodeTypeKey: IContextKey<string>;
|
||||
private _subTypeKey: IContextKey<string>;
|
||||
private _statusKey: IContextKey<string>;
|
||||
private _treeNodeKey: IContextKey<TreeNode>;
|
||||
private _nodeLabelKey: IContextKey<string>;
|
||||
|
||||
constructor(
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
@@ -28,6 +30,7 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
|
||||
this._subTypeKey = TreeNodeContextKey.SubType.bindTo(contextKeyService);
|
||||
this._statusKey = TreeNodeContextKey.Status.bindTo(contextKeyService);
|
||||
this._treeNodeKey = TreeNodeContextKey.TreeNode.bindTo(contextKeyService);
|
||||
this._nodeLabelKey = TreeNodeContextKey.NodeLabel.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
set(value: TreeNode) {
|
||||
@@ -35,6 +38,7 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
|
||||
this._nodeTypeKey.set(value && value.nodeTypeId);
|
||||
this._subTypeKey.set(value && value.nodeSubType);
|
||||
this._statusKey.set(value && value.nodeStatus);
|
||||
this._nodeLabelKey.set(value && value.label);
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
@@ -42,6 +46,7 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
|
||||
this._subTypeKey.reset();
|
||||
this._statusKey.reset();
|
||||
this._treeNodeKey.reset();
|
||||
this._nodeLabelKey.reset();
|
||||
}
|
||||
|
||||
public get(): TreeNode {
|
||||
|
||||
@@ -22,7 +22,7 @@ export class TreeUpdateUtils {
|
||||
/**
|
||||
* Set input for the tree.
|
||||
*/
|
||||
public static structuralTreeUpdate(tree: ITree, viewKey: string, connectionManagementService: IConnectionManagementService): void {
|
||||
public static structuralTreeUpdate(tree: ITree, viewKey: string, connectionManagementService: IConnectionManagementService, providers?: string[]): void {
|
||||
let selectedElement: any;
|
||||
let targetsToExpand: any[];
|
||||
if (tree) {
|
||||
@@ -35,13 +35,13 @@ export class TreeUpdateUtils {
|
||||
let groups;
|
||||
let treeInput = new ConnectionProfileGroup('root', null, undefined, undefined, undefined);
|
||||
if (viewKey === 'recent') {
|
||||
groups = connectionManagementService.getRecentConnections();
|
||||
groups = connectionManagementService.getRecentConnections(providers);
|
||||
treeInput.addConnections(groups);
|
||||
} else if (viewKey === 'active') {
|
||||
groups = connectionManagementService.getActiveConnections();
|
||||
groups = connectionManagementService.getActiveConnections(providers);
|
||||
treeInput.addConnections(groups);
|
||||
} else if (viewKey === 'saved') {
|
||||
treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService);
|
||||
treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService, providers);
|
||||
}
|
||||
|
||||
tree.setInput(treeInput).done(() => {
|
||||
@@ -96,9 +96,9 @@ export class TreeUpdateUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static getTreeInput(connectionManagementService: IConnectionManagementService): ConnectionProfileGroup {
|
||||
public static getTreeInput(connectionManagementService: IConnectionManagementService, providers?: string[]): ConnectionProfileGroup {
|
||||
|
||||
let groups = connectionManagementService.getConnectionGroups();
|
||||
let groups = connectionManagementService.getConnectionGroups(providers);
|
||||
if (groups && groups.length > 0) {
|
||||
let treeInput = groups[0];
|
||||
treeInput.name = 'root';
|
||||
|
||||
@@ -13,7 +13,7 @@ import * as nls from 'vs/nls';
|
||||
|
||||
import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
|
||||
import { ProfilerEditor } from 'sql/parts/profiler/editor/profilerEditor';
|
||||
import { PROFILER_VIEW_TEMPLATE_SETTINGS, IProfilerViewTemplate } from 'sql/parts/profiler/service/interfaces';
|
||||
import { PROFILER_VIEW_TEMPLATE_SETTINGS, PROFILER_SESSION_TEMPLATE_SETTINGS, IProfilerViewTemplate, IProfilerSessionTemplate } from 'sql/parts/profiler/service/interfaces';
|
||||
|
||||
const profilerDescriptor = new EditorDescriptor(
|
||||
ProfilerEditor,
|
||||
@@ -25,212 +25,300 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors)
|
||||
.registerEditor(profilerDescriptor, [new SyncDescriptor(ProfilerInput)]);
|
||||
|
||||
const profilerViewTemplateSchema: IJSONSchema = {
|
||||
description: nls.localize('profiler.settings.viewTemplates', "Specifies view templates"),
|
||||
type: 'array',
|
||||
items: <IJSONSchema>{
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string'
|
||||
description: nls.localize('profiler.settings.viewTemplates', "Specifies view templates"),
|
||||
type: 'array',
|
||||
items: <IJSONSchema>{
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
},
|
||||
default: <Array<IProfilerViewTemplate>>[
|
||||
{
|
||||
name: 'Standard View',
|
||||
columns: [
|
||||
{
|
||||
name: 'EventClass',
|
||||
eventsMapped: ['name']
|
||||
},
|
||||
{
|
||||
name: 'TextData',
|
||||
eventsMapped: ['options_text', 'batch_text']
|
||||
},
|
||||
{
|
||||
name: 'ApplicationName',
|
||||
width: '1',
|
||||
eventsMapped: ['client_app_name']
|
||||
},
|
||||
{
|
||||
name: 'NTUserName',
|
||||
eventsMapped: ['nt_username']
|
||||
},
|
||||
{
|
||||
name: 'LoginName',
|
||||
eventsMapped: ['server_principal_name']
|
||||
},
|
||||
{
|
||||
name: 'ClientProcessID',
|
||||
eventsMapped: ['client_pid']
|
||||
},
|
||||
{
|
||||
name: 'SPID',
|
||||
eventsMapped: ['session_id']
|
||||
},
|
||||
{
|
||||
name: 'StartTime',
|
||||
eventsMapped: ['timestamp']
|
||||
},
|
||||
{
|
||||
name: 'CPU',
|
||||
eventsMapped: ['cpu_time']
|
||||
},
|
||||
{
|
||||
name: 'Reads',
|
||||
eventsMapped: ['logical_reads']
|
||||
},
|
||||
{
|
||||
name: 'Writes',
|
||||
eventsMapped: ['writes']
|
||||
},
|
||||
{
|
||||
name: 'Duration',
|
||||
eventsMapped: ['duration']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
default: <Array<IProfilerViewTemplate>>[
|
||||
{
|
||||
name: 'Standard View',
|
||||
columns: [
|
||||
{
|
||||
name: 'EventClass',
|
||||
eventsMapped: ['name']
|
||||
},
|
||||
{
|
||||
name: 'TextData',
|
||||
eventsMapped: ['options_text', 'batch_text']
|
||||
},
|
||||
{
|
||||
name: 'ApplicationName',
|
||||
width: '1',
|
||||
eventsMapped: ['client_app_name']
|
||||
},
|
||||
{
|
||||
name: 'NTUserName',
|
||||
eventsMapped: ['nt_username']
|
||||
},
|
||||
{
|
||||
name: 'LoginName',
|
||||
eventsMapped: ['server_principal_name']
|
||||
},
|
||||
{
|
||||
name: 'ClientProcessID',
|
||||
eventsMapped: ['client_pid']
|
||||
},
|
||||
{
|
||||
name: 'SPID',
|
||||
eventsMapped: ['session_id']
|
||||
},
|
||||
{
|
||||
name: 'StartTime',
|
||||
eventsMapped: ['timestamp']
|
||||
},
|
||||
{
|
||||
name: 'CPU',
|
||||
eventsMapped: ['cpu_time']
|
||||
},
|
||||
{
|
||||
name: 'Reads',
|
||||
eventsMapped: ['logical_reads']
|
||||
},
|
||||
{
|
||||
name: 'Writes',
|
||||
eventsMapped: ['writes']
|
||||
},
|
||||
{
|
||||
name: 'Duration',
|
||||
eventsMapped: ['duration']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'TSQL View',
|
||||
columns: [
|
||||
{
|
||||
name: 'EventClass',
|
||||
eventsMapped: ['name']
|
||||
},
|
||||
{
|
||||
name: 'TextData',
|
||||
eventsMapped: ['options_text', 'batch_text']
|
||||
},
|
||||
{
|
||||
name: 'SPID',
|
||||
eventsMapped: ['session_id']
|
||||
},
|
||||
{
|
||||
name: 'StartTime',
|
||||
eventsMapped: ['timestamp']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Tuning View',
|
||||
columns: [
|
||||
{
|
||||
name: 'EventClass',
|
||||
eventsMapped: ['name']
|
||||
},
|
||||
{
|
||||
name: 'TextData',
|
||||
eventsMapped: ['options_text', 'batch_text']
|
||||
},
|
||||
{
|
||||
name: 'Duration',
|
||||
eventsMapped: ['duration']
|
||||
},
|
||||
{
|
||||
name: 'SPID',
|
||||
eventsMapped: ['session_id']
|
||||
},
|
||||
{
|
||||
name: 'DatabaseID',
|
||||
eventsMapped: ['database_id']
|
||||
},
|
||||
{
|
||||
name: 'DatabaseName',
|
||||
eventsMapped: ['database_name']
|
||||
},
|
||||
{
|
||||
name: 'ObjectType',
|
||||
eventsMapped: ['object_type']
|
||||
},
|
||||
{
|
||||
name: 'LoginName',
|
||||
eventsMapped: ['server_principal_name']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'TSQL_Locks View',
|
||||
columns: [
|
||||
{
|
||||
name: 'EventClass',
|
||||
eventsMapped: ['name']
|
||||
},
|
||||
{
|
||||
name: 'TextData',
|
||||
eventsMapped: ['options_text', 'batch_text']
|
||||
},
|
||||
{
|
||||
name: 'ApplicationName',
|
||||
eventsMapped: ['client_app_name']
|
||||
},
|
||||
{
|
||||
name: 'NTUserName',
|
||||
eventsMapped: ['nt_username']
|
||||
},
|
||||
{
|
||||
name: 'LoginName',
|
||||
eventsMapped: ['server_principal_name']
|
||||
},
|
||||
{
|
||||
name: 'ClientProcessID',
|
||||
eventsMapped: ['client_pid']
|
||||
},
|
||||
{
|
||||
name: 'SPID',
|
||||
eventsMapped: ['session_id']
|
||||
},
|
||||
{
|
||||
name: 'StartTime',
|
||||
eventsMapped: ['timestamp']
|
||||
},
|
||||
{
|
||||
name: 'CPU',
|
||||
eventsMapped: ['cpu_time']
|
||||
},
|
||||
{
|
||||
name: 'Reads',
|
||||
eventsMapped: ['logical_reads']
|
||||
},
|
||||
{
|
||||
name: 'Writes',
|
||||
eventsMapped: ['writes']
|
||||
},
|
||||
{
|
||||
name: 'Duration',
|
||||
eventsMapped: ['duration']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'TSQL_Duration View',
|
||||
columns: [
|
||||
{
|
||||
name: 'EventClass',
|
||||
eventsMapped: ['name']
|
||||
},
|
||||
{
|
||||
name: 'Duration',
|
||||
eventsMapped: ['duration']
|
||||
},
|
||||
{
|
||||
name: 'TextData',
|
||||
eventsMapped: ['options_text', 'batch_text']
|
||||
},
|
||||
{
|
||||
name: 'SPID',
|
||||
eventsMapped: ['session_id']
|
||||
}
|
||||
]
|
||||
{
|
||||
name: 'TSQL View',
|
||||
columns: [
|
||||
{
|
||||
name: 'EventClass',
|
||||
eventsMapped: ['name']
|
||||
},
|
||||
{
|
||||
name: 'TextData',
|
||||
eventsMapped: ['options_text', 'batch_text']
|
||||
},
|
||||
{
|
||||
name: 'SPID',
|
||||
eventsMapped: ['session_id']
|
||||
},
|
||||
{
|
||||
name: 'StartTime',
|
||||
eventsMapped: ['timestamp']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Tuning View',
|
||||
columns: [
|
||||
{
|
||||
name: 'EventClass',
|
||||
eventsMapped: ['name']
|
||||
},
|
||||
{
|
||||
name: 'TextData',
|
||||
eventsMapped: ['options_text', 'batch_text']
|
||||
},
|
||||
{
|
||||
name: 'Duration',
|
||||
eventsMapped: ['duration']
|
||||
},
|
||||
{
|
||||
name: 'SPID',
|
||||
eventsMapped: ['session_id']
|
||||
},
|
||||
{
|
||||
name: 'DatabaseID',
|
||||
eventsMapped: ['database_id']
|
||||
},
|
||||
{
|
||||
name: 'DatabaseName',
|
||||
eventsMapped: ['database_name']
|
||||
},
|
||||
{
|
||||
name: 'ObjectType',
|
||||
eventsMapped: ['object_type']
|
||||
},
|
||||
{
|
||||
name: 'LoginName',
|
||||
eventsMapped: ['server_principal_name']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'TSQL_Locks View',
|
||||
columns: [
|
||||
{
|
||||
name: 'EventClass',
|
||||
eventsMapped: ['name']
|
||||
},
|
||||
{
|
||||
name: 'TextData',
|
||||
eventsMapped: ['options_text', 'batch_text']
|
||||
},
|
||||
{
|
||||
name: 'ApplicationName',
|
||||
eventsMapped: ['client_app_name']
|
||||
},
|
||||
{
|
||||
name: 'NTUserName',
|
||||
eventsMapped: ['nt_username']
|
||||
},
|
||||
{
|
||||
name: 'LoginName',
|
||||
eventsMapped: ['server_principal_name']
|
||||
},
|
||||
{
|
||||
name: 'ClientProcessID',
|
||||
eventsMapped: ['client_pid']
|
||||
},
|
||||
{
|
||||
name: 'SPID',
|
||||
eventsMapped: ['session_id']
|
||||
},
|
||||
{
|
||||
name: 'StartTime',
|
||||
eventsMapped: ['timestamp']
|
||||
},
|
||||
{
|
||||
name: 'CPU',
|
||||
eventsMapped: ['cpu_time']
|
||||
},
|
||||
{
|
||||
name: 'Reads',
|
||||
eventsMapped: ['logical_reads']
|
||||
},
|
||||
{
|
||||
name: 'Writes',
|
||||
eventsMapped: ['writes']
|
||||
},
|
||||
{
|
||||
name: 'Duration',
|
||||
eventsMapped: ['duration']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'TSQL_Duration View',
|
||||
columns: [
|
||||
{
|
||||
name: 'EventClass',
|
||||
eventsMapped: ['name']
|
||||
},
|
||||
{
|
||||
name: 'Duration',
|
||||
eventsMapped: ['duration']
|
||||
},
|
||||
{
|
||||
name: 'TextData',
|
||||
eventsMapped: ['options_text', 'batch_text']
|
||||
},
|
||||
{
|
||||
name: 'SPID',
|
||||
eventsMapped: ['session_id']
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const profilerSessionTemplateSchema: IJSONSchema = {
|
||||
description: nls.localize('profiler.settings.sessionTemplates', "Specifies session templates"),
|
||||
type: 'array',
|
||||
items: <IJSONSchema>{
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
},
|
||||
default: <Array<IProfilerSessionTemplate>>[
|
||||
{
|
||||
name: 'Standard_OnPrem',
|
||||
defaultView: 'Standard View',
|
||||
createStatement:
|
||||
`CREATE EVENT SESSION [{sessionName}] ON SERVER
|
||||
ADD EVENT sqlserver.attention(
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.nt_username,sqlserver.query_hash,sqlserver.server_principal_name,sqlserver.session_id)
|
||||
WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))),
|
||||
ADD EVENT sqlserver.existing_connection(SET collect_options_text=(1)
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.nt_username,sqlserver.server_principal_name,sqlserver.session_id)),
|
||||
ADD EVENT sqlserver.login(SET collect_options_text=(1)
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.nt_username,sqlserver.server_principal_name,sqlserver.session_id)),
|
||||
ADD EVENT sqlserver.logout(
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.nt_username,sqlserver.server_principal_name,sqlserver.session_id)),
|
||||
ADD EVENT sqlserver.rpc_completed(
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.nt_username,sqlserver.query_hash,sqlserver.server_principal_name,sqlserver.session_id)
|
||||
WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))),
|
||||
ADD EVENT sqlserver.sql_batch_completed(
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.nt_username,sqlserver.query_hash,sqlserver.server_principal_name,sqlserver.session_id)
|
||||
WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))),
|
||||
ADD EVENT sqlserver.sql_batch_starting(
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.nt_username,sqlserver.query_hash,sqlserver.server_principal_name,sqlserver.session_id)
|
||||
WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))))
|
||||
ADD TARGET package0.ring_buffer(SET max_events_limit=(1000),max_memory=(51200))
|
||||
WITH (MAX_MEMORY=8192 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=PER_CPU,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF)`
|
||||
},
|
||||
{
|
||||
name: 'Standard_Azure',
|
||||
defaultView: 'Standard View',
|
||||
createStatement:
|
||||
`CREATE EVENT SESSION [{sessionName}] ON DATABASE
|
||||
ADD EVENT sqlserver.attention(
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.username,sqlserver.query_hash,sqlserver.session_id)
|
||||
WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))),
|
||||
ADD EVENT sqlserver.existing_connection(SET collect_options_text=(1)
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.username,sqlserver.session_id)),
|
||||
ADD EVENT sqlserver.login(SET collect_options_text=(1)
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.username,sqlserver.session_id)),
|
||||
ADD EVENT sqlserver.logout(
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.username,sqlserver.session_id)),
|
||||
ADD EVENT sqlserver.rpc_completed(
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.username,sqlserver.query_hash,sqlserver.session_id)
|
||||
WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))),
|
||||
ADD EVENT sqlserver.sql_batch_completed(
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.username,sqlserver.query_hash,sqlserver.session_id)
|
||||
WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))),
|
||||
ADD EVENT sqlserver.sql_batch_starting(
|
||||
ACTION(package0.event_sequence,sqlserver.client_app_name,sqlserver.client_pid,sqlserver.database_id,sqlserver.username,sqlserver.query_hash,sqlserver.session_id)
|
||||
WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))))
|
||||
ADD TARGET package0.ring_buffer(SET max_events_limit=(1000),max_memory=(51200))
|
||||
WITH (MAX_MEMORY=8192 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=PER_CPU,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF)`
|
||||
},
|
||||
{
|
||||
name: 'TSQL_OnPrem',
|
||||
defaultView: 'TSQL View',
|
||||
createStatement:
|
||||
`CREATE EVENT SESSION [{sessionName}] ON SERVER
|
||||
ADD EVENT sqlserver.existing_connection(
|
||||
ACTION(package0.event_sequence,sqlserver.session_id,sqlserver.client_hostname)),
|
||||
ADD EVENT sqlserver.login(SET collect_options_text=(1)
|
||||
ACTION(package0.event_sequence,sqlserver.session_id,sqlserver.client_hostname)),
|
||||
ADD EVENT sqlserver.logout(
|
||||
ACTION(package0.event_sequence,sqlserver.session_id)),
|
||||
ADD EVENT sqlserver.rpc_starting(
|
||||
ACTION(package0.event_sequence,sqlserver.session_id,sqlserver.database_name)
|
||||
WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0)))),
|
||||
ADD EVENT sqlserver.sql_batch_starting(
|
||||
ACTION(package0.event_sequence,sqlserver.session_id,sqlserver.database_name)
|
||||
WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))))
|
||||
ADD TARGET package0.ring_buffer(SET max_events_limit=(1000),max_memory=(51200))
|
||||
WITH (MAX_MEMORY=8192 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=PER_CPU,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF)`
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
const dashboardConfig: IConfigurationNode = {
|
||||
id: 'Profiler',
|
||||
type: 'object',
|
||||
properties: {
|
||||
[PROFILER_VIEW_TEMPLATE_SETTINGS]: profilerViewTemplateSchema
|
||||
[PROFILER_VIEW_TEMPLATE_SETTINGS]: profilerViewTemplateSchema,
|
||||
[PROFILER_SESSION_TEMPLATE_SETTINGS]: profilerSessionTemplateSchema
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { IObjectExplorerService } from '../../objectExplorer/common/objectExplor
|
||||
import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
||||
import { IProfilerService} from '../service/interfaces';
|
||||
import { IProfilerService } from '../service/interfaces';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyCode, KeyMod } from 'vs/editor/editor.api';
|
||||
import { ProfilerEditor } from '../editor/profilerEditor';
|
||||
@@ -73,12 +73,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
let activeEditor = editorService.getActiveEditor();
|
||||
if (activeEditor instanceof ProfilerEditor) {
|
||||
let profilerInput = activeEditor.input;
|
||||
if (profilerInput.state.isRunning){
|
||||
if (profilerInput.state.isRunning) {
|
||||
return profilerService.stopSession(profilerInput.id);
|
||||
} else {
|
||||
// clear data when profiler is started
|
||||
profilerInput.data.clear();
|
||||
return profilerService.startSession(profilerInput.id);
|
||||
return profilerService.startSession(profilerInput.id, profilerInput.sessionName);
|
||||
}
|
||||
}
|
||||
return TPromise.as(false);
|
||||
|
||||
@@ -20,7 +20,9 @@ import { Action } from 'vs/base/common/actions';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IEditorAction } from 'vs/editor/common/editorCommon';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands'
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
export class ProfilerConnect extends Action {
|
||||
public static ID = 'profiler.connect';
|
||||
@@ -78,7 +80,27 @@ export class ProfilerStart extends Action {
|
||||
|
||||
public run(input: ProfilerInput): TPromise<boolean> {
|
||||
input.data.clear();
|
||||
return TPromise.wrap(this._profilerService.startSession(input.id));
|
||||
return TPromise.wrap(this._profilerService.startSession(input.id, input.sessionName));
|
||||
}
|
||||
}
|
||||
|
||||
export class ProfilerCreate extends Action {
|
||||
public static ID = 'profiler.create';
|
||||
public static LABEL = nls.localize('create', "Create");
|
||||
|
||||
constructor(
|
||||
id: string, label: string,
|
||||
@ICommandService private _commandService: ICommandService,
|
||||
@IProfilerService private _profilerService: IProfilerService,
|
||||
@INotificationService private _notificationService: INotificationService
|
||||
) {
|
||||
super(id, label, 'add');
|
||||
}
|
||||
|
||||
public run(input: ProfilerInput): TPromise<boolean> {
|
||||
return TPromise.wrap(this._profilerService.launchCreateSessionDialog(input).then(() => {
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,12 @@ import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { textFormatter } from 'sql/parts/grid/services/sharedServices';
|
||||
import { IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
|
||||
export interface ProfilerTableViewState {
|
||||
scrollTop: number;
|
||||
scrollLeft: number;
|
||||
}
|
||||
|
||||
export class ProfilerTableEditor extends BaseEditor implements IProfilerController, ITableController {
|
||||
|
||||
@@ -219,4 +225,18 @@ export class ProfilerTableEditor extends BaseEditor implements IProfilerControll
|
||||
this._findState.changeMatchInfo(0, 0, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public saveViewState(): ProfilerTableViewState {
|
||||
let viewElement = this._profilerTable.grid.getCanvasNode().parentElement;
|
||||
return {
|
||||
scrollTop: viewElement.scrollTop,
|
||||
scrollLeft: viewElement.scrollLeft
|
||||
};
|
||||
}
|
||||
|
||||
public restoreViewState(state: ProfilerTableViewState): void {
|
||||
let viewElement = this._profilerTable.grid.getCanvasNode().parentElement;
|
||||
viewElement.scrollTop = state.scrollTop;
|
||||
viewElement.scrollLeft = state.scrollLeft;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user