Import Wizard (#2130)

Flat File Importer
This commit is contained in:
Amir Ali Omidi
2018-08-07 17:27:07 -07:00
committed by GitHub
parent d690b80493
commit 39bfd69dc9
28 changed files with 2670 additions and 0 deletions

1
extensions/import/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
flatfileimportservice/

View File

@@ -0,0 +1,2 @@
client/src/**
client/tsconfig.json

View File

@@ -0,0 +1,16 @@
# Microsoft SQL Server Import for SQL Operations Studio
--
## Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Privacy Statement
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement) describes the privacy statement of this software.
## License
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt).

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,105 @@
{
"name": "import",
"displayName": "SQL Server Import",
"description": "Imports data from a flat file.",
"version": "0.0.1",
"publisher": "Microsoft",
"preview": true,
"engines": {
"vscode": "^1.25.0",
"sqlops": "*"
},
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
"icon": "images/sqlserver.png",
"aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412",
"activationEvents": [
"*"
],
"main": "./out/main",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/sqlopsstudio.git"
},
"extensionDependencies": [
"Microsoft.mssql"
],
"contributes": {
"commands": [
{
"command": "flatFileImport.start",
"title": "Import wizard",
"category": "Flat File Import",
"icon": {
"light": "./images/light_icon.svg",
"dark": "./images/dark_icon.svg"
}
},
{
"command": "flatFileImport.importFlatFile"
},
{
"command": "flatFileImport.listDatabases"
}
],
"keybindings": [
{
"command": "flatFileImport.start",
"key": "ctrl+i",
"mac": "ctrl+i"
}
],
"dashboard.tabs": [
{
"id": "flat-file-import",
"title": "Flat File Import",
"description": "The flat file importer.",
"container": {
"flat-file-import-container": {}
}
}
],
"dashboard.containers": [
{
"id": "flat-file-import-container",
"container": {
"widgets-container": [
{
"name": "Tasks",
"widget": {
"tasks-widget": [
"flatFileImport.start"
]
}
}
]
}
}
],
"menus": {
"objectExplorer/item/context": [
{
"command": "flatFileImport.start",
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database",
"group": "import"
}
]
}
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.5",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.4",
"vscode-extension-telemetry": "^0.0.5",
"vscode-nls": "^3.2.1"
},
"devDependencies": {
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7"
},
"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,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
export const extensionConfigSectionName = 'flatFileImport';
export const serviceName = 'Flat File Import Service';
export const providerId = 'FlatFileImport';
export const configLogDebugInfo = 'logDebugInfo';
export const sqlConfigSectionName = 'sql';
export const serviceCrashLink = 'https://github.com/Microsoft/sqlopsstudio/issues/2090';

View File

@@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
export default abstract class ControllerBase implements vscode.Disposable {
protected _context: vscode.ExtensionContext;
public constructor(context: vscode.ExtensionContext) {
this._context = context;
}
public get extensionContext(): vscode.ExtensionContext {
return this._context;
}
abstract activate(): Promise<boolean>;
abstract deactivate(): void;
public dispose(): void {
this.deactivate();
}
}

View File

@@ -0,0 +1,41 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as constants from '../constants';
import * as sqlops from 'sqlops';
import ControllerBase from './controllerBase';
import * as vscode from 'vscode';
import { FlatFileWizard } from '../wizard/flatFileWizard';
import { ServiceClient } from '../services/serviceClient';
import { ApiType, managerInstance } from '../services/serviceApiManager';
import { FlatFileProvider } from '../services/contracts';
/**
* The main controller class that initializes the extension
*/
export default class MainController extends ControllerBase {
/**
*/
public deactivate(): void {
}
public activate(): Promise<boolean> {
const outputChannel = vscode.window.createOutputChannel(constants.serviceName);
new ServiceClient(outputChannel).startService(this._context);
managerInstance.onRegisteredApi<FlatFileProvider>(ApiType.FlatFileProvider)(provider => {
this.initializeFlatFileProvider(provider);
});
return Promise.resolve(true);
}
private initializeFlatFileProvider(provider: FlatFileProvider) {
sqlops.tasks.registerTask('flatFileImport.start', () => new FlatFileWizard(provider).start());
}
}

View File

@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import ControllerBase from './controllers/controllerBase';
import MainController from './controllers/mainController';
let controllers: ControllerBase[] = [];
export function activate(context: vscode.ExtensionContext) {
let activations: Promise<boolean>[] = [];
// Start the main controller
let mainController = new MainController(context);
controllers.push(mainController);
context.subscriptions.push(mainController);
activations.push(mainController.activate());
return Promise.all(activations)
.then((results: boolean[]) => {
for (let result of results) {
if (!result) {
return false;
}
}
return true;
});
}
export function deactivate() {
for (let controller of controllers) {
controller.deactivate();
}
}

View File

@@ -0,0 +1,16 @@
{
"downloadUrl": "https://sqlopsextensions.blob.core.windows.net/extensions/import/{#fileName#}",
"useDefaultLinuxRuntime": true,
"version": "0.0.1",
"downloadFileNames": {
"Windows_64": "win-x64.zip",
"Windows_86": "win-x86.zip",
"OSX": "osx.zip",
"Linux_64": "linux-x64.tar.gz"
},
"installDirectory": "flatfileimportservice/{#platform#}/{#version#}",
"executableFiles": [
"MicrosoftSqlToolsFlatFileImport",
"MicrosoftSqlToolsFlatFileImport.exe"
]
}

View File

@@ -0,0 +1,149 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { RequestType, NotificationType } from 'vscode-languageclient';
/**
* @interface IMessage
*/
export interface IMessage {
jsonrpc: string;
}
// ------------------------------- < Telemetry Sent Event > ------------------------------------
/**
* Event sent when the language service send a telemetry event
*/
export namespace TelemetryNotification {
export const type = new NotificationType<TelemetryParams, void>('telemetry/sqlevent');
}
/**
* Update event parameters
*/
export class TelemetryParams {
public params: {
eventName: string;
properties: ITelemetryEventProperties;
measures: ITelemetryEventMeasures;
};
}
export interface ITelemetryEventProperties {
[key: string]: string;
}
export interface ITelemetryEventMeasures {
[key: string]: number;
}
/**
* Contract Classes
*/
export interface Result {
success: boolean;
errorMessage: string;
}
export interface ColumnInfo {
name: string;
sqlType: string;
isNullable: boolean;
}
/**
* PROSEDiscoveryRequest
* Send this request to create a new PROSE session with a new file and preview it
*/
const proseDiscoveryRequestName = 'flatfile/proseDiscovery';
export interface PROSEDiscoveryParams {
filePath: string;
tableName: string;
schemaName?: string;
fileType?: string;
}
export interface PROSEDiscoveryResponse {
dataPreview: string[][];
columnInfo: ColumnInfo[];
}
/**
* InsertDataRequest
*/
const insertDataRequestName = 'flatfile/insertData';
export interface InsertDataParams {
connectionString: string;
batchSize: number;
}
export interface InsertDataResponse {
result: Result;
}
/**
* GetColumnInfoRequest
*/
const getColumnInfoRequestName = 'flatfile/getColumnInfo';
export interface GetColumnInfoParams {
}
export interface GetColumnInfoResponse {
columnInfo: ColumnInfo[];
}
/**
* ChangeColumnSettingsRequest
*/
const changeColumnSettingsRequestName = 'flatfile/changeColumnSettings';
export interface ChangeColumnSettingsParams {
index: number;
newName?: string;
newDataType?: string;
newNullable?: boolean;
newInPrimaryKey?: boolean;
}
export interface ChangeColumnSettingsResponse {
result: Result;
}
/**
* Requests
*/
export namespace PROSEDiscoveryRequest {
export const type = new RequestType<PROSEDiscoveryParams, PROSEDiscoveryResponse, void, void>(proseDiscoveryRequestName);
}
export namespace InsertDataRequest {
export const type = new RequestType<InsertDataParams, InsertDataResponse, void, void>(insertDataRequestName);
}
export namespace GetColumnInfoRequest {
export const type = new RequestType<GetColumnInfoParams, GetColumnInfoResponse, void, void>(getColumnInfoRequestName);
}
export namespace ChangeColumnSettingsRequest {
export const type = new RequestType<ChangeColumnSettingsParams, ChangeColumnSettingsResponse, void, void>(changeColumnSettingsRequestName);
}
export interface FlatFileProvider {
providerId?: string;
sendPROSEDiscoveryRequest(params: PROSEDiscoveryParams): Thenable<PROSEDiscoveryResponse>;
sendInsertDataRequest(params: InsertDataParams): Thenable<InsertDataResponse>;
sendGetColumnInfoRequest(params: GetColumnInfoParams): Thenable<GetColumnInfoResponse>;
sendChangeColumnSettingsRequest(params: ChangeColumnSettingsParams): Thenable<ChangeColumnSettingsResponse>;
}

View File

@@ -0,0 +1,96 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { SqlOpsDataClient, SqlOpsFeature } from 'dataprotocol-client';
import {
ClientCapabilities,
StaticFeature,
RPCMessageType,
ServerCapabilities
} from 'vscode-languageclient';
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
import { Disposable } from 'vscode';
import { Telemetry } from './telemetry';
import * as serviceUtils from './serviceUtils';
import * as Contracts from './contracts';
import { managerInstance, ApiType } from './serviceApiManager';
export class TelemetryFeature implements StaticFeature {
constructor(private _client: SqlOpsDataClient) {
}
fillClientCapabilities(capabilities: ClientCapabilities): void {
serviceUtils.ensure(capabilities, 'telemetry')!.telemetry = true;
}
initialize(): void {
this._client.onNotification(Contracts.TelemetryNotification.type, e => {
Telemetry.sendTelemetryEvent(e.params.eventName, e.params.properties, e.params.measures);
});
}
}
export class FlatFileImportFeature extends SqlOpsFeature<undefined> {
private static readonly messagesTypes: RPCMessageType[] = [
Contracts.PROSEDiscoveryRequest.type
];
constructor(client: SqlOpsDataClient) {
super(client, FlatFileImportFeature.messagesTypes);
}
public fillClientCapabilities(capabilities: ClientCapabilities): void {
}
public initialize(capabilities: ServerCapabilities): void {
this.register(this.messages, {
id: UUID.generateUuid(),
registerOptions: undefined
});
}
protected registerProvider(options: undefined): Disposable {
const client = this._client;
let requestSender = (requestType, params) => {
return client.sendRequest(requestType, params).then(
r => {
return r as any;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.reject(e);
}
);
};
let sendPROSEDiscoveryRequest = (params: Contracts.PROSEDiscoveryParams): Thenable<Contracts.PROSEDiscoveryResponse> => {
return requestSender(Contracts.PROSEDiscoveryRequest.type, params);
};
let sendInsertDataRequest = (params: Contracts.InsertDataParams): Thenable<Contracts.InsertDataResponse> => {
return requestSender(Contracts.InsertDataRequest.type, params);
};
let sendGetColumnInfoRequest = (params: Contracts.GetColumnInfoParams): Thenable<Contracts.GetColumnInfoResponse> => {
return requestSender(Contracts.GetColumnInfoRequest.type, params);
};
let sendChangeColumnSettingsRequest = (params: Contracts.ChangeColumnSettingsParams): Thenable<Contracts.ChangeColumnSettingsResponse> => {
return requestSender(Contracts.ChangeColumnSettingsRequest.type, params);
};
return managerInstance.registerApi<Contracts.FlatFileProvider>(ApiType.FlatFileProvider, {
providerId: client.providerId,
sendPROSEDiscoveryRequest,
sendChangeColumnSettingsRequest,
sendGetColumnInfoRequest,
sendInsertDataRequest
});
}
}

View File

@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import * as contracts from './contracts';
import { SqlOpsDataClient } from 'dataprotocol-client/lib/main';
export enum ApiType {
FlatFileProvider = 'FlatFileProvider'
}
export interface IServiceApi {
onRegisteredApi<T>(type: ApiType): vscode.Event<T>;
registerApi<T>(type: ApiType, feature: T): vscode.Disposable;
}
export interface IModelViewDefinition {
id: string;
modelView: sqlops.ModelView;
}
export class ServiceApiManager implements IServiceApi {
private modelViewRegistrations: { [id: string]: boolean } = {};
private featureEventChannels: { [type: string]: vscode.EventEmitter<any> } = {};
private _onRegisteredModelView = new vscode.EventEmitter<IModelViewDefinition>();
public onRegisteredApi<T>(type: ApiType): vscode.Event<T> {
let featureEmitter = this.featureEventChannels[type];
if (!featureEmitter) {
featureEmitter = new vscode.EventEmitter<T>();
this.featureEventChannels[type] = featureEmitter;
}
return featureEmitter.event;
}
public registerApi<T>(type: ApiType, feature: T): vscode.Disposable {
let featureEmitter = this.featureEventChannels[type];
if (featureEmitter) {
featureEmitter.fire(feature);
}
// TODO handle unregistering API on close
return {
dispose: () => undefined
};
}
public get onRegisteredModelView(): vscode.Event<IModelViewDefinition> {
return this._onRegisteredModelView.event;
}
public registerModelView(id: string, modelView: sqlops.ModelView): void {
this._onRegisteredModelView.fire({
id: id,
modelView: modelView
});
}
}
export let managerInstance = new ServiceApiManager();

View File

@@ -0,0 +1,165 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client';
import { IConfig, ServerProvider, Events } from 'service-downloader';
import { ServerOptions, TransportKind } from 'vscode-languageclient';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import * as path from 'path';
import { EventAndListener } from 'eventemitter2';
import { Telemetry, LanguageClientErrorHandler } from './telemetry';
import * as Constants from '../constants';
import { TelemetryFeature, FlatFileImportFeature } from './features';
import * as serviceUtils from './serviceUtils';
const baseConfig = require('./config.json');
export class ServiceClient {
private statusView: vscode.StatusBarItem;
constructor(private outputChannel: vscode.OutputChannel) {
this.statusView = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
}
public startService(context: vscode.ExtensionContext): Promise<SqlOpsDataClient> {
let config: IConfig = JSON.parse(JSON.stringify(baseConfig));
config.installDirectory = path.join(context.extensionPath, config.installDirectory);
config.proxy = vscode.workspace.getConfiguration('http').get('proxy');
config.strictSSL = vscode.workspace.getConfiguration('http').get('proxyStrictSSL') || true;
const serverdownloader = new ServerProvider(config);
serverdownloader.eventEmitter.onAny(this.generateHandleServerProviderEvent());
let clientOptions: ClientOptions = this.createClientOptions();
const installationStart = Date.now();
let client: SqlOpsDataClient;
return new Promise((resolve, reject) => {
serverdownloader.getOrDownloadServer().then(e => {
const installationComplete = Date.now();
let serverOptions = this.generateServerOptions(e);
client = new SqlOpsDataClient(Constants.serviceName, serverOptions, clientOptions);
const processStart = Date.now();
client.onReady().then(() => {
const processEnd = Date.now();
this.statusView.text = localize('serviceStarted', 'Service Started');
setTimeout(() => {
this.statusView.hide();
}, 1500);
Telemetry.sendTelemetryEvent('startup/LanguageClientStarted', {
installationTime: String(installationComplete - installationStart),
processStartupTime: String(processEnd - processStart),
totalTime: String(processEnd - installationStart),
beginningTimestamp: String(installationStart)
});
});
this.statusView.show();
this.statusView.text = localize('serviceStarting', 'Starting service');
let disposable = client.start();
context.subscriptions.push(disposable);
resolve(client);
}, e => {
Telemetry.sendTelemetryEvent('ServiceInitializingFailed');
vscode.window.showErrorMessage(localize('serviceStartFailed', 'Failed to start Scale Out Data service:{0}', e));
// Just resolve to avoid unhandled promise. We show the error to the user.
resolve(undefined);
});
});
}
private createClientOptions(): ClientOptions {
return {
providerId: Constants.providerId,
errorHandler: new LanguageClientErrorHandler(),
synchronize: {
configurationSection: [Constants.extensionConfigSectionName, Constants.sqlConfigSectionName]
},
features: [
// we only want to add new features
TelemetryFeature,
FlatFileImportFeature
],
outputChannel: new CustomOutputChannel()
};
}
private generateServerOptions(executablePath: string): ServerOptions {
let launchArgs = [];
launchArgs.push('--log-dir');
let logFileLocation = path.join(serviceUtils.getDefaultLogLocation(), 'flatfileimport');
launchArgs.push(logFileLocation);
let config = vscode.workspace.getConfiguration(Constants.extensionConfigSectionName);
if (config) {
let logDebugInfo = config[Constants.configLogDebugInfo];
if (logDebugInfo) {
launchArgs.push('--enable-logging');
}
}
return { command: executablePath, args: launchArgs, transport: TransportKind.stdio };
}
private generateHandleServerProviderEvent(): EventAndListener {
let dots = 0;
return (e: string, ...args: any[]) => {
this.outputChannel.show();
this.statusView.show();
switch (e) {
case Events.INSTALL_START:
this.outputChannel.appendLine(localize('installingServiceDetailed', 'Installing {0} service to {1}', Constants.serviceName, args[0]));
this.statusView.text = localize('installingService', 'Installing Service');
break;
case Events.INSTALL_END:
this.outputChannel.appendLine(localize('serviceInstalled', 'Installed'));
break;
case Events.DOWNLOAD_START:
this.outputChannel.appendLine(localize('downloadingService', 'Downloading {0}', args[0]));
this.outputChannel.append(`(${Math.ceil(args[1] / 1024)} KB)`);
this.statusView.text = localize('downloadingServiceStatus', 'Downloading Service');
break;
case Events.DOWNLOAD_PROGRESS:
let newDots = Math.ceil(args[0] / 5);
if (newDots > dots) {
this.outputChannel.append('.'.repeat(newDots - dots));
dots = newDots;
}
break;
case Events.DOWNLOAD_END:
this.outputChannel.appendLine(localize('downloadingServiceComplete', 'Done!'));
break;
default:
break;
}
};
}
}
class CustomOutputChannel implements vscode.OutputChannel {
name: string;
append(value: string): void {
}
appendLine(value: string): void {
}
// tslint:disable-next-line:no-empty
clear(): void {
}
show(preserveFocus?: boolean): void;
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void;
// tslint:disable-next-line:no-empty
show(column?: any, preserveFocus?: any): void {
}
// tslint:disable-next-line:no-empty
hide(): void {
}
// tslint:disable-next-line:no-empty
dispose(): void {
}
}

View File

@@ -0,0 +1,156 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as path from 'path';
import * as crypto from 'crypto';
import * as os from 'os';
const baseConfig = require('./config.json');
// The function is a duplicate of \src\paths.js. IT would be better to import path.js but it doesn't
// work for now because the extension is running in different process.
export function getAppDataPath(): string {
let platform = process.platform;
switch (platform) {
case 'win32': return process.env['APPDATA'] || path.join(process.env['USERPROFILE'], 'AppData', 'Roaming');
case 'darwin': return path.join(os.homedir(), 'Library', 'Application Support');
case 'linux': return process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
default: throw new Error('Platform not supported');
}
}
export function getDefaultLogLocation(): string {
return path.join(getAppDataPath(), 'sqlops');
}
export function ensure(target: object, key: string): any {
if (target[key] === void 0) {
target[key] = {} as any;
}
return target[key];
}
export interface IPackageInfo {
name: string;
version: string;
aiKey: string;
}
export function getPackageInfo(packageJson: any): IPackageInfo {
if (packageJson) {
return {
name: packageJson.name,
version: packageJson.version,
aiKey: packageJson.aiKey
};
}
}
export function generateUserId(): Promise<string> {
return new Promise<string>(resolve => {
try {
let interfaces = os.networkInterfaces();
let mac;
for (let key of Object.keys(interfaces)) {
let item = interfaces[key][0];
if (!item.internal) {
mac = item.mac;
break;
}
}
if (mac) {
resolve(crypto.createHash('sha256').update(mac + os.homedir(), 'utf8').digest('hex'));
} else {
resolve(generateGuid());
}
} catch (err) {
resolve(generateGuid()); // fallback
}
});
}
export function generateGuid(): string {
let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
let oct: string = '';
let tmp: number;
/* tslint:disable:no-bitwise */
for (let a: number = 0; a < 4; a++) {
tmp = (4294967296 * Math.random()) | 0;
oct += hexValues[tmp & 0xF] +
hexValues[tmp >> 4 & 0xF] +
hexValues[tmp >> 8 & 0xF] +
hexValues[tmp >> 12 & 0xF] +
hexValues[tmp >> 16 & 0xF] +
hexValues[tmp >> 20 & 0xF] +
hexValues[tmp >> 24 & 0xF] +
hexValues[tmp >> 28 & 0xF];
}
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12);
/* tslint:enable:no-bitwise */
}
export function verifyPlatform(): Thenable<boolean> {
if (os.platform() === 'darwin' && parseFloat(os.release()) < 16.0) {
return Promise.resolve(false);
} else {
return Promise.resolve(true);
}
}
export function getServiceInstallConfig(basePath?: string): any {
if (!basePath) {
basePath = __dirname;
}
let config = JSON.parse(JSON.stringify(baseConfig));
config.installDirectory = path.join(basePath, config.installDirectory);
return config;
}
export function getResolvedServiceInstallationPath(runtime: Runtime, basePath?: string): string {
let config = getServiceInstallConfig(basePath);
let dir = config.installDirectory;
dir = dir.replace('{#version#}', config.version);
dir = dir.replace('{#platform#}', getRuntimeDisplayName(runtime));
return dir;
}
export function getRuntimeDisplayName(runtime: Runtime): string {
switch (runtime) {
case Runtime.Windows_64:
return 'Windows';
case Runtime.Windows_86:
return 'Windows';
case Runtime.OSX:
return 'OSX';
case Runtime.Linux_64:
return 'Linux';
default:
return 'Unknown';
}
}
export enum Runtime {
Unknown = <any>'Unknown',
Windows_86 = <any>'Windows_86',
Windows_64 = <any>'Windows_64',
OSX = <any>'OSX',
CentOS_7 = <any>'CentOS_7',
Debian_8 = <any>'Debian_8',
Fedora_23 = <any>'Fedora_23',
OpenSUSE_13_2 = <any>'OpenSUSE_13_2',
SLES_12_2 = <any>'SLES_12_2',
RHEL_7 = <any>'RHEL_7',
Ubuntu_14 = <any>'Ubuntu_14',
Ubuntu_16 = <any>'Ubuntu_16',
Linux_64 = <any>'Linux_64',
Linux_86 = <any>'Linux-86'
}

