Refactor out extensions-modules and rewrite mssql extension (#909)

* commting .d.ts changes

* added serverinfo to .d.ts

* maybe its working?

* works

* updated contrib

* remove unnecessary code

* fix compile errors

* init

* conitnue

* on the way to working maybe?

* close

* EVERYTHING WORKS

* moved src out of client folder

* formatting

* reenable logging

* working on build file

* fixed install service gulp command

* fix the command to properly return promises

* clean up code

* add telemetry

* formatting

* added logging/telemetry/statusview

* formatting

* address comments

* resolute vscode-language versions
This commit is contained in:
Anthony Dresser
2018-03-28 10:59:08 -07:00
committed by GitHub
parent 22c54a9917
commit 2414f43757
62 changed files with 3396 additions and 11239 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,147 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import * as sqlops from 'sqlops';
import { Constants } from '../models/constants';
import { Serialization } from '../serialize/serialization';
import { CredentialStore } from '../credentialstore/credentialstore';
import { AzureResourceProvider } from '../resourceProvider/resourceProvider';
import { IExtensionConstants, Telemetry, Constants as SharedConstants, SqlToolsServiceClient, VscodeWrapper, Utils, PlatformInformation } from 'extensions-modules';
import { SqlOpsDataClient } from 'dataprotocol-client';
import * as path from 'path';
/**
* The main controller class that initializes the extension
*/
export default class MainController implements vscode.Disposable {
private _context: vscode.ExtensionContext;
private _vscodeWrapper: VscodeWrapper;
private _initialized: boolean = false;
private _serialization: Serialization;
private _credentialStore: CredentialStore;
private static _extensionConstants: IExtensionConstants = new Constants();
private _client: SqlToolsServiceClient;
/**
* The main controller constructor
* @constructor
*/
constructor(context: vscode.ExtensionContext,
vscodeWrapper?: VscodeWrapper) {
this._context = context;
this._vscodeWrapper = vscodeWrapper || new VscodeWrapper(MainController._extensionConstants);
SqlToolsServiceClient.constants = MainController._extensionConstants;
this._client = SqlToolsServiceClient.getInstance(path.join(__dirname, '../config.json'));
this._credentialStore = new CredentialStore(this._client);
this._serialization = new Serialization(this._client);
}
/**
* Disposes the controller
*/
dispose(): void {
this.deactivate();
}
/**
* Deactivates the extension
*/
public deactivate(): void {
Utils.logDebug(SharedConstants.extensionDeactivated, MainController._extensionConstants.extensionConfigSectionName);
}
/**
* Initializes the extension
*/
public activate(): Promise<boolean> {
return this.initialize();
}
/**
* Returns a flag indicating if the extension is initialized
*/
public isInitialized(): boolean {
return this._initialized;
}
private createClient(executableFiles: string[]): Promise<SqlOpsDataClient> {
return PlatformInformation.getCurrent(SqlToolsServiceClient.constants.getRuntimeId, SqlToolsServiceClient.constants.extensionName).then(platformInfo => {
return SqlToolsServiceClient.getInstance(path.join(__dirname, '../config.json')).createClient(this._context, platformInfo.runtimeId, undefined, executableFiles);
});
}
private createCredentialClient(): Promise<SqlOpsDataClient> {
return this.createClient(['MicrosoftSqlToolsCredentials.exe', 'MicrosoftSqlToolsCredentials']);
}
private createResourceProviderClient(): Promise<SqlOpsDataClient> {
return this.createClient(['SqlToolsResourceProviderService.exe', 'SqlToolsResourceProviderService']);
}
/**
* Initializes the extension
*/
public initialize(): Promise<boolean> {
// initialize language service client
return new Promise<boolean>((resolve, reject) => {
const self = this;
SqlToolsServiceClient.getInstance(path.join(__dirname, '../config.json')).initialize(self._context).then(serverResult => {
// Initialize telemetry
Telemetry.initialize(self._context, new Constants());
// telemetry for activation
Telemetry.sendTelemetryEvent('ExtensionActivated', {},
{ serviceInstalled: serverResult.installedBeforeInitializing ? 1 : 0 }
);
self.createResourceProviderClient().then(rpClient => {
let resourceProvider = new AzureResourceProvider(self._client, rpClient);
sqlops.resources.registerResourceProvider({
displayName: 'Azure SQL Resource Provider', // TODO Localize
id: 'Microsoft.Azure.SQL.ResourceProvider',
settings: {
}
}, resourceProvider);
Utils.logDebug('resourceProvider registered', MainController._extensionConstants.extensionConfigSectionName);
}, error => {
Utils.logDebug('Cannot find ResourceProvider executables. error: ' + error, MainController._extensionConstants.extensionConfigSectionName);
});
self.createCredentialClient().then(credentialClient => {
self._credentialStore.languageClient = credentialClient;
credentialClient.onReady().then(() => {
let credentialProvider: sqlops.CredentialProvider = {
handle: 0,
saveCredential(credentialId: string, password: string): Thenable<boolean> {
return self._credentialStore.saveCredential(credentialId, password);
},
readCredential(credentialId: string): Thenable<sqlops.Credential> {
return self._credentialStore.readCredential(credentialId);
},
deleteCredential(credentialId: string): Thenable<boolean> {
return self._credentialStore.deleteCredential(credentialId);
}
};
sqlops.credentials.registerProvider(credentialProvider);
Utils.logDebug('credentialProvider registered', MainController._extensionConstants.extensionConfigSectionName);
});
}, error => {
Utils.logDebug('Cannot find credentials executables. error: ' + error, MainController._extensionConstants.extensionConfigSectionName);
});
Utils.logDebug(SharedConstants.extensionActivated, MainController._extensionConstants.extensionConfigSectionName);
self._initialized = true;
resolve(true);
}).catch(err => {
Telemetry.sendTelemetryEventForException(err, 'initialize', MainController._extensionConstants.extensionConfigSectionName);
reject(err);
});
});
}
}

View File

@@ -1,74 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as Contracts from '../models/contracts';
import { ICredentialStore } from './icredentialstore';
import { SqlToolsServiceClient, Utils } from 'extensions-modules';
import { SqlOpsDataClient } from 'dataprotocol-client';
import * as path from 'path';
/**
* Implements a credential storage for Windows, Mac (darwin), or Linux.
*
* Allows a single credential to be stored per service (that is, one username per service);
*/
export class CredentialStore implements ICredentialStore {
public languageClient: SqlOpsDataClient;
constructor(private _client?: SqlToolsServiceClient) {
if (!this._client) {
this._client = SqlToolsServiceClient.getInstance(path.join(__dirname, '../config.json'));
}
}
/**
* Gets a credential saved in the credential store
*
* @param {string} credentialId the ID uniquely identifying this credential
* @returns {Promise<Credential>} Promise that resolved to the credential, or undefined if not found
*/
public readCredential(credentialId: string): Promise<Contracts.Credential> {
Utils.logDebug(this.languageClient, 'MainController._extensionConstants');
let self = this;
let cred: Contracts.Credential = new Contracts.Credential();
cred.credentialId = credentialId;
return new Promise<Contracts.Credential>((resolve, reject) => {
self._client
.sendRequest(Contracts.ReadCredentialRequest.type, cred, this.languageClient)
.then(returnedCred => {
resolve(<Contracts.Credential>returnedCred);
}, err => reject(err));
});
}
public saveCredential(credentialId: string, password: any): Promise<boolean> {
let self = this;
let cred: Contracts.Credential = new Contracts.Credential();
cred.credentialId = credentialId;
cred.password = password;
return new Promise<boolean>((resolve, reject) => {
self._client
.sendRequest(Contracts.SaveCredentialRequest.type, cred, this.languageClient)
.then(status => {
resolve(<boolean>status);
}, err => reject(err));
});
}
public deleteCredential(credentialId: string): Promise<boolean> {
let self = this;
let cred: Contracts.Credential = new Contracts.Credential();
cred.credentialId = credentialId;
return new Promise<boolean>((resolve, reject) => {
self._client
.sendRequest(Contracts.DeleteCredentialRequest.type, cred, this.languageClient)
.then(status => {
resolve(<boolean>status);
}, err => reject(err));
});
}
}

View File

@@ -1,23 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
// This code is originally from https://github.com/microsoft/vsts-vscode
// License: https://github.com/Microsoft/vsts-vscode/blob/master/LICENSE.txt
import { Credential } from '../models/contracts';
/**
* A credential store that securely stores sensitive information in a platform-specific manner
*
* @export
* @interface ICredentialStore
*/
export interface ICredentialStore {
readCredential(credentialId: string): Promise<Credential>;
saveCredential(credentialId: string, password: any): Promise<boolean>;
deleteCredential(credentialId: string): Promise<boolean>;
}

View File

@@ -1,26 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import vscode = require('vscode');
import MainController from './controllers/mainController';
import ContextProvider from './contextProvider';
export let controller: MainController;
export function activate(context: vscode.ExtensionContext) {
controller = new MainController(context);
let contextProvider = new ContextProvider();
context.subscriptions.push(controller);
context.subscriptions.push(contextProvider);
controller.activate();
}
// this method is called when your extension is deactivated
export function deactivate(): void {
if (controller) {
controller.deactivate();
}
}

View File

@@ -1,143 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IExtensionConstants } from 'extensions-modules/lib/models/contracts/contracts';
import { Runtime, LinuxDistribution } from 'extensions-modules/lib/models/platform';
// constants
export class Constants implements IExtensionConstants {
public readonly languageId = 'sql';
public readonly extensionName = 'mssql';
public readonly extensionConfigSectionName = 'mssql';
public readonly outputChannelName = 'MSSQL';
public readonly providerId = 'MSSQL';
public readonly installFolderName = 'sqltoolsservice';
public readonly telemetryExtensionName = 'carbon-mssql';
// localizable strings
public readonly serviceCompatibleVersion = '1.0.0';
public readonly serviceInstallingTo = 'Installing SQL tools service to';
public readonly serviceInitializing = 'Initializing SQL tools service for the mssql extension.';
public readonly commandsNotAvailableWhileInstallingTheService = 'Note: mssql commands will be available after installing the service.';
public readonly serviceInstalled = 'Sql Tools Service installed';
public readonly serviceInstallationFailed = 'Failed to install Sql Tools Service';
public readonly serviceLoadingFailed = 'Failed to load Sql Tools Service';
public readonly invalidServiceFilePath = 'Invalid file path for Sql Tools Service';
public readonly serviceName = 'SQLToolsService';
public readonly serviceInitializingOutputChannelName = 'SqlToolsService Initialization';
public readonly serviceCrashMessage = 'SQL Tools Service component exited unexpectedly. Please restart SQL Operations Studio.';
public readonly serviceCrashLink = 'https://github.com/Microsoft/vscode-mssql/wiki/SqlToolsService-Known-Issues';
/**
* Returns a supported .NET Core Runtime ID (RID) for the current platform. The list of Runtime IDs
* is available at https://github.com/dotnet/corefx/tree/master/pkg/Microsoft.NETCore.Platforms.
*/
public getRuntimeId(platform: string, architecture: string, distribution: LinuxDistribution): Runtime {
switch (platform) {
case 'win32':
switch (architecture) {
case 'x86': return Runtime.Windows_86;
case 'x86_64': return Runtime.Windows_64;
default:
}
throw new Error(`Unsupported Windows architecture: ${architecture}`);
case 'darwin':
if (architecture === 'x86_64') {
// Note: We return the El Capitan RID for Sierra
return Runtime.OSX;
}
throw new Error(`Unsupported macOS architecture: ${architecture}`);
case 'linux':
if (architecture === 'x86_64') {
// First try the distribution name
let runtimeId = Constants.getRuntimeIdHelper(distribution.name, distribution.version);
// If the distribution isn't one that we understand, but the 'ID_LIKE' field has something that we understand, use that
//
// NOTE: 'ID_LIKE' doesn't specify the version of the 'like' OS. So we will use the 'VERSION_ID' value. This will restrict
// how useful ID_LIKE will be since it requires the version numbers to match up, but it is the best we can do.
if (runtimeId === Runtime.UnknownRuntime && distribution.idLike && distribution.idLike.length > 0) {
for (let id of distribution.idLike) {
runtimeId = Constants.getRuntimeIdHelper(id, distribution.version);
if (runtimeId !== Runtime.UnknownRuntime) {
break;
}
}
}
if (runtimeId !== Runtime.UnknownRuntime && runtimeId !== Runtime.UnknownVersion) {
return runtimeId;
}
}
// If we got here, this is not a Linux distro or architecture that we currently support.
throw new Error(`Unsupported Linux distro: ${distribution.name}, ${distribution.version}, ${architecture}`);
default:
// If we got here, we've ended up with a platform we don't support like 'freebsd' or 'sunos'.
// Chances are, VS Code doesn't support these platforms either.
throw Error('Unsupported platform ' + platform);
}
}
private static getRuntimeIdHelper(distributionName: string, distributionVersion: string): Runtime {
switch (distributionName) {
case 'ubuntu':
if (distributionVersion.startsWith('14')) {
// This also works for Linux Mint
return Runtime.Ubuntu_14;
} else if (distributionVersion.startsWith('16')) {
return Runtime.Ubuntu_16;
}
break;
case 'elementary':
case 'elementary OS':
if (distributionVersion.startsWith('0.3')) {
// Elementary OS 0.3 Freya is binary compatible with Ubuntu 14.04
return Runtime.Ubuntu_14;
} else if (distributionVersion.startsWith('0.4')) {
// Elementary OS 0.4 Loki is binary compatible with Ubuntu 16.04
return Runtime.Ubuntu_16;
}
break;
case 'linuxmint':
if (distributionVersion.startsWith('18')) {
// Linux Mint 18 is binary compatible with Ubuntu 16.04
return Runtime.Ubuntu_16;
}
break;
case 'centos':
case 'ol':
// Oracle Linux is binary compatible with CentOS
return Runtime.CentOS_7;
case 'fedora':
return Runtime.Fedora_23;
case 'opensuse':
return Runtime.OpenSUSE_13_2;
case 'sles':
return Runtime.SLES_12_2;
case 'rhel':
return Runtime.RHEL_7;
case 'debian':
return Runtime.Debian_8;
case 'galliumos':
if (distributionVersion.startsWith('2.0')) {
return Runtime.Ubuntu_16;
}
break;
default:
return Runtime.UnknownRuntime;
}
return Runtime.UnknownVersion;
}
}

