mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
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:
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
4
extensions/mssql/npm-shrinkwrap.json
generated
4
extensions/mssql/npm-shrinkwrap.json
generated
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "mssql",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
19
extensions/mssql/src/config.json
Normal file
19
extensions/mssql/src/config.json
Normal 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"]
|
||||
}
|
||||
11
extensions/mssql/src/constants.ts
Normal file
11
extensions/mssql/src/constants.ts
Normal 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';
|
||||
80
extensions/mssql/src/contracts.ts
Normal file
80
extensions/mssql/src/contracts.ts
Normal 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');
|
||||
}
|
||||
@@ -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';
|
||||
34
extensions/mssql/src/credentialstore/contracts.ts
Normal file
34
extensions/mssql/src/credentialstore/contracts.ts
Normal 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 > -------------------------------------------------
|
||||
110
extensions/mssql/src/credentialstore/credentialstore.ts
Normal file
110
extensions/mssql/src/credentialstore/credentialstore.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { 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 };
|
||||
}
|
||||
}
|
||||
99
extensions/mssql/src/features.ts
Normal file
99
extensions/mssql/src/features.ts
Normal 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
|
||||
});
|
||||
}
|
||||
}
|
||||
136
extensions/mssql/src/main.ts
Normal file
136
extensions/mssql/src/main.ts
Normal 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 {
|
||||
}
|
||||
8
extensions/mssql/src/resourceprovider/constants.ts
Normal file
8
extensions/mssql/src/resourceprovider/constants.ts
Normal 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';
|
||||
42
extensions/mssql/src/resourceprovider/contracts.ts
Normal file
42
extensions/mssql/src/resourceprovider/contracts.ts
Normal 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;
|
||||
}
|
||||
115
extensions/mssql/src/resourceprovider/resourceprovider.ts
Normal file
115
extensions/mssql/src/resourceprovider/resourceprovider.ts
Normal 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 };
|
||||
}
|
||||
}
|
||||
211
extensions/mssql/src/telemetry.ts
Normal file
211
extensions/mssql/src/telemetry.ts
Normal 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();
|
||||
7
extensions/mssql/src/typings/refs.d.ts
vendored
Normal file
7
extensions/mssql/src/typings/refs.d.ts
vendored
Normal 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'/>
|
||||
103
extensions/mssql/src/utils.ts
Normal file
103
extensions/mssql/src/utils.ts
Normal 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
Reference in New Issue
Block a user