View File

@@ -0,0 +1,216 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ErrorAction, CloseAction } from 'vscode-languageclient';
import TelemetryReporter from 'vscode-extension-telemetry';
import { PlatformInformation } from 'service-downloader/out/platform';
import * as opener from 'opener';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import * as constants from '../constants';
import * as serviceUtils from './serviceUtils';
import { IMessage, ITelemetryEventProperties, ITelemetryEventMeasures } from './contracts';
/**
* Handle Language Service client errors
* @class LanguageClientErrorHandler
*/
export class LanguageClientErrorHandler {
/**
* Creates an instance of LanguageClientErrorHandler.
* @memberOf LanguageClientErrorHandler
*/
constructor() {
}
/**
* Show an error message prompt with a link to known issues wiki page
* @memberOf LanguageClientErrorHandler
*/
showOnErrorPrompt(): void {
// TODO add telemetry
// Telemetry.sendTelemetryEvent('SqlToolsServiceCrash');
let crashButtonText = localize('import.serviceCrashButton', 'Give Feedback');
vscode.window.showErrorMessage(
localize('serviceCrashMessage', 'service component could not start'),
crashButtonText
).then(action => {
if (action && action === crashButtonText) {
opener(constants.serviceCrashLink);
}
});
}
/**
* Callback for language service client error
*
* @param {Error} error
* @param {Message} message
* @param {number} count
* @returns {ErrorAction}
*
* @memberOf LanguageClientErrorHandler
*/
error(error: Error, message: IMessage, count: number): ErrorAction {
this.showOnErrorPrompt();
// we don't retry running the service since crashes leave the extension
// in a bad, unrecovered state
return ErrorAction.Shutdown;
}
/**
* Callback for language service client closed
*
* @returns {CloseAction}
*
* @memberOf LanguageClientErrorHandler
*/
closed(): CloseAction {
this.showOnErrorPrompt();
// we don't retry running the service since crashes leave the extension
// in a bad, unrecovered state
return CloseAction.DoNotRestart;
}
}
/**
* Filters error paths to only include source files. Exported to support testing
*/
export function FilterErrorPath(line: string): string {
if (line) {
let values: string[] = line.split('/out/');
if (values.length <= 1) {
// Didn't match expected format
return line;
} else {
return values[1];
}
}
}
export class Telemetry {
private static reporter: TelemetryReporter;
private static userId: string;
private static platformInformation: PlatformInformation;
private static disabled: boolean;
// Get the unique ID for the current user of the extension
public static getUserId(): Promise<string> {
return new Promise<string>(resolve => {
// Generate the user id if it has not been created already
if (typeof this.userId === 'undefined') {
let id = serviceUtils.generateUserId();
id.then(newId => {
this.userId = newId;
resolve(this.userId);
});
} else {
resolve(this.userId);
}
});
}
public static getPlatformInformation(): Promise<PlatformInformation> {
if (this.platformInformation) {
return Promise.resolve(this.platformInformation);
} else {
return new Promise<PlatformInformation>(resolve => {
PlatformInformation.getCurrent().then(info => {
this.platformInformation = info;
resolve(this.platformInformation);
});
});
}
}
/**
* Disable telemetry reporting
*/
public static disable(): void {
this.disabled = true;
}
/**
* Initialize the telemetry reporter for use.
*/
public static initialize(): void {
if (typeof this.reporter === 'undefined') {
// Check if the user has opted out of telemetry
if (!vscode.workspace.getConfiguration('telemetry').get<boolean>('enableTelemetry', true)) {
this.disable();
return;
}
let packageInfo = vscode.extensions.getExtension('Microsoft.import').packageJSON;
this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
}
}
/**
* Send a telemetry event for an exception
*/
public static sendTelemetryEventForException(
err: any, methodName: string, extensionConfigName: string): void {
try {
let stackArray: string[];
let firstLine: string = '';
if (err !== undefined && err.stack !== undefined) {
stackArray = err.stack.split('\n');
if (stackArray !== undefined && stackArray.length >= 2) {
firstLine = stackArray[1]; // The fist line is the error message and we don't want to send that telemetry event
firstLine = FilterErrorPath(firstLine);
}
}
// Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII
this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine });
// Utils.logDebug('Unhandled Exception occurred. error: ' + err + ' method: ' + methodName, extensionConfigName);
} catch (telemetryErr) {
// If sending telemetry event fails ignore it so it won't break the extension
// Utils.logDebug('Failed to send telemetry event. error: ' + telemetryErr, extensionConfigName);
}
}
/**
* Send a telemetry event using application insights
*/
public static sendTelemetryEvent(
eventName: string,
properties?: ITelemetryEventProperties,
measures?: ITelemetryEventMeasures): void {
if (typeof this.disabled === 'undefined') {
this.disabled = false;
}
if (this.disabled || typeof (this.reporter) === 'undefined') {
// Don't do anything if telemetry is disabled
return;
}
if (!properties || typeof properties === 'undefined') {
properties = {};
}
// Augment the properties structure with additional common properties before sending
Promise.all([this.getUserId(), this.getPlatformInformation()]).then(() => {
properties['userId'] = this.userId;
properties['distribution'] = (this.platformInformation && this.platformInformation.distribution) ?
`${this.platformInformation.distribution.name}, ${this.platformInformation.distribution.version}` : '';
this.reporter.sendTelemetryEvent(eventName, properties, measures);
});
}
}
Telemetry.initialize();