View File

@@ -1,101 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { RequestType } from 'vscode-languageclient';
import * as sqlops from 'sqlops';
// DEV-NOTE: Still finalizing what we'll need as part of this interface
/**
* Contains necessary information for serializing and saving results
* @param {string} saveFormat the format / type that the results will be saved in
* @param {string} savePath path the results will be saved to
* @param {string} results either a subset or all of the results we wish to save to savePath
* @param {boolean} appendToFile Whether we should append or overwrite the file in savePath
*/
export class SaveResultsInfo {
constructor(public saveFormat: string, public savePath: string, public results: string,
public appendToFile: boolean) {
}
}
export namespace SaveAsRequest {
export const type = new RequestType<SaveResultsInfo, sqlops.SaveResultRequestResult, void, void>('query/saveAs');
}
// --------------------------------- < Read Credential Request > -------------------------------------------------
// Read Credential request message callback declaration
export namespace ReadCredentialRequest {
export const type = new RequestType<Credential, Credential, void, void>('credential/read');
}
/**
* Parameters to initialize a connection to a database
*/
export class Credential {
/**
* Unique ID identifying the credential
*/
public credentialId: string;
/**
* password
*/
public password: string;
}
// --------------------------------- </ Read Credential Request > -------------------------------------------------
// --------------------------------- < Save Credential Request > -------------------------------------------------
// Save Credential request message callback declaration
export namespace SaveCredentialRequest {
export const type = new RequestType<Credential, boolean, void, void>('credential/save');
}
// --------------------------------- </ Save Credential Request > -------------------------------------------------
// --------------------------------- < Delete Credential Request > -------------------------------------------------
// Delete Credential request message callback declaration
export namespace DeleteCredentialRequest {
export const type = new RequestType<Credential, boolean, void, void>('credential/delete');
}
// --------------------------------- </ Delete Credential Request > -------------------------------------------------
// ------------------------------- < Resource Events > ------------------------------------
export namespace CreateFirewallRuleRequest {
export const type = new RequestType<CreateFirewallRuleParams, CreateFirewallRuleResponse, void, void>('resource/createFirewallRule');
}
export namespace HandleFirewallRuleRequest {
export const type = new RequestType<HandleFirewallRuleParams, HandleFirewallRuleResponse, void, void>('resource/handleFirewallRule');
}
// Firewall rule interfaces
export interface CreateFirewallRuleParams {
account: sqlops.Account;
serverName: string;
startIpAddress: string;
endIpAddress: string;
securityTokenMappings: {};
}
export interface CreateFirewallRuleResponse {
result: boolean;
errorMessage: string;
}
export interface HandleFirewallRuleParams {
errorCode: number;
errorMessage: string;
connectionTypeId: string;
}
export interface HandleFirewallRuleResponse {
result: boolean;
ipAddress: string;
}

