Add the first version of whoisactive project (#943)
* add the first version of whoisactive * address comments * address comments * fix the issues from the last commit * clean up package.json
5
samples/sp_whoIsActive/src/constants.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
// CONFIG VALUES ///////////////////////////////////////////////////////////
|
||||
export const extensionConfigSectionName = 'extensionSamples';
|
||||
export const configLogDebugInfo = 'logDebugInfo';
|
||||
25
samples/sp_whoIsActive/src/controllers/controllerBase.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. 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;
|
||||
|
||||
public constructor(context: vscode.ExtensionContext) {
|
||||
this._context = context;
|
||||
}
|
||||
|
||||
abstract activate(): Promise<boolean>;
|
||||
|
||||
abstract deactivate(): void;
|
||||
|
||||
public dispose(): void {
|
||||
this.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
63
samples/sp_whoIsActive/src/controllers/mainController.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as Utils from '../utils';
|
||||
import ControllerBase from './controllerBase';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import * as openurl from 'openurl';
|
||||
|
||||
|
||||
/**
|
||||
* The main controller class that initializes the extension
|
||||
*/
|
||||
export default class MainController extends ControllerBase {
|
||||
|
||||
public apiWrapper;
|
||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||
/**
|
||||
* Deactivates the extension
|
||||
*/
|
||||
public deactivate(): void {
|
||||
Utils.logDebug('Main controller deactivated');
|
||||
}
|
||||
|
||||
public activate(): Promise<boolean> {
|
||||
sqlops.dashboard.registerWebviewProvider('sp_whoisactive_documentation', webview => {
|
||||
let templateValues = {url: 'http://whoisactive.com/docs/'};
|
||||
Utils.renderTemplateHtml(path.join(__dirname, '..'), 'templateTab.html', templateValues)
|
||||
.then(html => {
|
||||
webview.html = html;
|
||||
});
|
||||
});
|
||||
|
||||
sqlops.tasks.registerTask('sp_whoisactive.install', e => this.onInstall(e));
|
||||
sqlops.tasks.registerTask('sp_whoisactive.findBlockLeaders', e => this.onExecute(e, 'findBlockLeaders.sql'));
|
||||
sqlops.tasks.registerTask('sp_whoisactive.getPlans', e => this.onExecute(e, 'getPlans.sql'));
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
private onInstall(connection: sqlops.IConnectionProfile): void {
|
||||
openurl.open('http://whoisactive.com/downloads/');
|
||||
}
|
||||
|
||||
private onExecute(connection: sqlops.IConnectionProfile, fileName: string): void {
|
||||
let sqlFile = fs.readFileSync(path.join(__dirname, '..', 'sql', fileName)).toString();
|
||||
this.openSQLFileWithContent(sqlFile);
|
||||
}
|
||||
|
||||
private openSQLFileWithContent(content: string): void {
|
||||
vscode.workspace.openTextDocument({language: 'sql', content: content}).then(doc => {
|
||||
vscode.window.showTextDocument(doc, vscode.ViewColumn.Active, false);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
39
samples/sp_whoIsActive/src/extension.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. 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): Promise<boolean> {
|
||||
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(): void {
|
||||
for (let controller of controllers) {
|
||||
controller.deactivate();
|
||||
}
|
||||
}
|
||||
5
samples/sp_whoIsActive/src/localizedConstants.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
// TODO: localize!
|
||||
|
||||
export const msgErrorLoadingTab = 'An error occurred while loading the tab.';
|
||||
1
samples/sp_whoIsActive/src/media/config.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>configure_dashboard</title><path d="M16,0V4H15V1.71L10.5,6.2l-3-3L0,10.71V9.29L7.5,1.8l3,3L14.29,1H12V0Zm-.12,10.93-1,.4a2.75,2.75,0,0,1,.06.33,3,3,0,0,1,0,.68,2.71,2.71,0,0,1-.06.33l1,.4L15.5,14l-1-.4a3,3,0,0,1-.95.95l.4,1-.92.38-.4-1-.33.06L12,15l-.34,0-.33-.06-.4,1L10,15.5l.4-1a2.93,2.93,0,0,1-.95-.95l-1,.4-.38-.92,1-.4a3.25,3.25,0,0,1,0-1.34l-1-.4L8.49,10l1,.4a2.83,2.83,0,0,1,.95-.95l-.4-1,.92-.38.4,1a3.25,3.25,0,0,1,1.34,0l.4-1,.92.38-.4,1a3,3,0,0,1,.53.42,3,3,0,0,1,.42.53l1-.4ZM12,14a1.94,1.94,0,0,0,.78-.16,2,2,0,0,0,1.07-1.07,2,2,0,0,0,0-1.55,2,2,0,0,0-1.07-1.07,2,2,0,0,0-1.55,0,2,2,0,0,0-1.07,1.07,2,2,0,0,0,0,1.55,2,2,0,0,0,1.07,1.07A1.94,1.94,0,0,0,12,14Z"/></svg>
|
||||
|
After Width: | Height: | Size: 781 B |
1
samples/sp_whoIsActive/src/media/config_inverse.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{font-size:12px;font-family:FullMDL2Assets, Full MDL2 Assets;}.cls-1,.cls-2{fill:#fff;}</style></defs><title>manage_inverse_16x16 </title><text class="cls-1" transform="translate(0.01 11.59)"> </text><path class="cls-2" d="M16,0V4H15V1.71L10.5,6.2l-3-3L0,10.71V9.29L7.5,1.8l3,3L14.29,1H12V0Zm-.12,10.93-1,.4a2.75,2.75,0,0,1,.06.33,3,3,0,0,1,0,.68,2.71,2.71,0,0,1-.06.33l1,.4L15.5,14l-1-.4a3,3,0,0,1-.95.95l.4,1-.92.38-.4-1-.33.06L12,15l-.34,0-.33-.06-.4,1L10,15.5l.4-1a2.93,2.93,0,0,1-.95-.95l-1,.4-.38-.92,1-.4a3.25,3.25,0,0,1,0-1.34l-1-.4L8.49,10l1,.4a2.83,2.83,0,0,1,.95-.95l-.4-1,.92-.38.4,1a3.25,3.25,0,0,1,1.34,0l.4-1,.92.38-.4,1a3,3,0,0,1,.53.42,3,3,0,0,1,.42.53l1-.4ZM12,14a1.94,1.94,0,0,0,.78-.16,2,2,0,0,0,1.07-1.07,2,2,0,0,0,0-1.55,2,2,0,0,0-1.07-1.07,2,2,0,0,0-1.55,0,2,2,0,0,0-1.07,1.07,2,2,0,0,0,0,1.55,2,2,0,0,0,1.07,1.07A1.94,1.94,0,0,0,12,14Z"/></svg>
|
||||
|
After Width: | Height: | Size: 980 B |
1
samples/sp_whoIsActive/src/media/file.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#231f20;}.cls-2{fill:#212121;}</style></defs><title>file_16x16</title><polygon class="cls-1" points="13.59 2.21 13.58 2.22 13.58 2.2 13.59 2.21"/><path class="cls-2" d="M8.71,0,14,5.29V16H2V0ZM3,15H13V6H8V1H3ZM9,1.71V5h3.29Z"/></svg>
|
||||
|
After Width: | Height: | Size: 351 B |
1
samples/sp_whoIsActive/src/media/file_inverse.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>file_inverse_16x16</title><polygon class="cls-1" points="13.59 2.21 13.58 2.22 13.58 2.2 13.59 2.21"/><path class="cls-1" d="M8.71,0,14,5.29V16H2V0ZM3,15H13V6H8V1H3ZM9,1.71V5h3.29Z"/></svg>
|
||||
|
After Width: | Height: | Size: 335 B |
1
samples/sp_whoIsActive/src/media/monitor.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>query_plan_16x16</title><path d="M13.16,10.59h1.9v5H10.32v-5h1.89v-2H3.68v2H5.58v5H.84v-5h1.9v-3H7.47v-2H5.58v-5h4.74v5H8.42v2h4.74Zm-8.53,4v-3H1.79v3Zm1.9-13v3H9.37v-3Zm7.58,13v-3H11.26v3Z"/></svg>
|
||||
|
After Width: | Height: | Size: 298 B |
1
samples/sp_whoIsActive/src/media/monitor_inverse.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>query_plan_inverse_16x16</title><path class="cls-1" d="M13.06,10.49H15v5H10.22v-5h1.89v-2H3.59v2H5.48v5H.75v-5h1.9v-3H7.38v-2H5.48v-5h4.74v5H8.33v2h4.74Zm-8.53,4v-3H1.69v3Zm1.9-13v3H9.27v-3Zm7.58,13v-3H11.17v3Z"/></svg>
|
||||
|
After Width: | Height: | Size: 365 B |
1
samples/sp_whoIsActive/src/media/start.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#3bb44a;}</style></defs><title>run</title><path class="cls-1" d="M3.24,0,14.61,8,3.24,16Zm2,12.07L11.13,8,5.24,3.88Z"/><path class="cls-1" d="M3.74,1l10,7-10,7Zm1,1.92V13.07L12,8Z"/></svg>
|
||||
|
After Width: | Height: | Size: 306 B |
30
samples/sp_whoIsActive/src/resources/templateTab.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
document.getElementById('frame').src = "{{{url}}}";
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#frame {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="frame" width="100%" height="100%" frameborder="0"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
8
samples/sp_whoIsActive/src/sql/cpuDelta.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'sp_WhoIsActive')
|
||||
EXEC sp_WhoIsActive
|
||||
@delta_interval = 1,
|
||||
@output_column_list = '[session_id][CPU_delta]',
|
||||
@sort_order = '[CPU_delta] DESC'
|
||||
ELSE
|
||||
SELECT 0;
|
||||
GO
|
||||
7
samples/sp_whoIsActive/src/sql/cpuUsage.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'sp_WhoIsActive')
|
||||
EXEC sp_WhoIsActive
|
||||
@output_column_list = '[session_id][CPU]',
|
||||
@sort_order = '[CPU] DESC'
|
||||
ELSE
|
||||
SELECT 0;
|
||||
GO
|
||||
6
samples/sp_whoIsActive/src/sql/findBlockLeaders.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'sp_WhoIsActive')
|
||||
EXEC sp_WhoIsActive
|
||||
@find_block_leaders = 1,
|
||||
@get_plans = 1,
|
||||
@sort_order = '[blocked_session_count] DESC'
|
||||
GO
|
||||
4
samples/sp_whoIsActive/src/sql/getPlans.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'sp_WhoIsActive')
|
||||
EXEC sp_WhoIsActive
|
||||
@get_plans = 1
|
||||
GO
|
||||
8
samples/sp_whoIsActive/src/sql/memoryDelta.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'sp_WhoIsActive')
|
||||
EXEC sp_WhoIsActive
|
||||
@delta_interval = 1,
|
||||
@output_column_list = '[session_id][used_memory_delta]',
|
||||
@sort_order = '[used_memory_delta] DESC'
|
||||
ELSE
|
||||
SELECT 0;
|
||||
GO
|
||||
7
samples/sp_whoIsActive/src/sql/memoryUsage.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'sp_WhoIsActive')
|
||||
EXEC sp_WhoIsActive
|
||||
@output_column_list = '[session_id][used_memory]',
|
||||
@sort_order = '[used_memory] DESC'
|
||||
ELSE
|
||||
SELECT 0;
|
||||
GO
|
||||
45
samples/sp_whoIsActive/src/utils.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
import * as fs from 'fs-extra';
|
||||
import * as handlebars from 'handlebars';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as Constants from './constants';
|
||||
import * as LocalizedConstants from './localizedConstants';
|
||||
|
||||
/**
|
||||
* Helper to log messages to the developer console if enabled
|
||||
* @param msg Message to log to the console
|
||||
*/
|
||||
export function logDebug(msg: any): void {
|
||||
let config = vscode.workspace.getConfiguration(Constants.extensionConfigSectionName);
|
||||
let logDebugInfo = config[Constants.configLogDebugInfo];
|
||||
if (logDebugInfo === true) {
|
||||
let currentTime = new Date().toLocaleTimeString();
|
||||
let outputMsg = '[' + currentTime + ']: ' + msg ? msg.toString() : '';
|
||||
console.log(outputMsg);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderTemplateHtml(extensionPath: string, templateName: string, templateValues: object): Thenable<string> {
|
||||
let templatePath = path.join(extensionPath, 'resources', templateName);
|
||||
|
||||
// 1) Read the template from the disk
|
||||
// 2) Compile it as a handlebars template and render the HTML
|
||||
// 3) On failure, return a simple string as an error
|
||||
return fs.readFile(templatePath, 'utf-8')
|
||||
.then(templateText => {
|
||||
let template = handlebars.compile(templateText);
|
||||
return template(templateValues);
|
||||
})
|
||||
.then(
|
||||
undefined,
|
||||
error => {
|
||||
logDebug(error);
|
||||
return LocalizedConstants.msgErrorLoadingTab;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||