View File

@@ -0,0 +1,9 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../src/sql/sqlops.d.ts'/>
/// <reference path='../../../../src/sql/sqlops.proposed.d.ts'/>
/// <reference types='@types/node'/>

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ImportDataModel } from './models';
import * as sqlops from 'sqlops';
import { FlatFileProvider } from '../../services/contracts';
import { FlatFileWizard } from '../flatFileWizard';
export abstract class ImportPage {
protected readonly instance: FlatFileWizard;
protected readonly model: ImportDataModel;
protected readonly view: sqlops.ModelView;
protected readonly provider: FlatFileProvider;
protected constructor(instance: FlatFileWizard, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
this.instance = instance;
this.model = model;
this.view = view;
this.provider = provider;
}
/**
* This method constructs all the elements of the page.
* @returns {Promise<boolean>}
*/
public async abstract start(): Promise<boolean>;
/**
* This method is called when the user is entering the page.
* @returns {Promise<boolean>}
*/
public async abstract onPageEnter(): Promise<boolean>;
/**
* This method is called when the user is leaving the page.
* @returns {Promise<boolean>}
*/
public async abstract onPageLeave(): Promise<boolean>;
/**
* Override this method to cleanup what you don't need cached in the page.
* @returns {Promise<boolean>}
*/
public async cleanup(): Promise<boolean> {
return true;
}
}