View File

@@ -1,64 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as Contracts from '../models/contracts';
import { SqlToolsServiceClient } from 'extensions-modules';
import { SqlOpsDataClient } from 'dataprotocol-client';
import * as sqlops from 'sqlops';
import * as path from 'path';
/**
* Implements a credential storage for Windows, Mac (darwin), or Linux.
*
* Allows a single credential to be stored per service (that is, one username per service);
*/
export class AzureResourceProvider implements sqlops.ResourceProvider {
public languageClient: SqlOpsDataClient;
constructor(private _client?: SqlToolsServiceClient, langClient?: SqlOpsDataClient) {
if (!this._client) {
this._client = SqlToolsServiceClient.getInstance(path.join(__dirname, '../config.json'));
}
this.languageClient = langClient;
}
public createFirewallRule(account: sqlops.Account, firewallruleInfo: sqlops.FirewallRuleInfo): Thenable<sqlops.CreateFirewallRuleResponse> {
let self = this;
return new Promise<sqlops.CreateFirewallRuleResponse>((resolve, reject) => {
self._client.
sendRequest(Contracts.CreateFirewallRuleRequest.type, self.asCreateFirewallRuleParams(account, firewallruleInfo), self.languageClient)
.then(response => {
resolve(response);
}, err => reject(err));
});
}
public handleFirewallRule(errorCode: number, errorMessage: string, connectionTypeId: string): Thenable<sqlops.HandleFirewallRuleResponse> {
let self = this;
return new Promise<sqlops.HandleFirewallRuleResponse>((resolve, reject) => {
let params: Contracts.HandleFirewallRuleParams = { errorCode: errorCode, errorMessage: errorMessage, connectionTypeId: connectionTypeId };
self._client.
sendRequest(Contracts.HandleFirewallRuleRequest.type, params, self.languageClient)
.then(response => {
resolve(response);
}, err => reject(err));
});
}
private asCreateFirewallRuleParams(account: sqlops.Account, params: sqlops.FirewallRuleInfo): Contracts.CreateFirewallRuleParams {
return {
account: account,
serverName: params.serverName,
startIpAddress: params.startIpAddress,
endIpAddress: params.endIpAddress,
securityTokenMappings: params.securityTokenMappings
};
}
}

