This commit is contained in:
Anthony Dresser 2016-08-19 18:38:56 -07:00
Родитель a36bf3ebff
Коммит 180ed98254
10 изменённых файлов: 3198 добавлений и 106 удалений

2946
npm-shrinkwrap.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -58,14 +58,15 @@
"dependencies": {
"async": "^2.0.0-rc.3",
"ejs": "^2.4.2",
"error-ex": "^1.3.0",
"express": "^4.13.3",
"figures": "^1.4.0",
"getmac": "1.2.1",
"mssql": "^3.2.0",
"request": "^2.73.0",
"typemoq": "^0.3.2",
"vscode-extension-telemetry": "^0.0.5",
"vscode-languageclient": "^1.0.0",
"error-ex": "^1.3.0",
"figures": "^1.4.0"
"vscode-languageclient": "^1.0.0"
},
"contributes": {
"languages": [

Просмотреть файл

@ -23,7 +23,7 @@ export class QueryNotificationHandler {
// register the handler to handle notifications for queries
private initialize(): void {
SqlToolsServiceClient.getInstance().getClient().onNotification(QueryExecuteCompleteNotification.type, this.handleNotification());
SqlToolsServiceClient.instance.onNotification(QueryExecuteCompleteNotification.type, this.handleNotification());
}
// registers queryRunners with their uris to distribute notifications

Просмотреть файл

@ -7,7 +7,7 @@ import Interfaces = require('../models/interfaces');
import { ConnectionUI } from '../views/connectionUI';
import StatusView from '../views/statusView';
import SqlToolsServerClient from '../languageservice/serviceclient';
import { LanguageClient } from 'vscode-languageclient';
import { RequestType } from 'vscode-languageclient';
import { IPrompter } from '../prompts/question';
import Telemetry from '../models/telemetry';
@ -22,14 +22,14 @@ class ConnectionInfo {
// ConnectionManager class is the main controller for connection management
export default class ConnectionManager {
private _client: LanguageClient;
private _client: SqlToolsServerClient;
private _context: vscode.ExtensionContext;
private _statusView: StatusView;
private _prompter: IPrompter;
private _connections: { [fileUri: string]: ConnectionInfo };
private _connectionUI: ConnectionUI;
constructor(context: vscode.ExtensionContext, statusView: StatusView, prompter: IPrompter, client?: LanguageClient) {
constructor(context: vscode.ExtensionContext, statusView: StatusView, prompter: IPrompter, client?: SqlToolsServerClient) {
this._context = context;
this._statusView = statusView;
this._prompter = prompter;
@ -37,12 +37,20 @@ export default class ConnectionManager {
this._connections = {};
if (typeof client === 'undefined') {
this._client = SqlToolsServerClient.getInstance().getClient();
this.client = SqlToolsServerClient.instance;
} else {
this._client = client;
this.client = client;
}
}
private get client(): SqlToolsServerClient {
return this._client;
}
private set client(client: SqlToolsServerClient) {
this._client = client;
}
private get connectionUI(): ConnectionUI {
return this._connectionUI;
}
@ -154,7 +162,7 @@ export default class ConnectionManager {
let serviceTimer = new Utils.Timer();
// send connection request message to service host
self._client.sendRequest(Contracts.ConnectionRequest.type, connectParams).then((result) => {
self.client.sendRequest(Contracts.ConnectionRequest.type, connectParams).then((result) => {
// handle connection complete callback
serviceTimer.end();

Просмотреть файл

@ -75,7 +75,7 @@ export default class MainController implements vscode.Disposable {
this._context.subscriptions.push(registration);
// initialize language service client
SqlToolsServerClient.getInstance().initialize(this._context);
SqlToolsServerClient.instance.initialize(this._context);
activationTimer.end();
@ -107,7 +107,17 @@ export default class MainController implements vscode.Disposable {
if (!Utils.isEditingSqlFile()) {
Utils.showWarnMsg(Constants.msgOpenSqlFile);
} else {
this._outputContentProvider.runQuery(this._connectionMgr, this._statusview);
let editor = vscode.window.activeTextEditor;
let uri = 'vscode-mssql';
let title = editor.document.fileName;
let queryText: string;
if (editor.selection.isEmpty) {
queryText = editor.document.getText();
} else {
queryText = editor.document.getText(new vscode.Range(editor.selection.start, editor.selection.end));
}
this._outputContentProvider.runQuery(this._connectionMgr, this._statusview, uri, queryText, title);
}
}

Просмотреть файл

@ -1,14 +1,13 @@
'use strict';
import vscode = require('vscode');
import { SqlOutputContentProvider } from '../models/sqlOutputContentProvider';
import ConnectionManager from './connectionManager';
import StatusView from '../views/statusView';
import SqlToolsServerClient from '../languageservice/serviceclient';
import {QueryNotificationHandler} from './QueryNotificationHandler';
import * as Contracts from '../models/contracts';
import * as Utils from '../models/utils';
import VscodeWrapper from './vscodeWrapper';
interface IResultSet {
export interface IResultSet {
columns: string[];
totalNumberOfRows: number;
}
@ -17,7 +16,6 @@ interface IResultSet {
* and handles getting more rows from the service layer and disposing when the content is closed.
*/
export default class QueryRunner {
private _client: SqlToolsServerClient;
private _resultSets: Contracts.ResultSetSummary[];
private _uri: string;
private _title: string;
@ -25,15 +23,44 @@ export default class QueryRunner {
constructor(private _connectionMgr: ConnectionManager,
private _statusView: StatusView,
private _outputProvider: SqlOutputContentProvider) {
this.client = SqlToolsServerClient.getInstance();
private _outputProvider: SqlOutputContentProvider,
private _client?: SqlToolsServerClient,
private _notificationHandler?: QueryNotificationHandler,
private _vscodeWrapper?: VscodeWrapper) {
if (!_client) {
this.client = SqlToolsServerClient.instance;
}
if (!_notificationHandler) {
this.notificationHandler = QueryNotificationHandler.instance;
}
if (!_vscodeWrapper) {
this.vscodeWrapper = new VscodeWrapper();
}
}
private get uri(): string {
private get notificationHandler(): QueryNotificationHandler {
return this._notificationHandler;
}
private set notificationHandler(handler: QueryNotificationHandler) {
this._notificationHandler = handler;
}
private get vscodeWrapper(): VscodeWrapper {
return this._vscodeWrapper;
}
private set vscodeWrapper(wrapper: VscodeWrapper) {
this._vscodeWrapper = wrapper;
}
get uri(): string {
return this._uri;
}
private set uri(uri: string) {
set uri(uri: string) {
this._uri = uri;
}
@ -62,35 +89,30 @@ export default class QueryRunner {
}
// Pulls the query text from the current document/selection and initiates the query
public runQuery(): Thenable<void> {
public runQuery(uri: string, text: string, title: string): Thenable<void> {
const self = this;
let editor = vscode.window.activeTextEditor;
this.uri = editor.document.uri.toString();
this.title = editor.document.fileName;
let queryDetails = new Contracts.QueryExecuteParams();
queryDetails.ownerUri = this.uri;
if (editor.selection.isEmpty) {
queryDetails.queryText = editor.document.getText();
} else {
queryDetails.queryText = editor.document.getText(new vscode.Range(editor.selection.start, editor.selection.end));
}
queryDetails.ownerUri = uri;
queryDetails.queryText = text;
this.title = title;
this.uri = title;
return this._client.getClient().sendRequest(Contracts.QueryExecuteRequest.type, queryDetails).then(result => {
return this.client.sendRequest(Contracts.QueryExecuteRequest.type, queryDetails).then(result => {
if (result.messages) {
Utils.showErrorMsg('Execution failed: ' + result.messages);
self.vscodeWrapper.showErrorMessage('Execution failed: ' + result.messages);
} else {
// register with the Notification Handler
QueryNotificationHandler.instance.registerRunner(self, queryDetails.ownerUri);
self.notificationHandler.registerRunner(self, queryDetails.ownerUri);
}
}, error => {
Utils.showErrorMsg('Execution failed: ' + error);
self.vscodeWrapper.showErrorMessage('Execution failed: ' + error);
});
}
// handle the result of the notification
public handleResult(result: Contracts.QueryExecuteCompleteNotificationResult): void {
if (result.hasError) {
Utils.showErrorMsg('Something went wrong during the query: ' + result.messages[0]);
this.vscodeWrapper.showErrorMessage('Something went wrong during the query: ' + result.messages[0]);
} else {
this._resultSets = result.resultSetSummaries;
this._messages = result.messages;
@ -99,7 +121,11 @@ export default class QueryRunner {
}
// get more data rows from the current resultSets from the service layer
<<<<<<< a36bf3ebff8f6e58bfd6322cee36cee5eaafe56e
public getRows(rowStart: number, numberOfRows: number, resultSetIndex: number): Thenable<Contracts.QueryExecuteSubsetResult> {
=======
public getRows(rowStart: number, numberOfRows: number, resultSetIndex: number): Promise<Contracts.QueryExecuteSubsetResult> {
>>>>>>> added tests for query runner
const self = this;
let queryDetails = new Contracts.QueryExecuteSubsetParams();
queryDetails.ownerUri = this.uri;
@ -107,9 +133,10 @@ export default class QueryRunner {
queryDetails.rowsCount = numberOfRows;
queryDetails.rowsStartIndex = rowStart;
return new Promise<Contracts.QueryExecuteSubsetResult>((resolve, reject) => {
self._client.getClient().sendRequest(Contracts.QueryExecuteSubsetRequest.type, queryDetails).then(result => {
self.client.sendRequest(Contracts.QueryExecuteSubsetRequest.type, queryDetails).then(result => {
if (result.message) {
Utils.showErrorMsg('Something went wrong getting more rows: ' + result.message);
self.vscodeWrapper.showErrorMessage('Something went wrong getting more rows: ' + result.message);
reject();
} else {
resolve(result);
}
@ -119,18 +146,19 @@ export default class QueryRunner {
// dispose the query from front end and and back end
public dispose(): Promise<boolean> {
const self = this;
return new Promise<boolean>((resolve, reject) => {
let disposeDetails = new Contracts.QueryDisposeParams();
disposeDetails.ownerUri = this.uri;
this.client.getClient().sendRequest(Contracts.QueryDisposeRequest.type, disposeDetails).then(result => {
this.client.sendRequest(Contracts.QueryDisposeRequest.type, disposeDetails).then(result => {
if (result.messages) {
Utils.showErrorMsg('Failed disposing query: ' + result.messages);
self.vscodeWrapper.showErrorMessage('Failed disposing query: ' + result.messages);
resolve(false);
} else {
resolve(true);
}
}, error => {
Utils.showErrorMsg('Execution failed: ' + error);
self.vscodeWrapper.showErrorMessage('Execution failed: ' + error);
});
});
}

Просмотреть файл

@ -1,4 +1,5 @@
import vscode = require('vscode');
import * as Constants from './../models/constants';
export default class VscodeWrapper {
public get activeTextEditor(): vscode.TextEditor {
@ -13,16 +14,19 @@ export default class VscodeWrapper {
return undefined;
}
public range(start: vscode.Position, end: vscode.Position): vscode.Range {
return new vscode.Range(start, end);
}
public showErrorMessage(msg: string): Thenable<string> {
return undefined;
return vscode.window.showErrorMessage(Constants.extensionName + ': ' + msg );
}
public showInformationMessage(msg: string): Thenable<string> {
return undefined;
return vscode.window.showInformationMessage(Constants.extensionName + ': ' + msg );
}
public showWarningMessage(msg: string): Thenable<string> {
return undefined;
return vscode.window.showWarningMessage(Constants.extensionName + ': ' + msg );
}
}

Просмотреть файл

@ -6,7 +6,8 @@
import * as path from 'path';
import { ExtensionContext } from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
import { LanguageClient, LanguageClientOptions, ServerOptions,
TransportKind, RequestType, NotificationType, INotificationHandler } from 'vscode-languageclient';
// The Service Client class handles communication with the VS Code LanguageClient
export default class SqlToolsServiceClient {
@ -17,12 +18,16 @@ export default class SqlToolsServiceClient {
private _client: LanguageClient = undefined;
// getter method for the Language Client
public getClient(): LanguageClient {
private get client(): LanguageClient {
return this._client;
}
private set client(client: LanguageClient) {
this._client = client;
}
// gets or creates the singleton SQL Tools service client instance
public static getInstance(): SqlToolsServiceClient {
public static get instance(): SqlToolsServiceClient {
if (this._instance === undefined) {
this._instance = new SqlToolsServiceClient();
}
@ -47,13 +52,21 @@ export default class SqlToolsServiceClient {
};
// cache the client instance for later use
this._client = new LanguageClient('sqlserverclient', serverOptions, clientOptions);
this.client = new LanguageClient('sqlserverclient', serverOptions, clientOptions);
// Create the language client and start the client.
let disposable = this._client.start();
let disposable = this.client.start();
// Push the disposable to the context's subscriptions so that the
// client can be deactivated on extension deactivation
context.subscriptions.push(disposable);
}
public sendRequest<P, R, E>(type: RequestType<P, R, E>, params?: P): Thenable<R> {
return this.client.sendRequest(type, params);
}
public onNotification<P>(type: NotificationType<P>, handler: INotificationHandler<P>): void {
return this.client.onNotification(type, handler);
}
}

Просмотреть файл

@ -112,9 +112,9 @@ export class SqlOutputContentProvider implements vscode.TextDocumentContentProvi
vscode.commands.executeCommand('vscode.previewHtml', uri, vscode.ViewColumn.Two, 'SQL Query Results: ' + title);
}
public runQuery(connectionMgr, statusView): void {
public runQuery(connectionMgr, statusView, uri: string, text: string, title: string): void {
let queryRunner = new QueryRunner(connectionMgr, statusView, this);
queryRunner.runQuery();
queryRunner.runQuery(uri, text, title);
}
public updateContent(queryRunner: QueryRunner): string {

Просмотреть файл

@ -1,57 +1,145 @@
// import * as TypeMoq from 'typemoq';
// import * as vscode from 'vscode';
// import assert = require('assert');
// import QueryRunner from './../src/controllers/queryRunner';
// import { QueryNotificationHandler } from './../src/controllers/QueryNotificationHandler';
// import { SqlOutputContentProvider } from './../src/models/sqlOutputContentProvider';
// import SqlToolsServerClient from './../src/languageservice/serviceclient';
import * as TypeMoq from 'typemoq';
import assert = require('assert');
import QueryRunner from './../src/controllers/queryRunner';
import { QueryNotificationHandler } from './../src/controllers/QueryNotificationHandler';
import { SqlOutputContentProvider } from './../src/models/sqlOutputContentProvider';
import SqlToolsServerClient from './../src/languageservice/serviceclient';
import { QueryExecuteParams } from './../src/models/contracts';
import VscodeWrapper from './../src/controllers/vscodeWrapper';
// suite('Query Runner tests', () => {
suite('Query Runner tests', () => {
// let outputProvider: TypeMoq.Mock<SqlOutputContentProvider>;
// let notificationHandler: TypeMoq.GlobalMock<typeof QueryNotificationHandler>;
// let serviceClient: TypeMoq.GlobalMock<typeof SqlToolsServerClient>;
// let editorMoq: TypeMoq.GlobalMock<typeof vscode.window.activeTextEditor>;
// let fileName = 'testSql';
let testSqlOutputContentProvider: TypeMoq.Mock<SqlOutputContentProvider>;
let testSqlToolsServerClient: TypeMoq.Mock<SqlToolsServerClient>;
let testQueryNotificationHandler: TypeMoq.Mock<QueryNotificationHandler>;
let testVscodeWrapper: TypeMoq.Mock<VscodeWrapper>;
// setup(() => {
// outputProvider = TypeMoq.Mock.ofType(SqlOutputContentProvider);
// notificationHandler = TypeMoq.GlobalMock.ofInstance(QueryNotificationHandler);
// editorMoq = TypeMoq.GlobalMock.ofInstance(vscode.window.activeTextEditor);
// editorMoq.setup(x => x.document.fileName).returns(() => { return fileName; });
// serviceClient = TypeMoq.GlobalMock.ofInstance(SqlToolsServerClient);
// serviceClient.setup(x => x.getInstance());
// });
setup(() => {
testSqlOutputContentProvider = TypeMoq.Mock.ofType(SqlOutputContentProvider, TypeMoq.MockBehavior.Loose, {extensionPath: ''});
testSqlToolsServerClient = TypeMoq.Mock.ofType(SqlToolsServerClient);
testQueryNotificationHandler = TypeMoq.Mock.ofType(QueryNotificationHandler);
testVscodeWrapper = TypeMoq.Mock.ofType(VscodeWrapper);
// test('Constructs properly', () => {
// TypeMoq.GlobalScope.using(editorMoq).with(() => {
// TypeMoq.GlobalScope.using(serviceClient).with(() => {
// TypeMoq.GlobalScope.using(notificationHandler).with(() => {
// new QueryRunner(
// undefined,
// undefined,
// outputProvider.object
// );
// });
// });
// });
// });
});
// test('Run Query Test', () => {
// return new Promise<void>((resolve, reject) => {
// TypeMoq.GlobalScope.using(serviceClient).with(() => {
// TypeMoq.GlobalScope.using(notificationHandler).with(() => {
// let queryRunner = new QueryRunner(
// undefined,
// undefined,
// outputProvider.object
// );
// queryRunner.runQuery().then(() => {
// assert.equal(queryRunner.title, fileName);
// assert.equal(queryRunner.uri, 'vscode-mssql');
// });
// });
// });
// });
// });
// });
test('Constructs properly', () => {
let queryRunner = new QueryRunner(undefined,
undefined,
testSqlOutputContentProvider.object,
testSqlToolsServerClient.object,
testQueryNotificationHandler.object);
assert.equal(typeof queryRunner !== undefined, true);
});
test('Runs Query Corrects', () => {
let testuri = 'uri';
let testquery = 'SELECT * FROM sys.objects';
let testtitle = 'title';
testSqlToolsServerClient.setup(x => x.sendRequest(TypeMoq.It.isAny(),
TypeMoq.It.isAny())).callback((type, details: QueryExecuteParams) => {
assert.equal(details.ownerUri, testuri);
assert.equal(details.queryText, testquery);
})
.returns(() => { return Promise.resolve({messages: undefined}); });
testQueryNotificationHandler.setup(x => x.registerRunner(TypeMoq.It.isAny(),
TypeMoq.It.isAnyString())).callback((queryRunner, uri: string) => {
assert.equal(uri, testuri);
});
let queryRunner = new QueryRunner(
undefined,
undefined,
testSqlOutputContentProvider.object,
testSqlToolsServerClient.object,
testQueryNotificationHandler.object
);
return queryRunner.runQuery(testuri, testquery, testtitle).then(() => {
testQueryNotificationHandler.verify(x => x.registerRunner(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
});
});
test('Handles Query Error Correctly', () => {
let testuri = 'uri';
let testquery = 'SELECT * FROM sys.objects';
let testtitle = 'title';
testSqlToolsServerClient.setup(x => x.sendRequest(TypeMoq.It.isAny(),
TypeMoq.It.isAny())).callback((type, details: QueryExecuteParams) => {
assert.equal(details.ownerUri, testuri);
assert.equal(details.queryText, testquery);
})
.returns(() => { return Promise.resolve({messages: 'failed'}); });
testVscodeWrapper.setup(x => x.showErrorMessage(TypeMoq.It.isAnyString()));
let queryRunner = new QueryRunner(
undefined,
undefined,
undefined,
testSqlToolsServerClient.object,
testQueryNotificationHandler.object,
testVscodeWrapper.object
);
return queryRunner.runQuery(testuri, testquery, testtitle).then(() => {
testVscodeWrapper.verify(x => x.showErrorMessage(TypeMoq.It.isAnyString()), TypeMoq.Times.once());
});
});
test('Handles result correctly', () => {});
test('Handles result error correctly', () => {});
test('Correctly handles subset', () => {
let testuri = 'test';
let testresult = {
message: '',
resultSubset: {
rowCount: 5,
rows: [
['1', '2'],
['3', '4'],
['5', '6'],
['7', '8'],
['9', '10']
]
}
};
testSqlToolsServerClient.setup(x => x.sendRequest(TypeMoq.It.isAny(),
TypeMoq.It.isAny())).callback(() => {
// testing
}).returns(() => { return Promise.resolve(testresult); });
let queryRunner = new QueryRunner(
undefined,
undefined,
undefined,
testSqlToolsServerClient.object,
testQueryNotificationHandler.object
);
queryRunner.uri = testuri;
return queryRunner.getRows(0, 5, 0).then(result => {
assert.equal(result, testresult);
});
});
test('Correctly handles error from subset request', () => {
let testuri = 'test';
let testresult = {
message: 'failed'
};
testSqlToolsServerClient.setup(x => x.sendRequest(TypeMoq.It.isAny(),
TypeMoq.It.isAny())).callback(() => {
// testing
}).returns(() => { return Promise.resolve(testresult); });
testVscodeWrapper.setup(x => x.showErrorMessage(TypeMoq.It.isAnyString()));
let queryRunner = new QueryRunner(
undefined,
undefined,
undefined,
testSqlToolsServerClient.object,
testQueryNotificationHandler.object,
testVscodeWrapper.object
);
queryRunner.uri = testuri;
return queryRunner.getRows(0, 5, 0).then(undefined, () => {
testVscodeWrapper.verify(x => x.showErrorMessage(TypeMoq.It.isAnyString()), TypeMoq.Times.once());
});
});
});