View File

@@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
/**
* The main data model that communicates between the pages.
*/
export interface ImportDataModel {
ownerUri: string;
proseColumns: ColumnMetadata[];
proseDataPreview: string[][];
server: sqlops.connection.Connection;
database: string;
table: string;
schema: string;
filePath: string;
fileType: string;
}
/**
* Metadata of a column
*/
export interface ColumnMetadata {
columnName: string;
dataType: string;
primaryKey: boolean;
nullable: boolean;
}

View File

@@ -0,0 +1,130 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import { FlatFileProvider } from '../services/contracts';
import { ImportDataModel } from './api/models';
import { ImportPage } from './api/importPage';
// pages
import { FileConfigPage } from './pages/fileConfigPage';
import { ProsePreviewPage } from './pages/prosePreviewPage';
import { ModifyColumnsPage } from './pages/modifyColumnsPage';
import { SummaryPage } from './pages/summaryPage';
const localize = nls.loadMessageBundle();
export class FlatFileWizard {
private readonly provider: FlatFileProvider;
private wizard: sqlops.window.modelviewdialog.Wizard;
private importAnotherFileButton: sqlops.window.modelviewdialog.Button;
constructor(provider: FlatFileProvider) {
this.provider = provider;
}
public async start() {
let model = <ImportDataModel>{};
let pages: Map<number, ImportPage> = new Map<number, ImportPage>();
// TODO localize this
let connections = await sqlops.connection.getActiveConnections();
if (!connections || connections.length === 0) {
vscode.window.showErrorMessage('Please connect to a server before using this wizard.');
return;
}
this.wizard = sqlops.window.modelviewdialog.createWizard(localize('flatFileImport.wizardName', 'Import flat file wizard'));
let page1 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page1Name', 'New Table Details'));
let page2 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page2Name', 'Preview Data'));
let page3 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page3Name', 'Modify Columns'));
let page4 = sqlops.window.modelviewdialog.createWizardPage(localize('flatFileImport.page4Name', 'Summary'));
let fileConfigPage: FileConfigPage;
page1.registerContent(async (view) => {
fileConfigPage = new FileConfigPage(this, model, view, this.provider);
pages.set(0, fileConfigPage);
await fileConfigPage.start();
fileConfigPage.onPageEnter();
});
let prosePreviewPage: ProsePreviewPage;
page2.registerContent(async (view) => {
prosePreviewPage = new ProsePreviewPage(this, model, view, this.provider);
pages.set(1, prosePreviewPage);
await prosePreviewPage.start();
});
let modifyColumnsPage: ModifyColumnsPage;
page3.registerContent(async (view) => {
modifyColumnsPage = new ModifyColumnsPage(this, model, view, this.provider);
pages.set(2, modifyColumnsPage);
await modifyColumnsPage.start();
});
let summaryPage: SummaryPage;
page4.registerContent(async (view) => {
summaryPage = new SummaryPage(this, model, view, this.provider);
pages.set(3, summaryPage);
await summaryPage.start();
});
this.importAnotherFileButton = sqlops.window.modelviewdialog.createButton(localize('flatFileImport.importNewFile', 'Import new file'));
this.importAnotherFileButton.onClick(() => {
//TODO replace this with proper cleanup for all the pages
this.wizard.close();
pages.forEach((page) => page.cleanup());
this.wizard.open();
});
this.importAnotherFileButton.hidden = true;
this.wizard.customButtons = [this.importAnotherFileButton];
this.wizard.onPageChanged(async (event) => {
let idx = event.newPage;
let page = pages.get(idx);
if (page) {
page.onPageEnter();
}
});
this.wizard.onPageChanged(async (event) => {
let idx = event.lastPage;
let page = pages.get(idx);
if (page) {
page.onPageLeave();
}
});
//not needed for this wizard
this.wizard.generateScriptButton.hidden = true;
this.wizard.pages = [page1, page2, page3, page4];
this.wizard.open();
}
public setImportAnotherFileVisibility(visibility: boolean) {
this.importAnotherFileButton.hidden = !visibility;
}
public registerNavigationValidator(validator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean) {
this.wizard.registerNavigationValidator(validator);
}
}