View File

@@ -1,42 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as Contracts from '../models/contracts';
import { ISerialization } from './iserialization';
import { SqlToolsServiceClient } from 'extensions-modules';
import * as sqlops from 'sqlops';
import { SqlOpsDataClient } from 'dataprotocol-client';
import * as path from 'path';
/**
* Implements serializer for query results
*/
export class Serialization implements ISerialization {
constructor(private _client?: SqlToolsServiceClient, private _languageClient?: SqlOpsDataClient) {
if (!this._client) {
this._client = SqlToolsServiceClient.getInstance(path.join(__dirname, '../config.json'));
}
}
/**
* Saves results as a specified path
*
* @param {string} credentialId the ID uniquely identifying this credential
* @returns {Promise<ISaveResultsInfo>} Promise that resolved to the credential, or undefined if not found
*/
public saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Promise<sqlops.SaveResultRequestResult> {
let self = this;
let resultsInfo: Contracts.SaveResultsInfo = new Contracts.SaveResultsInfo(saveFormat, savePath, results, appendToFile);
return new Promise<sqlops.SaveResultRequestResult>((resolve, reject) => {
self._client
.sendRequest(Contracts.SaveAsRequest.type, resultsInfo, this._languageClient)
.then(result => {
resolve(<sqlops.SaveResultRequestResult>result);
}, err => reject(err));
});
}
}

View File

@@ -1,4 +0,0 @@
{
"name": "mssql",
"version": "0.1.0"
}

View File