View File

@@ -0,0 +1,393 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { ImportDataModel } from '../api/models';
import { ImportPage } from '../api/importPage';
import { FlatFileProvider } from '../../services/contracts';
import { FlatFileWizard } from '../flatFileWizard';
const localize = nls.loadMessageBundle();
export class FileConfigPage extends ImportPage {
private serverDropdown: sqlops.DropDownComponent;
private databaseDropdown: sqlops.DropDownComponent;
private fileTextBox: sqlops.InputBoxComponent;
private fileButton: sqlops.ButtonComponent;
private tableNameTextBox: sqlops.InputBoxComponent;
private schemaDropdown: sqlops.DropDownComponent;
private form: sqlops.FormContainer;
private databaseLoader: sqlops.LoadingComponent;
private schemaLoader: sqlops.LoadingComponent;
private tableNames: string[] = [];
public constructor(instance: FlatFileWizard, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
super(instance, model, view, provider);
}
async start(): Promise<boolean> {
let schemaComponent = await this.createSchemaDropdown();
let tableNameComponent = await this.createTableNameBox();
let fileBrowserComponent = await this.createFileBrowser();
let databaseComponent = await this.createDatabaseDropdown();
let serverComponent = await this.createServerDropdown();
this.setupNavigationValidator();
this.form = this.view.modelBuilder.formContainer()
.withFormItems(
[
serverComponent,
databaseComponent,
fileBrowserComponent,
tableNameComponent,
schemaComponent
]).component();
await this.view.initializeModel(this.form);
return true;
}
async onPageEnter(): Promise<boolean> {
await this.populateServerDropdown();
await this.populateDatabaseDropdown();
await this.populateSchemaDropdown();
return true;
}
async onPageLeave(): Promise<boolean> {
return true;
}
public async cleanup(): Promise<boolean> {
delete this.model.filePath;
delete this.model.table;
return true;
}
private setupNavigationValidator() {
this.instance.registerNavigationValidator((info) => {
if (this.schemaLoader.loading || this.databaseLoader.loading) {
return false;
}
return true;
});
}
private async createServerDropdown(): Promise<sqlops.FormComponent> {
this.serverDropdown = this.view.modelBuilder.dropDown().component();
// Handle server changes
this.serverDropdown.onValueChanged(async (params) => {
this.model.server = (this.serverDropdown.value as ConnectionDropdownValue).connection;
await this.populateDatabaseDropdown();
await this.populateSchemaDropdown();
});
return {
component: this.serverDropdown,
title: localize('flatFileImport.serverDropdownTitle', 'Server the database is in')
};
}
private async populateServerDropdown(): Promise<boolean> {
let cons = await sqlops.connection.getActiveConnections();
// This user has no active connections ABORT MISSION
if (!cons || cons.length === 0) {
return true;
}
let count = -1;
let idx = -1;
let values = cons.map(c => {
// Handle the code to remember what the user's choice was from before
count++;
if (this.model.server && c.connectionId === this.model.server.connectionId) {
idx = count;
}
let db = c.options.databaseDisplayName;
let usr = c.options.user;
let srv = c.options.server;
if (!db) {
db = '<default>';
}
if (!usr) {
usr = 'default';
}
let finalName = `${srv}, ${db} (${usr})`;
return {
connection: c,
displayName: finalName,
name: c.connectionId
};
});
if (idx > 0) {
let tmp = values[0];
values[0] = values[idx];
values[idx] = tmp;
} else {
delete this.model.server;
delete this.model.database;
delete this.model.schema;
}
this.model.server = values[0].connection;
this.serverDropdown.updateProperties({
values: values
});
return true;
}
private async createDatabaseDropdown(): Promise<sqlops.FormComponent> {
this.databaseDropdown = this.view.modelBuilder.dropDown().component();
// Handle database changes
this.databaseDropdown.onValueChanged(async (db) => {
this.model.database = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
//this.populateTableNames();
this.populateSchemaDropdown();
});
this.databaseLoader = this.view.modelBuilder.loadingComponent().withItem(this.databaseDropdown).component();
return {
component: this.databaseLoader,
title: localize('flatFileImport.databaseDropdownTitle', 'Database the table is created in')
};
}
private async populateDatabaseDropdown(): Promise<boolean> {
this.databaseLoader.loading = true;
this.databaseDropdown.updateProperties({ values: [] });
this.schemaDropdown.updateProperties({ values: [] });
if (!this.model.server) {
//TODO handle error case
this.databaseLoader.loading = false;
return false;
}
let idx = -1;
let count = -1;
let values = (await sqlops.connection.listDatabases(this.model.server.connectionId)).map(db => {
count++;
if (this.model.database && db === this.model.database) {
idx = count;
}
return {
displayName: db,
name: db
};
});
if (idx > 0) {
let tmp = values[0];
values[0] = values[idx];
values[idx] = tmp;
} else {
delete this.model.database;
delete this.model.schema;
}
this.model.database = values[0].name;
this.databaseDropdown.updateProperties({
values: values
});
this.databaseLoader.loading = false;
return true;
}
private async createFileBrowser(): Promise<sqlops.FormComponent> {
this.fileTextBox = this.view.modelBuilder.inputBox().component();
this.fileButton = this.view.modelBuilder.button().withProperties({
label: localize('flatFileImport.browseFiles', 'Browse'),
}).component();
this.fileButton.onDidClick(async (click) => {
let fileUris = await vscode.window.showOpenDialog(
{
canSelectFiles: true,
canSelectFolders: false,
canSelectMany: false,
openLabel: localize('flatFileImport.openFile', 'Open'),
filters: {
'Files': ['csv', 'txt']
}
}
);
if (!fileUris || fileUris.length === 0) {
return;
}
let fileUri = fileUris[0];
this.fileTextBox.value = fileUri.fsPath;
// Get the name of the file.
let nameStart = fileUri.path.lastIndexOf('/');
let nameEnd = fileUri.path.lastIndexOf('.');
// Handle files without extensions
if (nameEnd === 0) {
nameEnd = fileUri.path.length;
}
this.model.fileType = 'TXT';
let extension = fileUri.path.substring(nameEnd + 1, fileUri.path.length);
if (extension.toLowerCase() === 'json') {
this.model.fileType = 'JSON';
}
this.tableNameTextBox.value = fileUri.path.substring(nameStart + 1, nameEnd);
this.model.table = this.tableNameTextBox.value;
this.tableNameTextBox.validate();
// Let then model know about the file path
this.model.filePath = fileUri.fsPath;
});
return {
component: this.fileTextBox,
title: localize('flatFileImport.fileTextboxTitle', 'Location of the file to be imported'),
actions: [this.fileButton]
};
}
private async createTableNameBox(): Promise<sqlops.FormComponent> {
this.tableNameTextBox = this.view.modelBuilder.inputBox().withValidation((name) => {
let tableName = name.value;
if (!tableName || tableName.length === 0) {
return false;
}
// This won't actually do anything until table names are brought back in.
if (this.tableNames.indexOf(tableName) !== -1) {
return false;
}
return true;
}).component();
this.tableNameTextBox.onTextChanged((tableName) => {
this.model.table = tableName;
});
return {
component: this.tableNameTextBox,
title: localize('flatFileImport.tableTextboxTitle', 'New table name'),
};
}
private async createSchemaDropdown(): Promise<sqlops.FormComponent> {
this.schemaDropdown = this.view.modelBuilder.dropDown().component();
this.schemaLoader = this.view.modelBuilder.loadingComponent().withItem(this.schemaDropdown).component();
this.schemaDropdown.onValueChanged(() => {
this.model.schema = (<sqlops.CategoryValue>this.schemaDropdown.value).name;
});
return {
component: this.schemaLoader,
title: localize('flatFileImport.schemaTextboxTitle', 'Table schema'),
};
}
private async populateSchemaDropdown(): Promise<Boolean> {
this.schemaLoader.loading = true;
let connectionUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
let queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(this.model.server.providerName, sqlops.DataProviderType.QueryProvider);
let query = `SELECT name FROM sys.schemas`;
let results = await queryProvider.runQueryAndReturn(connectionUri, query);
let idx = -1;
let count = -1;
let values = results.rows.map(row => {
let schemaName = row[0].displayValue;
count++;
if (this.model.schema && schemaName === this.model.schema) {
idx = count;
}
let val = row[0].displayValue;
return {
name: val,
displayName: val
};
});
if (idx > 0) {
let tmp = values[0];
values[0] = values[idx];
values[idx] = tmp;
}
this.model.schema = values[0].name;
this.schemaDropdown.updateProperties({
values: values
});
this.schemaLoader.loading = false;
return true;
}
// private async populateTableNames(): Promise<boolean> {
// this.tableNames = [];
// let databaseName = (<sqlops.CategoryValue>this.databaseDropdown.value).name;
//
// if (!databaseName || databaseName.length === 0) {
// this.tableNames = [];
// return false;
// }
//
// let connectionUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
// let queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(this.model.server.providerName, sqlops.DataProviderType.QueryProvider);
// let results: sqlops.SimpleExecuteResult;
//
// try {
// //let query = sqlstring.format('USE ?; SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = \'BASE TABLE\'', [databaseName]);
// //results = await queryProvider.runQueryAndReturn(connectionUri, query);
// } catch (e) {
// return false;
// }
//
// this.tableNames = results.rows.map(row => {
// return row[0].displayValue;
// });
//
// return true;
// }
}
interface ConnectionDropdownValue extends sqlops.CategoryValue {
connection: sqlops.connection.Connection;
}

View File

@@ -0,0 +1,161 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as nls from 'vscode-nls';
import { ColumnMetadata, ImportDataModel } from '../api/models';
import { ImportPage } from '../api/importPage';
import { FlatFileProvider } from '../../services/contracts';
import { FlatFileWizard } from '../flatFileWizard';
const localize = nls.loadMessageBundle();
export class ModifyColumnsPage extends ImportPage {
private readonly categoryValues = [
{ name: 'bigint', displayName: 'bigint' },
{ name: 'binary(50)', displayName: 'binary(50)' },
{ name: 'bit', displayName: 'bit' },
{ name: 'char(10)', displayName: 'char(10)' },
{ name: 'date', displayName: 'date' },
{ name: 'datetime', displayName: 'datetime' },
{ name: 'datetime2(7)', displayName: 'datetime2(7)' },
{ name: 'datetimeoffset(7)', displayName: 'datetimeoffset(7)' },
{ name: 'decimal(18, 10)', displayName: 'decimal(18, 10)' },
{ name: 'float', displayName: 'float' },
{ name: 'geography', displayName: 'geography' },
{ name: 'geometry', displayName: 'geometry' },
{ name: 'hierarchyid', displayName: 'hierarchyid' },
{ name: 'int', displayName: 'int' },
{ name: 'money', displayName: 'money' },
{ name: 'nchar(10)', displayName: 'nchar(10)' },
{ name: 'ntext', displayName: 'ntext' },
{ name: 'numeric(18, 0)', displayName: 'numeric(18, 0)' },
{ name: 'nvarchar(50)', displayName: 'nvarchar(50)' },
{ name: 'nvarchar(MAX)', displayName: 'nvarchar(MAX)' },
{ name: 'real', displayName: 'real' },
{ name: 'smalldatetime', displayName: 'smalldatetime' },
{ name: 'smallint', displayName: 'smallint' },
{ name: 'smallmoney', displayName: 'smallmoney' },
{ name: 'sql_variant', displayName: 'sql_variant' },
{ name: 'text', displayName: 'text' },
{ name: 'time(7)', displayName: 'time(7)' },
{ name: 'timestamp', displayName: 'timestamp' },
{ name: 'tinyint', displayName: 'tinyint' },
{ name: 'uniqueidentifier', displayName: 'uniqueidentifier' },
{ name: 'varbinary(50)', displayName: 'varbinary(50)' },
{ name: 'varbinary(MAX)', displayName: 'varbinary(MAX)' },
{ name: 'varchar(50)', displayName: 'varchar(50)' },
{ name: 'varchar(MAX)', displayName: 'varchar(MAX)' }
];
private table: sqlops.DeclarativeTableComponent;
private loading: sqlops.LoadingComponent;
private text: sqlops.TextComponent;
private form: sqlops.FormContainer;
public constructor(instance: FlatFileWizard, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
super(instance, model, view, provider);
}
private static convertMetadata(column: ColumnMetadata): any[] {
return [column.columnName, column.dataType, false, column.nullable];
}
async start(): Promise<boolean> {
this.loading = this.view.modelBuilder.loadingComponent().component();
this.table = this.view.modelBuilder.declarativeTable().component();
this.text = this.view.modelBuilder.text().component();
this.table.onDataChanged((e) => {
this.model.proseColumns = [];
this.table.data.forEach((row) => {
this.model.proseColumns.push({
columnName: row[0],
dataType: row[1],
primaryKey: row[2],
nullable: row[3]
});
});
});
this.form = this.view.modelBuilder.formContainer()
.withFormItems(
[
{
component: this.text,
title: ''
},
{
component: this.table,
title: ''
}
], {
horizontal: false,
componentWidth: '100%'
}).component();
this.loading.component = this.form;
await this.view.initializeModel(this.form);
return true;
}
async onPageEnter(): Promise<boolean> {
this.loading.loading = true;
await this.populateTable();
this.loading.loading = false;
return true;
}
async onPageLeave(): Promise<boolean> {
return undefined;
}
async cleanup(): Promise<boolean> {
delete this.model.proseColumns;
return true;
}
private async populateTable() {
let data: any[][] = [];
this.model.proseColumns.forEach((column) => {
data.push(ModifyColumnsPage.convertMetadata(column));
});
this.table.updateProperties({
height: 400,
columns: [{
displayName: localize('flatFileImport.columnName', 'Column Name'),
valueType: sqlops.DeclarativeDataType.string,
width: '150px',
isReadOnly: false
}, {
displayName: localize('flatFileImport.dataType', 'Data type'),
valueType: sqlops.DeclarativeDataType.editableCategory,
width: '150px',
isReadOnly: false,
categoryValues: this.categoryValues
}, {
displayName: localize('flatFileImport.primaryKey', 'Primary key'),
valueType: sqlops.DeclarativeDataType.boolean,
width: '100px',
isReadOnly: false
}, {
displayName: localize('flatFileImport.allowNull', 'Allow null'),
valueType: sqlops.DeclarativeDataType.boolean,
isReadOnly: false,
width: '100px'
}],
data: data
});
}
}