@@ -3,14 +3,13 @@
"version": "0.1.0",
"publisher": "Microsoft",
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
"engines": {
"vscode": "0.10.x",
"sqlops": "feature/agent1"
},
"activationEvents": [
"*"
],
"main": "./client/out/main",
"engines": {
"vscode": "*"
},
"main": "./out/main",
"extensionDependencies": [
"vscode.sql"
],
@@ -659,9 +658,16 @@
}
},
"dependencies": {
"extensions-modules": "file:../../extensions-modules"
"crypto": "^1.0.1",
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.5",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
"vscode-extension-telemetry": "^0.0.15"
},
"devDependencies": {
"vscode": "1.0.1"
"resolutions": {
"vscode-jsonrpc": "3.5.0",
"vscode-languageclient": "3.5.0",
"vscode-languageserver-protocol": "3.5.0",
"vscode-languageserver-types": "3.5.0"
}
}

View File

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

View File

@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* 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 serviceName = 'SqlToolsService';
export const providerId = 'MSSQL';
export const serviceCrashMessage = 'SQL Tools Service component exited unexpectedly. Please restart SQL Operations Studio.';
export const serviceCrashButton = 'View Known Issues';
export const serviceCrashLink = 'https://github.com/Microsoft/vscode-mssql/wiki/SqlToolsService-Known-Issues';

View File

@@ -0,0 +1,80 @@
/*---------------------------------------------------------------------------------------------
* 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 { NotificationType, RequestType } from 'vscode-languageclient';
import * as sqlops from 'sqlops';
import { ITelemetryEventProperties, ITelemetryEventMeasures } from './telemetry';
// ------------------------------- < 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;
};
}
// ------------------------------- </ Telemetry Sent Event > ----------------------------------
// Job Management types
export interface AgentJobsParams {
ownerUri: string;
jobId: string;
}
export interface AgentJobsResult {
succeeded: boolean;
errorMessage: string;
jobs: sqlops.AgentJobInfo[];
}
export interface AgentJobHistoryParams {
ownerUri: string;
jobId: string;
}
export interface AgentJobHistoryResult {
succeeded: boolean;
errorMessage: string;
jobs: sqlops.AgentJobHistoryInfo[];
}
export interface AgentJobActionParams {
ownerUri: string;
jobName: string;
action: string;
}
export interface AgentJobActionResult {
succeeded: boolean;
errorMessage: string;
}
export namespace AgentJobsRequest {
export const type = new RequestType<AgentJobsParams, AgentJobsResult, void, void>('agent/jobs');
}
export namespace AgentJobHistoryRequest {
export const type = new RequestType<AgentJobHistoryParams, AgentJobHistoryResult, void, void>('agent/jobhistory');
}
export namespace AgentJobActionRequest {
export const type = new RequestType<AgentJobActionParams, AgentJobActionResult, void, void>('agent/jobaction');
}

View File

@@ -4,14 +4,5 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
/**
* Serializer for saving results into a different format
*
* @export
* @interface ISerialization
*/
export interface ISerialization {
saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Promise<sqlops.SaveResultRequestResult>;
}
export const serviceName = 'SerilizationProvider';
export const providerId = 'serilizationProvider';

View File

@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* 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 } from 'vscode-languageclient';
import { Credential } from 'sqlops';
// --------------------------------- < Read Credential Request > -------------------------------------------------
// Read Credential request message callback declaration
export namespace ReadCredentialRequest {
export const type = new RequestType<Credential, Credential, void, void>('credential/read');
}
// --------------------------------- </ Read Credential Request > -------------------------------------------------
// --------------------------------- < Save Credential Request > -------------------------------------------------
// Save Credential request message callback declaration
export namespace SaveCredentialRequest {
export const type = new RequestType<Credential, boolean, void, void>('credential/save');
}
// --------------------------------- </ Save Credential Request > -------------------------------------------------
// --------------------------------- < Delete Credential Request > -------------------------------------------------
// Delete Credential request message callback declaration
export namespace DeleteCredentialRequest {
export const type = new RequestType<Credential, boolean, void, void>('credential/delete');
}
// --------------------------------- </ Delete Credential Request > -------------------------------------------------

View File