View File

@@ -0,0 +1,128 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as nls from 'vscode-nls';
import { ImportDataModel } from '../api/models';
import { ImportPage } from '../api/importPage';
import { FlatFileProvider } from '../../services/contracts';
import { FlatFileWizard } from '../flatFileWizard';
const localize = nls.loadMessageBundle();
export class ProsePreviewPage extends ImportPage {
private table: sqlops.TableComponent;
private loading: sqlops.LoadingComponent;
private form: sqlops.FormContainer;
private refresh: sqlops.ButtonComponent;
public constructor(instance: FlatFileWizard, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
super(instance, model, view, provider);
}
async start(): Promise<boolean> {
this.table = this.view.modelBuilder.table().component();
this.refresh = this.view.modelBuilder.button().withProperties({
label: localize('flatFileImport.refresh', 'Refresh'),
isFile: false
}).component();
this.refresh.onDidClick(async () => {
this.onPageEnter();
});
this.loading = this.view.modelBuilder.loadingComponent().component();
this.setupNavigationValidator();
this.form = this.view.modelBuilder.formContainer().withFormItems([
{
component: this.table,
title: localize('flatFileImport.prosePreviewMessage', 'This operation analyzed the input file structure to generate the preview below'),
actions: [this.refresh]
}
]).component();
this.loading.component = this.form;
await this.view.initializeModel(this.loading);
return true;
}
async onPageEnter(): Promise<boolean> {
this.loading.loading = true;
await this.handleProse();
await this.populateTable(this.model.proseDataPreview, this.model.proseColumns.map(c => c.columnName));
this.loading.loading = false;
return true;
}
async onPageLeave(): Promise<boolean> {
await this.emptyTable();
return true;
}
async cleanup(): Promise<boolean> {
delete this.model.proseDataPreview;
return true;
}
private setupNavigationValidator() {
this.instance.registerNavigationValidator((info) => {
if (this.loading.loading) {
return false;
}
return true;
});
}
private async handleProse() {
await this.provider.sendPROSEDiscoveryRequest({
filePath: this.model.filePath,
tableName: this.model.table,
schemaName: this.model.schema,
fileType: this.model.fileType
}).then((result) => {
this.model.proseDataPreview = result.dataPreview;
this.model.proseColumns = [];
result.columnInfo.forEach((column) => {
this.model.proseColumns.push({
columnName: column.name,
dataType: column.sqlType,
primaryKey: false,
nullable: column.isNullable
});
});
});
}
private async populateTable(tableData: string[][], columnHeaders: string[]) {
let rows;
let rowsLength = tableData.length;
if (rowsLength > 50) {
rows = tableData;
}
else {
rows = tableData.slice(0, rowsLength);
}
this.table.updateProperties({
data: rows,
columns: columnHeaders,
height: 400,
width: '700',
});
}
private async emptyTable() {
this.table.updateProperties([]);
}
}

View File

@@ -0,0 +1,168 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import * as nls from 'vscode-nls';
import { ImportDataModel } from '../api/models';
import { ImportPage } from '../api/importPage';
import { FlatFileProvider, InsertDataResponse } from '../../services/contracts';
import { FlatFileWizard } from '../flatFileWizard';
const localize = nls.loadMessageBundle();
export class SummaryPage extends ImportPage {
private table: sqlops.TableComponent;
private statusText: sqlops.TextComponent;
private loading: sqlops.LoadingComponent;
private form: sqlops.FormContainer;
public constructor(instance: FlatFileWizard, model: ImportDataModel, view: sqlops.ModelView, provider: FlatFileProvider) {
super(instance, model, view, provider);
}
async start(): Promise<boolean> {
this.table = this.view.modelBuilder.table().component();
this.statusText = this.view.modelBuilder.text().component();
this.loading = this.view.modelBuilder.loadingComponent().withItem(this.statusText).component();
this.form = this.view.modelBuilder.formContainer().withFormItems(
[
{
component: this.table,
title: localize('flatFileImport.importInformation', 'Import information')
},
{
component: this.loading,
title: localize('flatFileImport.importStatus', 'Import status')
}
]
).component();
await this.view.initializeModel(this.form);
return true;
}
async onPageEnter(): Promise<boolean> {
this.loading.loading = true;
this.populateTable();
await this.handleImport();
this.loading.loading = false;
this.instance.setImportAnotherFileVisibility(true);
return true;
}
async onPageLeave(): Promise<boolean> {
this.instance.setImportAnotherFileVisibility(false);
return true;
}
private populateTable() {
this.table.updateProperties({
data: [
['Server name', this.model.server.providerName],
['Database name', this.model.database],
['Table name', this.model.table],
['Table schema', this.model.schema],
['File to be imported', this.model.filePath]],
columns: ['Object type', 'Name'],
width: 600,
height: 200
});
}
private async handleImport(): Promise<boolean> {
let changeColumnResults = [];
this.model.proseColumns.forEach((val, i, arr) => {
let columnChangeParams = {
index: i,
newName: val.columnName,
newDataType: val.dataType,
newNullable: val.nullable,
newInPrimaryKey: val.primaryKey
};
changeColumnResults.push(this.provider.sendChangeColumnSettingsRequest(columnChangeParams));
});
let result: InsertDataResponse;
let err;
try {
result = await this.provider.sendInsertDataRequest({
connectionString: await this.getConnectionString(),
//TODO check what SSMS uses as batch size
batchSize: 500
});
} catch (e) {
err = e.toString();
}
let updateText: string;
if (!result || !result.result.success) {
updateText = '✗ ';
if (!result) {
updateText += err;
} else {
updateText += result.result.errorMessage;
}
} else {
// TODO: When sql statements are in, implement this.
//let rows = await this.getCountRowsInserted();
//if (rows < 0) {
updateText = localize('flatFileImport.success.norows', '✔ Awesome! You have successfully inserted the data into a table.');
//} else {
//updateText = localize('flatFileImport.success.rows', '✔ Awesome! You have successfully inserted {0} rows.', rows);
//}
}
this.statusText.updateProperties({
value: updateText
});
return true;
}
/**
* Gets the connection string to send to the middleware
* @returns {Promise<string>}
*/
private async getConnectionString(): Promise<string> {
let options = this.model.server.options;
let connectionString: string;
if (options.authenticationType === 'Integrated') {
connectionString = `Data Source=${options.server + (options.port ? `,${options.port}` : '')};Initial Catalog=${this.model.database};Integrated Security=True`;
} else {
let credentials = await sqlops.connection.getCredentials(this.model.server.connectionId);
connectionString = `Data Source=${options.server + (options.port ? `,${options.port}` : '')};Initial Catalog=${this.model.database};Integrated Security=False;User Id=${options.user};Password=${credentials.password}`;
}
// TODO: Fix this, it's returning undefined string.
//await sqlops.connection.getConnectionString(this.model.server.connectionId, true);
return connectionString;
}
// private async getCountRowsInserted(): Promise<Number> {
// let connectionUri = await sqlops.connection.getUriForConnection(this.model.server.connectionId);
// let queryProvider = sqlops.dataprotocol.getProvider<sqlops.QueryProvider>(this.model.server.providerName, sqlops.DataProviderType.QueryProvider);
// try {
// let query = sqlstring.format('USE ?; SELECT COUNT(*) FROM ?', [this.model.database, this.model.table]);
// let results = await queryProvider.runQueryAndReturn(connectionUri, query);
// let cell = results.rows[0][0];
// if (!cell || cell.isNull) {
// return -1;
// }
// let numericCell = Number(cell.displayValue);
// if (isNaN(numericCell)) {
// return -1;
// }
// return numericCell;
// } catch (e) {
// return -1;
// }
// }
}