@@ -0,0 +1,110 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { SqlOpsDataClient, ClientOptions, SqlOpsFeature } from 'dataprotocol-client';
import * as path from 'path';
import { IConfig, ServerProvider } from 'service-downloader';
import { ServerOptions, RPCMessageType, ClientCapabilities, ServerCapabilities, TransportKind } from 'vscode-languageclient';
import { Disposable } from 'vscode';
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
import * as sqlops from 'sqlops';
import * as Contracts from './contracts';
import * as Constants from './constants';
import * as Utils from '../utils';
class CredentialsFeature extends SqlOpsFeature<any> {
private static readonly messagesTypes: RPCMessageType[] = [
Contracts.DeleteCredentialRequest.type,
Contracts.SaveCredentialRequest.type,
Contracts.ReadCredentialRequest.type
];
constructor(client: SqlOpsDataClient) {
super(client, CredentialsFeature.messagesTypes);
}
fillClientCapabilities(capabilities: ClientCapabilities): void {
Utils.ensure(Utils.ensure(capabilities, 'credentials')!, 'credentials')!.dynamicRegistration = true;
}
initialize(capabilities: ServerCapabilities): void {
this.register(this.messages, {
id: UUID.generateUuid(),
registerOptions: undefined
});
}
protected registerProvider(options: any): Disposable {
const client = this._client;
let readCredential = (credentialId: string): Thenable<sqlops.Credential> => {
return client.sendRequest(Contracts.ReadCredentialRequest.type, { credentialId });
};
let saveCredential = (credentialId: string, password: string): Thenable<boolean> => {
return client.sendRequest(Contracts.SaveCredentialRequest.type, { credentialId, password });
};
let deleteCredential = (credentialId: string): Thenable<boolean> => {
return client.sendRequest(Contracts.DeleteCredentialRequest.type, { credentialId });
};
return sqlops.credentials.registerProvider({
deleteCredential,
readCredential,
saveCredential,
handle: 0
});
}
}
/**
* Implements a credential storage for Windows, Mac (darwin), or Linux.
*
* Allows a single credential to be stored per service (that is, one username per service);
*/
export class CredentialStore {
private _client: SqlOpsDataClient;
private _config: IConfig;
constructor(baseConfig: IConfig) {
if (baseConfig) {
this._config = JSON.parse(JSON.stringify(baseConfig));
this._config.executableFiles = ['MicrosoftSqlToolsCredentials.exe', 'MicrosoftSqlToolsCredentials'];
}
}
public start() {
let serverdownloader = new ServerProvider(this._config);
let clientOptions: ClientOptions = {
providerId: Constants.providerId,
features: [CredentialsFeature]
};
serverdownloader.getOrDownloadServer().then(e => {
let serverOptions = this.generateServerOptions(e);
this._client = new SqlOpsDataClient(Constants.serviceName, serverOptions, clientOptions);
this._client.start();
});
}
dispose() {
if (this._client) {
this._client.stop();
}
}
private generateServerOptions(executablePath: string): ServerOptions {
let launchArgs = [];
launchArgs.push('--log-dir');
let logFileLocation = path.join(Utils.getDefaultLogLocation(), 'mssql');
launchArgs.push(logFileLocation);
return { command: executablePath, args: launchArgs, transport: TransportKind.stdio };
}
}

View File