View File

@@ -0,0 +1,19 @@
{
"compileOnSave": true,
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "./out",
"lib": [
"es6", "es2015.promise"
],
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"declaration": true
},
"exclude": [
"node_modules"
]
}

470
extensions/import/yarn.lock Normal file
View File

@@ -0,0 +1,470 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
agent-base@4, agent-base@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
dependencies:
es6-promisify "^5.0.0"
ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
applicationinsights@0.15.6:
version "0.15.6"
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-0.15.6.tgz#201a0682c0704fe4bdd9a92d0b2cbe34d2ae5972"
base64-js@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978"
bl@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
dependencies:
readable-stream "^2.3.5"
safe-buffer "^5.1.1"
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
buffer-alloc@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
dependencies:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
buffer@^3.0.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb"
dependencies:
base64-js "0.0.8"
ieee754 "^1.1.4"
isarray "^1.0.0"
charenc@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
commander@~2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
dependencies:
graceful-readlink ">= 1.0.0"
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
crypt@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.1.5":
version "0.1.5"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/21b0bacfc759689a6c280408528c6029a21b1abf"
dependencies:
vscode-languageclient "3.5.0"
debug@3.1.0, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
debug@^2.2.0:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
ms "2.0.0"
decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
dependencies:
file-type "^5.2.0"
is-stream "^1.1.0"
tar-stream "^1.5.2"
decompress-tarbz2@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
dependencies:
decompress-tar "^4.1.0"
file-type "^6.1.0"
is-stream "^1.1.0"
seek-bzip "^1.0.5"
unbzip2-stream "^1.0.9"
decompress-targz@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
dependencies:
decompress-tar "^4.1.1"
file-type "^5.2.0"
is-stream "^1.1.0"
decompress-unzip@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
dependencies:
file-type "^3.8.0"
get-stream "^2.2.0"
pify "^2.3.0"
yauzl "^2.4.2"
decompress@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d"
dependencies:
decompress-tar "^4.0.0"
decompress-tarbz2 "^4.0.0"
decompress-targz "^4.0.0"
decompress-unzip "^4.0.1"
graceful-fs "^4.1.10"
make-dir "^1.0.0"
pify "^2.3.0"
strip-dirs "^2.0.0"
end-of-stream@^1.0.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
dependencies:
once "^1.4.0"
es6-promise@^4.0.3:
version "4.2.4"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
dependencies:
es6-promise "^4.0.3"
eventemitter2@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452"
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
dependencies:
pend "~1.2.0"
file-type@^3.8.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
file-type@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
file-type@^6.1.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
get-stream@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
dependencies:
object-assign "^4.0.1"
pinkie-promise "^2.0.0"
graceful-fs@^4.1.10:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
"graceful-readlink@>= 1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
http-proxy-agent@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
dependencies:
agent-base "4"
debug "3.1.0"
https-proxy-agent@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
dependencies:
agent-base "^4.1.0"
debug "^3.1.0"
ieee754@^1.1.4:
version "1.1.12"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"
inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
is-buffer@~1.1.1:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
is-natural-number@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
lodash@^4.16.4:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
make-dir@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
dependencies:
pify "^3.0.0"
md5@^2.1.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
dependencies:
charenc "~0.0.1"
crypt "~0.0.1"
is-buffer "~1.1.1"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
mkdirp@^0.5.1, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
minimist "0.0.8"
mocha-junit-reporter@^1.17.0:
version "1.17.0"
resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.17.0.tgz#2e5149ed40fc5d2e3ca71e42db5ab1fec9c6d85c"
dependencies:
debug "^2.2.0"
md5 "^2.1.0"
mkdirp "~0.5.1"
strip-ansi "^4.0.0"
xml "^1.0.0"
mocha-multi-reporters@^1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82"
dependencies:
debug "^3.1.0"
lodash "^4.16.4"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
dependencies:
wrappy "1"
opener@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
pinkie-promise@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
dependencies:
pinkie "^2.0.0"
pinkie@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
readable-stream@^2.3.0, readable-stream@^2.3.5:
version "2.3.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
seek-bzip@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
dependencies:
commander "~2.8.1"
"service-downloader@github:anthonydresser/service-downloader#0.1.4":
version "0.1.4"
resolved "https://codeload.github.com/anthonydresser/service-downloader/tar.gz/3c0abdf8603aca85d2eacfac3c547173e41bf0c7"
dependencies:
decompress "^4.2.0"
eventemitter2 "^5.0.1"
http-proxy-agent "^2.0.0"
https-proxy-agent "^2.1.1"
mkdirp "^0.5.1"
tmp "^0.0.33"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
dependencies:
ansi-regex "^3.0.0"
strip-dirs@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
dependencies:
is-natural-number "^4.0.1"
tar-stream@^1.5.2:
version "1.6.1"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395"
dependencies:
bl "^1.0.0"
buffer-alloc "^1.1.0"
end-of-stream "^1.0.0"
fs-constants "^1.0.0"
readable-stream "^2.3.0"
to-buffer "^1.1.0"
xtend "^4.0.0"
through@^2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
dependencies:
os-tmpdir "~1.0.2"
to-buffer@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
unbzip2-stream@^1.0.9:
version "1.2.5"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz#73a033a567bbbde59654b193c44d48a7e4f43c47"
dependencies:
buffer "^3.0.1"
through "^2.3.6"
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
vscode-extension-telemetry@^0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.5.tgz#21e2abb4cbce3326e469ddbb322123b3702f3f85"
dependencies:
applicationinsights "0.15.6"
winreg "0.0.13"
vscode-jsonrpc@3.5.0, vscode-jsonrpc@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
vscode-languageclient@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-3.5.0.tgz#36d02cc186a8365a4467719a290fb200a9ae490a"
dependencies:
vscode-languageserver-protocol "^3.5.0"
vscode-languageserver-protocol@3.5.0, vscode-languageserver-protocol@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0.tgz#067c5cbe27709795398d119692c97ebba1452209"
dependencies:
vscode-jsonrpc "^3.5.0"
vscode-languageserver-types "^3.5.0"
vscode-languageserver-types@3.5.0, vscode-languageserver-types@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374"
vscode-nls@^3.2.1:
version "3.2.2"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350"
winreg@0.0.13:
version "0.0.13"
resolved "https://registry.yarnpkg.com/winreg/-/winreg-0.0.13.tgz#76bfe02e1dd0c9c8275fb9fdf17a9f36846e3483"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
xml@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
xtend@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
yauzl@^2.4.2:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"