@@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* 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 * as sqlops from 'sqlops';
import { Telemetry } from './telemetry';
import * as Utils from './utils';
import { TelemetryNotification, AgentJobsRequest, AgentJobActionRequest, AgentJobHistoryRequest, AgentJobsParams, AgentJobHistoryParams, AgentJobActionParams } from './contracts';
export class TelemetryFeature implements StaticFeature {
constructor(private _client: SqlOpsDataClient) { }
fillClientCapabilities(capabilities: ClientCapabilities): void {
Utils.ensure(capabilities, 'telemetry')!.telemetry = true;
}
initialize(): void {
this._client.onNotification(TelemetryNotification.type, e => {
Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures);
});
}
}
export class AgentServicesFeature extends SqlOpsFeature<undefined> {
private static readonly messagesTypes: RPCMessageType[] = [
AgentJobsRequest.type,
AgentJobHistoryRequest.type,
AgentJobActionRequest.type
];
constructor(client: SqlOpsDataClient) {
super(client, AgentServicesFeature.messagesTypes);
}
public fillClientCapabilities(capabilities: ClientCapabilities): void {
// this isn't explicitly necessary
// ensure(ensure(capabilities, 'connection')!, 'agentServices')!.dynamicRegistration = true;
}
public initialize(capabilities: ServerCapabilities): void {
this.register(this.messages, {
id: UUID.generateUuid(),
registerOptions: undefined
});
}
protected registerProvider(options: undefined): Disposable {
const client = this._client;
let getJobs = (ownerUri: string): Thenable<sqlops.AgentJobsResult> => {
let params: AgentJobsParams = { ownerUri: ownerUri, jobId: null };
return client.sendRequest(AgentJobsRequest.type, params).then(
r => r,
e => {
client.logFailedRequest(AgentJobsRequest.type, e);
return Promise.resolve(undefined);
}
);
};
let getJobHistory = (connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> => {
let params: AgentJobHistoryParams = { ownerUri: connectionUri, jobId: jobID };
return client.sendRequest(AgentJobHistoryRequest.type, params).then(
r => r,
e => {
client.logFailedRequest(AgentJobHistoryRequest.type, e);
return Promise.resolve(undefined);
}
);
};
let jobAction = (connectionUri: string, jobName: string, action: string): Thenable<sqlops.AgentJobActionResult> => {
let params: AgentJobActionParams = { ownerUri: connectionUri, jobName: jobName, action: action };
return client.sendRequest(AgentJobActionRequest.type, params).then(
r => r,
e => {
client.logFailedRequest(AgentJobActionRequest.type, e);
return Promise.resolve(undefined);
}
);
};
return sqlops.dataprotocol.registerAgentServicesProvider({
providerId: client.providerId,
getJobs,
getJobHistory,
jobAction
});
}
}

View File

@@ -0,0 +1,136 @@
/*---------------------------------------------------------------------------------------------
* 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 path from 'path';
import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client';
import { IConfig, ServerProvider, Events } from 'service-downloader';
import { ServerOptions, TransportKind } from 'vscode-languageclient';
import * as Constants from './constants';
import ContextProvider from './contextProvider';
import { CredentialStore } from './credentialstore/credentialstore';
import { AzureResourceProvider } from './resourceProvider/resourceProvider';
import * as Utils from './utils';
import { Telemetry, LanguageClientErrorHandler } from './telemetry';
import { TelemetryFeature } from './features';
const baseConfig = require('./config.json');
const outputChannel = vscode.window.createOutputChannel(Constants.serviceName);
const statusView = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
export async function activate(context: vscode.ExtensionContext) {
// lets make sure we support this platform first
let supported = await Utils.verifyPlatform();
if (!supported) {
vscode.window.showErrorMessage('Unsupported platform');
return;
}
let config: IConfig = JSON.parse(JSON.stringify(baseConfig));
config.installDirectory = path.join(__dirname, config.installDirectory);
config.proxy = vscode.workspace.getConfiguration('http').get('proxy');
config.strictSSL = vscode.workspace.getConfiguration('http').get('proxyStrictSSL') || true;
const credentialsStore = new CredentialStore(config);
const resourceProvider = new AzureResourceProvider(config);
let languageClient: SqlOpsDataClient;
const serverdownloader = new ServerProvider(config);
serverdownloader.eventEmitter.onAny(generateHandleServerProviderEvent());
let clientOptions: ClientOptions = {
providerId: Constants.providerId,
errorHandler: new LanguageClientErrorHandler(),
features: [
// we only want to add new features
...SqlOpsDataClient.defaultFeatures,
TelemetryFeature
]
};
const installationStart = Date.now();
serverdownloader.getOrDownloadServer().then(e => {
const installationComplete = Date.now();
let serverOptions = generateServerOptions(e);
languageClient = new SqlOpsDataClient(Constants.serviceName, serverOptions, clientOptions);
const processStart = Date.now();
languageClient.onReady().then(() => {
const processEnd = Date.now();
statusView.text = 'Service Started';
setTimeout(() => {
statusView.hide();
}, 1500);
Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', {
installationTime: String(installationComplete - installationStart),
processStartupTime: String(processEnd - processStart),
totalTime: String(processEnd - installationStart),
beginningTimestamp: String(installationStart)
});
});
statusView.show();
statusView.text = 'Starting service';
languageClient.start();
credentialsStore.start();
resourceProvider.start();
}, e => {
Telemetry.sendTelemetryEvent('ServiceInitializingFailed');
vscode.window.showErrorMessage('Failed to start Sql tools service');
});
let contextProvider = new ContextProvider();
context.subscriptions.push(contextProvider);
context.subscriptions.push(credentialsStore);
context.subscriptions.push(resourceProvider);
context.subscriptions.push({ dispose: () => languageClient.stop() });
}
function generateServerOptions(executablePath: string): ServerOptions {
let launchArgs = [];
launchArgs.push('--log-dir');
let logFileLocation = path.join(Utils.getDefaultLogLocation(), 'mssql');
launchArgs.push(logFileLocation);
return { command: executablePath, args: launchArgs, transport: TransportKind.stdio };
}
function generateHandleServerProviderEvent() {
let dots = 0;
return (e: string, ...args: any[]) => {
outputChannel.show();
statusView.show();
switch (e) {
case Events.INSTALL_START:
outputChannel.appendLine(`Installing ${Constants.serviceName} service to ${args[0]}`);
statusView.text = 'Installing Service';
break;
case Events.INSTALL_END:
outputChannel.appendLine('Installed');
break;
case Events.DOWNLOAD_START:
outputChannel.appendLine(`Downloading ${args[0]}`);
outputChannel.append(`(${Math.ceil(args[1] / 1024)} KB)`);
statusView.text = 'Downloading Service';
break;
case Events.DOWNLOAD_PROGRESS:
let newDots = Math.ceil(args[0] / 5);
if (newDots > dots) {
outputChannel.append('.'.repeat(newDots - dots));
dots = newDots;
}
break;
case Events.DOWNLOAD_END:
outputChannel.appendLine('Done!');
break;
}
};
}
// this method is called when your extension is deactivated
export function deactivate(): void {
}

View File

@@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* 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 serviceName = 'AzureResourceProvider';
export const providerId = 'azureresourceProvider';

View File

@@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* 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 } from 'vscode-languageclient';
import * as sqlops from 'sqlops';
// ------------------------------- < Resource Events > ------------------------------------
export namespace CreateFirewallRuleRequest {
export const type = new RequestType<CreateFirewallRuleParams, CreateFirewallRuleResponse, void, void>('resource/createFirewallRule');
}
export namespace HandleFirewallRuleRequest {
export const type = new RequestType<HandleFirewallRuleParams, HandleFirewallRuleResponse, void, void>('resource/handleFirewallRule');
}
// Firewall rule interfaces
export interface CreateFirewallRuleParams {
account: sqlops.Account;
serverName: string;
startIpAddress: string;
endIpAddress: string;
securityTokenMappings: {};
}
export interface CreateFirewallRuleResponse {
result: boolean;
errorMessage: string;
}
export interface HandleFirewallRuleParams {
errorCode: number;
errorMessage: string;
connectionTypeId: string;
}
export interface HandleFirewallRuleResponse {
result: boolean;
ipAddress: string;
}

View File

@@ -0,0 +1,115 @@
/*---------------------------------------------------------------------------------------------
* 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 { IConfig, ServerProvider } from 'service-downloader';
import { SqlOpsDataClient, SqlOpsFeature, ClientOptions } from 'dataprotocol-client';
import { ServerCapabilities, ClientCapabilities, RPCMessageType, ServerOptions, TransportKind } from 'vscode-languageclient';
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
import * as sqlops from 'sqlops';
import { Disposable } from 'vscode';
import { CreateFirewallRuleRequest, HandleFirewallRuleRequest, CreateFirewallRuleParams, HandleFirewallRuleParams } from './contracts';
import * as Constants from './constants';
import * as Utils from '../utils';
class FireWallFeature extends SqlOpsFeature<any> {
private static readonly messagesTypes: RPCMessageType[] = [
CreateFirewallRuleRequest.type,
HandleFirewallRuleRequest.type
];
constructor(client: SqlOpsDataClient) {
super(client, FireWallFeature.messagesTypes);
}
fillClientCapabilities(capabilities: ClientCapabilities): void {
Utils.ensure(Utils.ensure(capabilities, 'firewall')!, 'firwall')!.dynamicRegistration = true;
}
initialize(capabilities: ServerCapabilities): void {
this.register(this.messages, {
id: UUID.generateUuid(),
registerOptions: undefined
});
}
protected registerProvider(options: any): Disposable {
const client = this._client;
let createFirewallRule = (account: sqlops.Account, firewallruleInfo: sqlops.FirewallRuleInfo): Thenable<sqlops.CreateFirewallRuleResponse> => {
return client.sendRequest(CreateFirewallRuleRequest.type, asCreateFirewallRuleParams(account, firewallruleInfo));
};
let handleFirewallRule = (errorCode: number, errorMessage: string, connectionTypeId: string): Thenable<sqlops.HandleFirewallRuleResponse> => {
let params: HandleFirewallRuleParams = { errorCode: errorCode, errorMessage: errorMessage, connectionTypeId: connectionTypeId };
return client.sendRequest(HandleFirewallRuleRequest.type, params);
};
return sqlops.resources.registerResourceProvider({
displayName: 'Azure SQL Resource Provider', // TODO Localize
id: 'Microsoft.Azure.SQL.ResourceProvider',
settings: {
}
}, {
handleFirewallRule,
createFirewallRule
});
}
}
function asCreateFirewallRuleParams(account: sqlops.Account, params: sqlops.FirewallRuleInfo): CreateFirewallRuleParams {
return {
account: account,
serverName: params.serverName,
startIpAddress: params.startIpAddress,
endIpAddress: params.endIpAddress,
securityTokenMappings: params.securityTokenMappings
};
}
export class AzureResourceProvider {
private _client: SqlOpsDataClient;
private _config: IConfig;
constructor(baseConfig: IConfig) {
if (baseConfig) {
this._config = JSON.parse(JSON.stringify(baseConfig));
this._config.executableFiles = ['SqlToolsResourceProviderService.exe', 'SqlToolsResourceProviderService'];
}
}
public start() {
let serverdownloader = new ServerProvider(this._config);
let clientOptions: ClientOptions = {
providerId: Constants.providerId,
features: [FireWallFeature]
};
serverdownloader.getOrDownloadServer().then(e => {
let serverOptions = this.generateServerOptions(e);
this._client = new SqlOpsDataClient(Constants.serviceName, serverOptions, clientOptions);
this._client.start();
});
}
public dispose() {
if (this._client) {
this._client.stop();
}
}
private generateServerOptions(executablePath: string): ServerOptions {
let launchArgs = [];
launchArgs.push('--log-dir');
let logFileLocation = path.join(Utils.getDefaultLogLocation(), 'mssql');
launchArgs.push(logFileLocation);
return { command: executablePath, args: launchArgs, transport: TransportKind.stdio };
}
}

View File

@@ -0,0 +1,211 @@
/*---------------------------------------------------------------------------------------------
* 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 opener from 'opener';
import TelemetryReporter from 'vscode-extension-telemetry';
import { PlatformInformation } from 'service-downloader/out/platform';
import { ErrorAction, ErrorHandler, Message, CloseAction } from 'vscode-languageclient';
import * as Utils from './utils';
import * as Constants from './constants';
const packageJson = require('../package.json');
export interface ITelemetryEventProperties {
[key: string]: string;
}
export interface ITelemetryEventMeasures {
[key: string]: number;
}
/**
* 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 = Utils.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 = Utils.getPackageInfo(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);
});
}
}
/**
* Handle Language Service client errors
* @class LanguageClientErrorHandler
*/
export class LanguageClientErrorHandler implements ErrorHandler {
/**
* Show an error message prompt with a link to known issues wiki page
* @memberOf LanguageClientErrorHandler
*/
showOnErrorPrompt(): void {
Telemetry.sendTelemetryEvent(Constants.serviceName + 'Crash');
vscode.window.showErrorMessage(
Constants.serviceCrashMessage,
Constants.serviceCrashButton).then(action => {
if (action && action === Constants.serviceCrashButton) {
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: Message, 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;
}
}
Telemetry.initialize();

View File

@@ -0,0 +1,7 @@
/*---------------------------------------------------------------------------------------------
* 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/sql/sqlops.d.ts'/>
/// <reference path='../../../../src/vs/vscode.d.ts'/>

View File

@@ -0,0 +1,103 @@
/*---------------------------------------------------------------------------------------------
* 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';
// 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() {
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() {
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);
}
}

File diff suppressed because it is too large Load Diff