Initial Code Layering (#3788)

* working on formatting

* fixed basic lint errors; starting moving things to their appropriate location

* formatting

* update tslint to match the version of vscode we have

* remove unused code

* work in progress fixing layering

* formatting

* moved connection management service to platform

* formatting

* add missing file

* moving more servies

* formatting

* moving more services

* formatting

* wip

* moving more services

* formatting

* revert back tslint rules

* move css file

* add missing svgs
This commit is contained in:
Anthony Dresser
2019-01-25 14:52:35 -08:00
committed by GitHub
parent c8986464ec
commit ea67859de7
338 changed files with 2036 additions and 7386 deletions

View File

@@ -1,443 +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 sqlops from 'sqlops';
import * as platform from 'vs/platform/registry/common/platform';
import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Event, Emitter } from 'vs/base/common/event';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Memento, Scope as MementoScope } from 'vs/workbench/common/memento';
import AccountStore from 'sql/services/accountManagement/accountStore';
import { AccountDialogController } from 'sql/parts/accountManagement/accountDialog/accountDialogController';
import { AutoOAuthDialogController } from 'sql/parts/accountManagement/autoOAuthDialog/autoOAuthDialogController';
import { AccountListStatusbarItem } from 'sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem';
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes';
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
import { Deferred } from 'sql/base/common/promise';
export class AccountManagementService implements IAccountManagementService {
// CONSTANTS ///////////////////////////////////////////////////////////
private static ACCOUNT_MEMENTO = 'AccountManagement';
// MEMBER VARIABLES ////////////////////////////////////////////////////
public _providers: { [id: string]: AccountProviderWithMetadata } = {};
public _serviceBrand: any;
private _accountStore: AccountStore;
private _accountDialogController: AccountDialogController;
private _autoOAuthDialogController: AutoOAuthDialogController;
private _mementoContext: Memento;
// EVENT EMITTERS //////////////////////////////////////////////////////
private _addAccountProviderEmitter: Emitter<AccountProviderAddedEventParams>;
public get addAccountProviderEvent(): Event<AccountProviderAddedEventParams> { return this._addAccountProviderEmitter.event; }
private _removeAccountProviderEmitter: Emitter<sqlops.AccountProviderMetadata>;
public get removeAccountProviderEvent(): Event<sqlops.AccountProviderMetadata> { return this._removeAccountProviderEmitter.event; }
private _updateAccountListEmitter: Emitter<UpdateAccountListEventParams>;
public get updateAccountListEvent(): Event<UpdateAccountListEventParams> { return this._updateAccountListEmitter.event; }
// CONSTRUCTOR /////////////////////////////////////////////////////////
constructor(
private _mementoObj: object,
@IInstantiationService private _instantiationService: IInstantiationService,
@IStorageService private _storageService: IStorageService,
@IClipboardService private _clipboardService: IClipboardService,
) {
// Create the account store
if (!this._mementoObj) {
this._mementoContext = new Memento(AccountManagementService.ACCOUNT_MEMENTO);
this._mementoObj = this._mementoContext.getMemento(this._storageService, MementoScope.GLOBAL);
}
this._accountStore = this._instantiationService.createInstance(AccountStore, this._mementoObj);
// Setup the event emitters
this._addAccountProviderEmitter = new Emitter<AccountProviderAddedEventParams>();
this._removeAccountProviderEmitter = new Emitter<sqlops.AccountProviderMetadata>();
this._updateAccountListEmitter = new Emitter<UpdateAccountListEventParams>();
// Register status bar item
let statusbarDescriptor = new statusbar.StatusbarItemDescriptor(
AccountListStatusbarItem,
statusbar.StatusbarAlignment.LEFT,
15000 /* Highest Priority */
);
(<statusbar.IStatusbarRegistry>platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(statusbarDescriptor);
}
private get autoOAuthDialogController(): AutoOAuthDialogController {
// If the add account dialog hasn't been defined, create a new one
if (!this._autoOAuthDialogController) {
this._autoOAuthDialogController = this._instantiationService.createInstance(AutoOAuthDialogController);
}
return this._autoOAuthDialogController;
}
// PUBLIC METHODS //////////////////////////////////////////////////////
/**
* Called from an account provider (via extension host -> main thread interop) when an
* account's properties have been updated (usually when the account goes stale).
* @param {Account} updatedAccount Account with the updated properties
*/
public accountUpdated(updatedAccount: sqlops.Account): Thenable<void> {
let self = this;
// 1) Update the account in the store
// 2a) If the account was added, then the account provider incorrectly called this method.
// Remove the account
// 2b) If the account was modified, then update it in the local cache and notify any
// listeners that the account provider's list changed
// 3) Handle any errors
return this.doWithProvider(updatedAccount.key.providerId, provider => {
return self._accountStore.addOrUpdate(updatedAccount)
.then(result => {
if (result.accountAdded) {
self._accountStore.remove(updatedAccount.key);
return Promise.reject('Called with a new account!');
}
if (result.accountModified) {
self.spliceModifiedAccount(provider, result.changedAccount);
self.fireAccountListUpdate(provider, false);
}
return Promise.resolve();
});
}).then(
() => { },
reason => {
console.warn(`Account update handler encountered error: ${reason}`);
}
);
}
/**
* Asks the requested provider to prompt for an account
* @param {string} providerId ID of the provider to ask to prompt for an account
* @return {Thenable<Account>} Promise to return an account
*/
public addAccount(providerId: string): Thenable<void> {
let self = this;
return this.doWithProvider(providerId, (provider) => {
return provider.provider.prompt()
.then(account => self._accountStore.addOrUpdate(account))
.then(result => {
if (result.accountAdded) {
// Add the account to the list
provider.accounts.push(result.changedAccount);
}
if (result.accountModified) {
self.spliceModifiedAccount(provider, result.changedAccount);
}
self.fireAccountListUpdate(provider, result.accountAdded);
})
.then(null, err => {
// On error, check to see if the error is because the user cancelled. If so, just ignore
if (err && 'userCancelledSignIn' in err) {
return Promise.resolve();
}
return Promise.reject(err);
});
});
}
/**
* Asks the requested provider to refresh an account
* @param {Account} account account to refresh
* @return {Thenable<Account>} Promise to return an account
*/
public refreshAccount(account: sqlops.Account): Thenable<sqlops.Account> {
let self = this;
return this.doWithProvider(account.key.providerId, (provider) => {
return provider.provider.refresh(account)
.then(account => self._accountStore.addOrUpdate(account))
.then(result => {
if (result.accountAdded) {
// Add the account to the list
provider.accounts.push(result.changedAccount);
}
if (result.accountModified) {
// Find the updated account and splice the updated on in
let indexToRemove: number = provider.accounts.findIndex(account => {
return account.key.accountId === result.changedAccount.key.accountId;
});
if (indexToRemove >= 0) {
provider.accounts.splice(indexToRemove, 1, result.changedAccount);
}
}
self.fireAccountListUpdate(provider, result.accountAdded);
return result.changedAccount;
});
});
}
/**
* Retrieves metadata of all providers that have been registered
* @returns {Thenable<AccountProviderMetadata[]>} Registered account providers
*/
public getAccountProviderMetadata(): Thenable<sqlops.AccountProviderMetadata[]> {
return Promise.resolve(Object.values(this._providers).map(provider => provider.metadata));
}
/**
* Retrieves the accounts that belong to a specific provider
* @param {string} providerId ID of the provider the returned accounts belong to
* @returns {Thenable<Account[]>} Promise to return a list of accounts
*/
public getAccountsForProvider(providerId: string): Thenable<sqlops.Account[]> {
let self = this;
// 1) Get the accounts from the store
// 2) Update our local cache of accounts
return this.doWithProvider(providerId, provider => {
return self._accountStore.getAccountsByProvider(provider.metadata.id)
.then(accounts => {
self._providers[providerId].accounts = accounts;
return accounts;
});
});
}
/**
* Generates a security token by asking the account's provider
* @param {Account} account Account to generate security token for
* @param {sqlops.AzureResource} resource The resource to get the security token for
* @return {Thenable<{}>} Promise to return the security token
*/
public getSecurityToken(account: sqlops.Account, resource: sqlops.AzureResource): Thenable<{}> {
return this.doWithProvider(account.key.providerId, provider => {
return provider.provider.getSecurityToken(account, resource);
});
}
/**
* Removes an account from the account store and clears sensitive data in the provider
* @param {AccountKey} accountKey Key for the account to remove
* @returns {Thenable<void>} Promise with result of account removal, true if account was
* removed, false otherwise.
*/
public removeAccount(accountKey: sqlops.AccountKey): Thenable<boolean> {
let self = this;
// Step 1) Remove the account
// Step 2) Clear the sensitive data from the provider (regardless of whether the account was removed)
// Step 3) Update the account cache and fire an event
return this.doWithProvider(accountKey.providerId, provider => {
return this._accountStore.remove(accountKey)
.then(result => {
provider.provider.clear(accountKey);
return result;
})
.then(result => {
if (!result) {
return result;
}
let indexToRemove: number = provider.accounts.findIndex(account => {
return account.key.accountId === accountKey.accountId;
});
if (indexToRemove >= 0) {
provider.accounts.splice(indexToRemove, 1);
self.fireAccountListUpdate(provider, false);
}
return result;
});
});
}
// UI METHODS //////////////////////////////////////////////////////////
/**
* Opens the account list dialog
* @return {TPromise<any>} Promise that finishes when the account list dialog opens
*/
public openAccountListDialog(): Thenable<void> {
let self = this;
return new Promise((resolve, reject) => {
try {
// If the account list dialog hasn't been defined, create a new one
if (!self._accountDialogController) {
self._accountDialogController = self._instantiationService.createInstance(AccountDialogController);
}
self._accountDialogController.openAccountDialog();
resolve();
} catch (e) {
reject(e);
}
});
}
/**
* Begin auto OAuth device code open add account dialog
* @return {TPromise<any>} Promise that finishes when the account list dialog opens
*/
public beginAutoOAuthDeviceCode(providerId: string, title: string, message: string, userCode: string, uri: string): Thenable<void> {
let self = this;
return this.doWithProvider(providerId, provider => {
return self.autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
});
}
/**
* End auto OAuth Devide code closes add account dialog
*/
public endAutoOAuthDeviceCode(): void {
this.autoOAuthDialogController.closeAutoOAuthDialog();
}
/**
* Called from the UI when a user cancels the auto OAuth dialog
*/
public cancelAutoOAuthDeviceCode(providerId: string): void {
this.doWithProvider(providerId, provider => provider.provider.autoOAuthCancelled())
.then( // Swallow errors
null,
err => { console.warn(`Error when cancelling auto OAuth: ${err}`); }
)
.then(() => this.autoOAuthDialogController.closeAutoOAuthDialog());
}
/**
* Copy the user code to the clipboard and open a browser to the verification URI
*/
public copyUserCodeAndOpenBrowser(userCode: string, uri: string): void {
this._clipboardService.writeText(userCode);
window.open(uri);
}
// SERVICE MANAGEMENT METHODS //////////////////////////////////////////
/**
* Called by main thread to register an account provider from extension
* @param {sqlops.AccountProviderMetadata} providerMetadata Metadata of the provider that is being registered
* @param {sqlops.AccountProvider} provider References to the methods of the provider
*/
public registerProvider(providerMetadata: sqlops.AccountProviderMetadata, provider: sqlops.AccountProvider): Thenable<void> {
let self = this;
// Store the account provider
this._providers[providerMetadata.id] = {
metadata: providerMetadata,
provider: provider,
accounts: []
};
// Initialize the provider:
// 1) Get all the accounts that were stored
// 2) Give those accounts to the provider for rehydration
// 3) Add the accounts to our local store of accounts
// 4) Write the accounts back to the store
// 5) Fire the event to let folks know we have another account provider now
return this._accountStore.getAccountsByProvider(providerMetadata.id)
.then((accounts: sqlops.Account[]) => {
return provider.initialize(accounts);
})
.then((accounts: sqlops.Account[]) => {
self._providers[providerMetadata.id].accounts = accounts;
let writePromises = accounts.map(account => {
return self._accountStore.addOrUpdate(account);
});
return Promise.all(writePromises);
})
.then(() => {
let provider = self._providers[providerMetadata.id];
self._addAccountProviderEmitter.fire({
addedProvider: provider.metadata,
initialAccounts: provider.accounts.slice(0) // Slice here to make sure no one can modify our cache
});
});
// TODO: Add stale event handling to the providers
}
/**
* Handler for when shutdown of the application occurs. Writes out the memento.
*/
public shutdown(): void {
if (this._mementoContext) {
this._mementoContext.saveMemento();
}
}
public unregisterProvider(providerMetadata: sqlops.AccountProviderMetadata): void {
// Delete this account provider
delete this._providers[providerMetadata.id];
// Alert our listeners that we've removed a provider
this._removeAccountProviderEmitter.fire(providerMetadata);
}
// TODO: Support for orphaned accounts (accounts with no provider)
// PRIVATE HELPERS /////////////////////////////////////////////////////
private doWithProvider<T>(providerId: string, op: (provider: AccountProviderWithMetadata) => Thenable<T>): Thenable<T> {
let provider = this._providers[providerId];
if (!provider) {
// If the provider doesn't already exist wait until it gets registered
let deferredPromise = new Deferred<T>();
let toDispose = this.addAccountProviderEvent(params => {
if (params.addedProvider.id === providerId) {
toDispose.dispose();
deferredPromise.resolve(op(this._providers[providerId]));
}
});
return deferredPromise;
}
return op(provider);
}
private fireAccountListUpdate(provider: AccountProviderWithMetadata, sort: boolean) {
// Step 1) Get and sort the list
if (sort) {
provider.accounts.sort((a: sqlops.Account, b: sqlops.Account) => {
if (a.displayInfo.displayName < b.displayInfo.displayName) {
return -1;
}
if (a.displayInfo.displayName > b.displayInfo.displayName) {
return 1;
}
return 0;
});
}
// Step 2) Fire the event
let eventArg: UpdateAccountListEventParams = {
providerId: provider.metadata.id,
accountList: provider.accounts
};
this._updateAccountListEmitter.fire(eventArg);
}
private spliceModifiedAccount(provider: AccountProviderWithMetadata, modifiedAccount: sqlops.Account) {
// Find the updated account and splice the updated one in
let indexToRemove: number = provider.accounts.findIndex(account => {
return account.key.accountId === modifiedAccount.key.accountId;
});
if (indexToRemove >= 0) {
provider.accounts.splice(indexToRemove, 1, modifiedAccount);
}
}
}
/**
* Joins together an account provider, its metadata, and its accounts, used in the provider list
*/
export interface AccountProviderWithMetadata {
metadata: sqlops.AccountProviderMetadata;
provider: sqlops.AccountProvider;
accounts: sqlops.Account[];
}

View File

@@ -1,204 +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 sqlops from 'sqlops';
import { AccountAdditionResult } from 'sql/services/accountManagement/eventTypes';
import { IAccountStore } from 'sql/services/accountManagement/interfaces';
export default class AccountStore implements IAccountStore {
// CONSTANTS ///////////////////////////////////////////////////////////
public static MEMENTO_KEY: string = 'Microsoft.SqlTools.Accounts';
// MEMBER VARIABLES ////////////////////////////////////////////////////
private _activeOperation: Thenable<any>;
constructor(private _memento: object) { }
// PUBLIC METHODS //////////////////////////////////////////////////////
public addOrUpdate(newAccount: sqlops.Account): Thenable<AccountAdditionResult> {
let self = this;
return this.doOperation(() => {
return self.readFromMemento()
.then(accounts => {
// Determine if account exists and proceed accordingly
let match = accounts.findIndex(account => AccountStore.findAccountByKey(account.key, newAccount.key));
return match < 0
? self.addToAccountList(accounts, newAccount)
: self.updateAccountList(accounts, newAccount.key, (matchAccount) => { AccountStore.mergeAccounts(newAccount, matchAccount); });
})
.then(result => self.writeToMemento(result.updatedAccounts).then(() => result))
.then(result => <AccountAdditionResult>result);
});
}
public getAccountsByProvider(providerId: string): Thenable<sqlops.Account[]> {
let self = this;
return this.doOperation(() => {
return self.readFromMemento()
.then(accounts => accounts.filter(account => account.key.providerId === providerId));
});
}
public getAllAccounts(): Thenable<sqlops.Account[]> {
let self = this;
return this.doOperation(() => {
return self.readFromMemento();
});
}
public remove(key: sqlops.AccountKey): Thenable<boolean> {
let self = this;
return this.doOperation(() => {
return self.readFromMemento()
.then(accounts => self.removeFromAccountList(accounts, key))
.then(result => self.writeToMemento(result.updatedAccounts).then(() => result))
.then(result => result.accountRemoved);
});
}
public update(key: sqlops.AccountKey, updateOperation: (account: sqlops.Account) => void): Thenable<boolean> {
let self = this;
return this.doOperation(() => {
return self.readFromMemento()
.then(accounts => self.updateAccountList(accounts, key, updateOperation))
.then(result => self.writeToMemento(result.updatedAccounts).then(() => result))
.then(result => result.accountModified);
});
}
// PRIVATE METHODS /////////////////////////////////////////////////////
private static findAccountByKey(key1: sqlops.AccountKey, key2: sqlops.AccountKey): boolean {
// Provider ID and Account ID must match
return key1.providerId === key2.providerId && key1.accountId === key2.accountId;
}
private static mergeAccounts(source: sqlops.Account, target: sqlops.Account): void {
// Take any display info changes
target.displayInfo = source.displayInfo;
// Take all property changes
target.properties = source.properties;
// Take any stale changes
target.isStale = source.isStale;
}
private doOperation<T>(op: () => Thenable<T>) {
// Initialize the active operation to an empty promise if necessary
let activeOperation = this._activeOperation || Promise.resolve<any>(null);
// Chain the operation to perform to the end of the existing promise
activeOperation = activeOperation.then(op);
// Add a catch at the end to make sure we can continue after any errors
activeOperation = activeOperation.then(null, (err) => {
// TODO: Log the error
});
// Point the current active operation to this one
this._activeOperation = activeOperation;
return <Promise<T>>this._activeOperation;
}
private addToAccountList(accounts: sqlops.Account[], accountToAdd: sqlops.Account): AccountListOperationResult {
// Check if the entry already exists
let match = accounts.findIndex(account => AccountStore.findAccountByKey(account.key, accountToAdd.key));
if (match >= 0) {
// Account already exists, we won't do anything
return {
accountAdded: false,
accountModified: false,
accountRemoved: false,
changedAccount: null,
updatedAccounts: accounts
};
}
// Add the account to the store
accounts.push(accountToAdd);
return {
accountAdded: true,
accountModified: false,
accountRemoved: false,
changedAccount: accountToAdd,
updatedAccounts: accounts
};
}
private removeFromAccountList(accounts: sqlops.Account[], accountToRemove: sqlops.AccountKey): AccountListOperationResult {
// Check if the entry exists
let match = accounts.findIndex(account => AccountStore.findAccountByKey(account.key, accountToRemove));
if (match >= 0) {
// Account exists, remove it from the account list
accounts.splice(match, 1);
}
return {
accountAdded: false,
accountModified: false,
accountRemoved: match >= 0,
changedAccount: null,
updatedAccounts: accounts
};
}
private updateAccountList(accounts: sqlops.Account[], accountToUpdate: sqlops.AccountKey, updateOperation: (account: sqlops.Account) => void): AccountListOperationResult {
// Check if the entry exists
let match = accounts.findIndex(account => AccountStore.findAccountByKey(account.key, accountToUpdate));
if (match < 0) {
// Account doesn't exist, we won't do anything
return {
accountAdded: false,
accountModified: false,
accountRemoved: false,
changedAccount: null,
updatedAccounts: accounts
};
}
// Account exists, apply the update operation to it
updateOperation(accounts[match]);
return {
accountAdded: false,
accountModified: true,
accountRemoved: false,
changedAccount: accounts[match],
updatedAccounts: accounts
};
}
// MEMENTO IO METHODS //////////////////////////////////////////////////
private readFromMemento(): Thenable<sqlops.Account[]> {
// Initialize the account list if it isn't already
let accounts = this._memento[AccountStore.MEMENTO_KEY];
if (!accounts) {
accounts = [];
}
// Make a deep copy of the account list to ensure that the memento list isn't obliterated
accounts = JSON.parse(JSON.stringify(accounts));
return Promise.resolve(accounts);
}
private writeToMemento(accounts: sqlops.Account[]): Thenable<void> {
// Store a shallow copy of the account list to disconnect the memento list from the active list
this._memento[AccountStore.MEMENTO_KEY] = JSON.parse(JSON.stringify(accounts));
return Promise.resolve();
}
}
interface AccountListOperationResult extends AccountAdditionResult {
accountRemoved: boolean;
updatedAccounts: sqlops.Account[];
}

View File

@@ -1,58 +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 sqlops from 'sqlops';
/**
* Result from calling add/update on the account store
*/
export interface AccountAdditionResult {
/**
* Whether or not an account was added in the add/update process
*/
accountAdded: boolean;
/**
* Whether or not an account was updated in the add/update process
*/
accountModified: boolean;
/**
* The account that was added/updated (with any updates applied)
*/
changedAccount: sqlops.Account;
}
/**
* Parameters that go along with an account provider being added
*/
export interface AccountProviderAddedEventParams {
/**
* The provider that was registered
*/
addedProvider: sqlops.AccountProviderMetadata;
/**
* The accounts that were rehydrated with the provider
*/
initialAccounts: sqlops.Account[];
}
/**
* Parameters that go along when a provider's account list changes
*/
export interface UpdateAccountListEventParams {
/**
* ID of the provider who's account list changes
*/
providerId: string;
/**
* Updated list of accounts, sorted appropriately
*/
accountList: sqlops.Account[];
}

View File

@@ -1,92 +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 sqlops from 'sqlops';
import { Event } from 'vs/base/common/event';
import { AccountAdditionResult, AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const SERVICE_ID = 'accountManagementService';
export const IAccountManagementService = createDecorator<IAccountManagementService>(SERVICE_ID);
export interface IAccountManagementService {
_serviceBrand: any;
// ACCOUNT MANAGEMENT METHODS //////////////////////////////////////////
accountUpdated(account: sqlops.Account): Thenable<void>;
addAccount(providerId: string): Thenable<void>;
getAccountProviderMetadata(): Thenable<sqlops.AccountProviderMetadata[]>;
getAccountsForProvider(providerId: string): Thenable<sqlops.Account[]>;
getSecurityToken(account: sqlops.Account, resource: sqlops.AzureResource): Thenable<{}>;
removeAccount(accountKey: sqlops.AccountKey): Thenable<boolean>;
refreshAccount(account: sqlops.Account): Thenable<sqlops.Account>;
// UI METHODS //////////////////////////////////////////////////////////
openAccountListDialog(): Thenable<void>;
beginAutoOAuthDeviceCode(providerId: string, title: string, message: string, userCode: string, uri: string): Thenable<void>;
endAutoOAuthDeviceCode(): void;
cancelAutoOAuthDeviceCode(providerId: string): void;
copyUserCodeAndOpenBrowser(userCode: string, uri: string): void;
// SERVICE MANAGEMENT METHODS /////////////////////////////////////////
registerProvider(providerMetadata: sqlops.AccountProviderMetadata, provider: sqlops.AccountProvider): void;
shutdown(): void;
unregisterProvider(providerMetadata: sqlops.AccountProviderMetadata): void;
// EVENTING ////////////////////////////////////////////////////////////
readonly addAccountProviderEvent: Event<AccountProviderAddedEventParams>;
readonly removeAccountProviderEvent: Event<sqlops.AccountProviderMetadata>;
readonly updateAccountListEvent: Event<UpdateAccountListEventParams>;
}
// Enum matching the AzureResource enum from sqlops.d.ts
export enum AzureResource {
ResourceManagement = 0,
Sql = 1
}
export interface IAccountStore {
/**
* Adds the provided account if the account doesn't exist. Updates the account if it already exists
* @param {Account} account Account to add/update
* @return {Thenable<AccountAdditionResult>} Results of the add/update operation
*/
addOrUpdate(account: sqlops.Account): Thenable<AccountAdditionResult>;
/**
* Retrieves all accounts, filtered by provider ID
* @param {string} providerId ID of the provider to filter by
* @return {Thenable<Account[]>} Promise to return all accounts that belong to the provided provider
*/
getAccountsByProvider(providerId: string): Thenable<sqlops.Account[]>;
/**
* Retrieves all accounts in the store. Returns empty array if store is not initialized
* @return {Thenable<Account[]>} Promise to return all accounts
*/
getAllAccounts(): Thenable<sqlops.Account[]>;
/**
* Removes an account.
* Returns false if the account was not found.
* Otherwise, returns true.
* @param key - The key of an account.
* @returns True if the account was removed, false if the account doesn't exist
*/
remove(key: sqlops.AccountKey): Thenable<boolean>;
/**
* Updates the custom properties stored with an account.
* Returns null if no account was found to update.
* Otherwise, returns a new updated account instance.
* @param key - The key of an account.
* @param updateOperation - Operation to perform on the matching account
* @returns True if the account was modified, false if the account doesn't exist
*/
update(key: sqlops.AccountKey, updateOperation: (account: sqlops.Account) => void): Thenable<boolean>;
}

View File

@@ -1,74 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Subscription } from 'rxjs/Subscription';
import { Subject } from 'rxjs/Subject';
import { warn } from 'sql/base/common/log';
const ANGULAREVENTING_SERVICE_ID = 'angularEventingService';
export const IAngularEventingService = createDecorator<IAngularEventingService>(ANGULAREVENTING_SERVICE_ID);
export enum AngularEventType {
NAV_DATABASE,
NAV_SERVER,
DELETE_WIDGET,
PINUNPIN_TAB,
NEW_TABS,
CLOSE_TAB,
COLLAPSE_WIDGET
}
export interface IDeleteWidgetPayload {
id: string;
}
export interface IAngularEvent {
event: AngularEventType;
payload: any;
}
export interface IAngularEventingService {
_serviceBrand: any;
/**
* Adds a listener for the dashboard to send events, should only be called once for each dashboard by the dashboard itself
* @param uri Uri of the dashboard
* @param cb Listening function
* @returns
*/
onAngularEvent(uri: string, cb: (event: IAngularEvent) => void): Subscription;
/**
* Send an event to the dashboard; no op if the dashboard has not started listening yet
* @param uri Uri of the dashboard to send the event to
* @param event event to send
*/
sendAngularEvent(uri: string, event: AngularEventType, payload?: any): void;
}
export class AngularEventingService implements IAngularEventingService {
public _serviceBrand: any;
private _angularMap = new Map<string, Subject<IAngularEvent>>();
public onAngularEvent(uri: string, cb: (event: IAngularEvent) => void): Subscription {
let subject: Subject<IAngularEvent>;
if (!this._angularMap.has(uri)) {
subject = new Subject<IAngularEvent>();
this._angularMap.set(uri, subject);
} else {
subject = this._angularMap.get(uri);
}
let sub = subject.subscribe(cb);
return sub;
}
public sendAngularEvent(uri: string, event: AngularEventType, payload?: any): void {
if (!this._angularMap.has(uri)) {
warn('Got request to send an event to a dashboard that has not started listening');
} else {
this._angularMap.get(uri).next({ event, payload });
}
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { DataService } from 'sql/parts/grid/services/dataService';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
import { IBootstrapParams } from './bootstrapService';

View File

@@ -1,239 +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 { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import * as Constants from 'sql/common/constants';
import { Deferred } from 'sql/base/common/promise';
import { ConnectionProviderProperties, IConnectionProviderRegistry, Extensions as ConnectionExtensions } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
import { toObject } from 'sql/base/common/map';
import * as sqlops from 'sqlops';
import { Event, Emitter } from 'vs/base/common/event';
import { IAction } from 'vs/base/common/actions';
import { Memento } from 'vs/workbench/common/memento';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Registry } from 'vs/platform/registry/common/platform';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
export const SERVICE_ID = 'capabilitiesService';
export const HOST_NAME = 'sqlops';
export const HOST_VERSION = '1.0';
const connectionRegistry = Registry.as<IConnectionProviderRegistry>(ConnectionExtensions.ConnectionProviderContributions);
interface ConnectionCache {
[id: string]: ConnectionProviderProperties;
}
interface CapabilitiesMomento {
connectionProviderCache: ConnectionCache;
}
export const clientCapabilities = {
hostName: HOST_NAME,
hostVersion: HOST_VERSION
};
export interface ProviderFeatures {
connection: ConnectionProviderProperties;
}
export const ICapabilitiesService = createDecorator<ICapabilitiesService>(SERVICE_ID);
/**
* Interface for managing provider capabilities
*/
export interface ICapabilitiesService {
_serviceBrand: any;
/**
* Retrieve a list of registered capabilities providers
*/
getCapabilities(provider: string): ProviderFeatures;
/**
* get the old version of provider information
*/
getLegacyCapabilities(provider: string): sqlops.DataProtocolServerCapabilities;
/**
* Register a capabilities provider
*/
registerProvider(provider: sqlops.CapabilitiesProvider): void;
/**
* Returns true if the feature is available for given connection
*/
isFeatureAvailable(action: IAction, connectionManagementInfo: ConnectionManagementInfo): boolean;
/**
* When a new capabilities is registered, it emits the provider name, be to use to get the new capabilities
*/
readonly onCapabilitiesRegistered: Event<ProviderFeatures>;
/**
* Get an array of all known providers
*/
readonly providers: { [id: string]: ProviderFeatures };
}
/**
* Capabilities service implementation class. This class provides the ability
* to discover the DMP capabilties that a DMP provider offers.
*/
export class CapabilitiesService extends Disposable implements ICapabilitiesService {
_serviceBrand: any;
private _momento = new Memento('capabilities');
private _providers = new Map<string, ProviderFeatures>();
private _featureUpdateEvents = new Map<string, Emitter<ProviderFeatures>>();
private _legacyProviders = new Map<string, sqlops.DataProtocolServerCapabilities>();
private _onCapabilitiesRegistered = this._register(new Emitter<ProviderFeatures>());
public readonly onCapabilitiesRegistered = this._onCapabilitiesRegistered.event;
constructor(
@IStorageService private _storageService: IStorageService,
@IExtensionService extensionService: IExtensionService,
@IExtensionManagementService extentionManagementService: IExtensionManagementService
) {
super();
if (!this.capabilities.connectionProviderCache) {
this.capabilities.connectionProviderCache = {};
}
// handle in case some extensions have already registered (unlikley)
Object.entries(connectionRegistry.providers).map(v => {
this.handleConnectionProvider({ id: v[0], properties: v[1] });
});
// register for when new extensions are added
this._register(connectionRegistry.onNewProvider(this.handleConnectionProvider, this));
// handle adding already known capabilities (could have caching problems)
Object.entries(this.capabilities.connectionProviderCache).map(v => {
this.handleConnectionProvider({ id: v[0], properties: v[1] }, false);
});
extensionService.whenInstalledExtensionsRegistered().then(() => {
this.cleanupProviders();
});
this._register(extentionManagementService.onDidUninstallExtension(({ identifier }) => {
let extensionid = getIdFromLocalExtensionId(identifier.id);
extensionService.getExtensions().then(i => {
let extension = i.find(c => c.id === extensionid);
let id = extension.contributes['connectionProvider'].providerId;
delete this.capabilities.connectionProviderCache[id];
});
}));
}
private cleanupProviders(): void {
let knownProviders = Object.keys(connectionRegistry.providers);
for (let key in this.capabilities.connectionProviderCache) {
if (!knownProviders.includes(key)) {
this._providers.delete(key);
delete this.capabilities.connectionProviderCache[key];
}
}
}
private handleConnectionProvider(e: { id: string, properties: ConnectionProviderProperties }, isNew = true): void {
let provider = this._providers.get(e.id);
if (provider) {
provider.connection = e.properties;
} else {
provider = {
connection: e.properties
};
this._providers.set(e.id, provider);
}
if (!this._featureUpdateEvents.has(e.id)) {
this._featureUpdateEvents.set(e.id, new Emitter<ProviderFeatures>());
}
if (isNew) {
this.capabilities.connectionProviderCache[e.id] = e.properties;
this._onCapabilitiesRegistered.fire(provider);
}
}
/**
* Retrieve a list of registered server capabilities
*/
public getCapabilities(provider: string): ProviderFeatures {
return this._providers.get(provider);
}
public getLegacyCapabilities(provider: string): sqlops.DataProtocolServerCapabilities {
return this._legacyProviders.get(provider);
}
public get providers(): { [id: string]: ProviderFeatures } {
return toObject(this._providers);
}
private get capabilities(): CapabilitiesMomento {
return this._momento.getMemento(this._storageService) as CapabilitiesMomento;
}
/**
* Register the capabilities provider and query the provider for its capabilities
* @param provider
*/
public registerProvider(provider: sqlops.CapabilitiesProvider): void {
// request the capabilities from server
provider.getServerCapabilities(clientCapabilities).then(serverCapabilities => {
this._legacyProviders.set(serverCapabilities.providerName, serverCapabilities);
});
}
/**
* Returns true if the feature is available for given connection
* @param featureComponent a component which should have the feature name
* @param connectionManagementInfo connectionManagementInfo
*/
public isFeatureAvailable(action: IAction, connectionManagementInfo: ConnectionManagementInfo): boolean {
let isCloud = connectionManagementInfo && connectionManagementInfo.serverInfo && connectionManagementInfo.serverInfo.isCloud;
let isMssql = connectionManagementInfo.connectionProfile.providerName === 'MSSQL';
// TODO: The logic should from capabilities service.
if (action) {
let featureName: string = action.id;
switch (featureName) {
case Constants.BackupFeatureName:
if (isMssql) {
return connectionManagementInfo.connectionProfile.databaseName && !isCloud;
} else {
return !!connectionManagementInfo.connectionProfile.databaseName;
}
case Constants.RestoreFeatureName:
if (isMssql) {
return !isCloud;
} else {
return !!connectionManagementInfo.connectionProfile.databaseName;
}
default:
return true;
}
} else {
return true;
}
}
public shutdown(): void {
this._momento.saveMemento();
}
}

View File

@@ -4,25 +4,25 @@
*--------------------------------------------------------------------------------------------*/
/* Node Modules */
import { Injectable, Inject, forwardRef, OnDestroy } from '@angular/core';
import { Injectable, Inject } from '@angular/core';
import { Observable } from 'rxjs/Observable';
/* SQL imports */
import { IDefaultComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { IMetadataService } from 'sql/services/metadata/metadataService';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import { IAdminService } from 'sql/parts/admin/common/adminService';
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { IMetadataService } from 'sql/platform/metadata/common/metadataService';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
import { IAdminService } from 'sql/workbench/services/admin/common/adminService';
import { IQueryManagementService } from 'sql/platform/query/common/queryManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'sqlops';
/* VS imports */
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
/* Wrapper for a metadata service that contains the uri string to use on each request */
export class SingleConnectionMetadataService {

View File

@@ -1,80 +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 { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import * as sqlops from 'sqlops';
import { Deferred } from 'sql/base/common/promise';
export const SERVICE_ID = 'credentialsService';
export interface CredentialManagementEvents {
onSaveCredential(credentialId: string, password: string): Thenable<boolean>;
onReadCredential(credentialId: string): Thenable<sqlops.Credential>;
onDeleteCredential(credentialId: string): Thenable<boolean>;
}
export const ICredentialsService = createDecorator<ICredentialsService>(SERVICE_ID);
export interface ICredentialsService {
_serviceBrand: any;
saveCredential(credentialId: string, password: string): Thenable<boolean>;
readCredential(credentialId: string): Thenable<sqlops.Credential>;
deleteCredential(credentialId: string): Thenable<boolean>;
addEventListener(handle: number, events: CredentialManagementEvents): IDisposable;
}
export class CredentialsService implements ICredentialsService {
_serviceBrand: any;
private disposables: IDisposable[] = [];
private _serverEvents: { [handle: number]: CredentialManagementEvents; } = Object.create(null);
private _lastHandle: number;
private _onServerEventsReady: Deferred<void> = new Deferred<void>();
constructor() {
}
public addEventListener(handle: number, events: CredentialManagementEvents): IDisposable {
this._lastHandle = handle;
this._serverEvents[handle] = events;
this._onServerEventsReady.resolve();
return {
dispose: () => {
}
};
}
public saveCredential(credentialId: string, password: string): Thenable<boolean> {
return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onSaveCredential(credentialId, password));
}
public readCredential(credentialId: string): Thenable<sqlops.Credential> {
return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onReadCredential(credentialId));
}
public deleteCredential(credentialId: string): Thenable<boolean> {
return this._onServerEventsReady.promise.then(() => this._serverEvents[this._lastHandle].onDeleteCredential(credentialId));
}
public dispose(): void {
this.disposables = dispose(this.disposables);
}
}

View File

@@ -1,78 +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 sqlops from 'sqlops';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { TPromise } from 'vs/base/common/winjs.base';
import { localize } from 'vs/nls';
import { data } from 'vs/base/test/common/filters.perf.data';
export const SERVICE_ID = 'dacFxService';
export const IDacFxService = createDecorator<IDacFxService>(SERVICE_ID);
export interface IDacFxService {
_serviceBrand: any;
registerProvider(providerId: string, provider: sqlops.DacFxServicesProvider): void;
exportBacpac(sourceDatabaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
importBacpac(packageFilePath: string, targetDatabaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
extractDacpac(sourceDatabaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
deployDacpac(packageFilePath: string, targetDatabaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): void;
}
export class DacFxService implements IDacFxService {
_serviceBrand: any;
private _providers: { [handle: string]: sqlops.DacFxServicesProvider; } = Object.create(null);
constructor(
@IConnectionManagementService private _connectionService: IConnectionManagementService
) {
}
registerProvider(providerId: string, provider: sqlops.DacFxServicesProvider): void {
this._providers[providerId] = provider;
}
exportBacpac(databasesName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
return this._runAction(ownerUri, (runner) => {
return runner.exportBacpac(databasesName, packageFilePath, ownerUri, taskExecutionMode);
});
}
importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
return this._runAction(ownerUri, (runner) => {
return runner.importBacpac(packageFilePath, databaseName, ownerUri, taskExecutionMode);
});
}
extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
return this._runAction(ownerUri, (runner) => {
return runner.extractDacpac(databaseName, packageFilePath, applicationName, applicationVersion, ownerUri, taskExecutionMode);
});
}
deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.DacFxResult> {
return this._runAction(ownerUri, (runner) => {
return runner.deployDacpac(packageFilePath, databaseName, upgradeExisting, ownerUri, taskExecutionMode);
});
}
private _runAction<T>(uri: string, action: (handler: sqlops.DacFxServicesProvider) => Thenable<T>): Thenable<T> {
let providerId: string = this._connectionService.getProviderIdFromUri(uri);
if (!providerId) {
return TPromise.wrapError(new Error(localize('providerIdNotValidError', "Connection is required in order to interact with DacFxService")));
}
let handler = this._providers[providerId];
if (handler) {
return action(handler);
} else {
return TPromise.wrapError(new Error(localize('noHandlerRegistered', "No Handler Registered")));
}
}
}

View File

@@ -1,26 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import * as DOM from 'vs/base/browser/dom';
import * as sqlops from 'sqlops';
export const IDashboardService = createDecorator<IDashboardService>('dashboardService');
export interface IDashboardService {
_serviceBrand: any;
readonly onDidOpenDashboard: Event<sqlops.DashboardDocument>;
readonly onDidChangeToDashboard: Event<sqlops.DashboardDocument>;
readonly onLayout: Event<DOM.Dimension>;
openDashboard(document: sqlops.DashboardDocument): void;
changeToDashboard(document: sqlops.DashboardDocument): void;
layout(dimension: DOM.Dimension): void;
}

View File

@@ -1,34 +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 { IDashboardService } from './dashboardService';
import { Event, Emitter } from 'vs/base/common/event';
import * as DOM from 'vs/base/browser/dom';
import * as sqlops from 'sqlops';
export class DashboardService implements IDashboardService {
public _serviceBrand: any;
private _onDidOpenDashboard = new Emitter<sqlops.DashboardDocument>();
public readonly onDidOpenDashboard: Event<sqlops.DashboardDocument> = this._onDidOpenDashboard.event;
private _onDidChangeToDashboard = new Emitter<sqlops.DashboardDocument>();
public readonly onDidChangeToDashboard: Event<sqlops.DashboardDocument> = this._onDidChangeToDashboard.event;
private _onLayout = new Emitter<DOM.Dimension>();
public readonly onLayout: Event<DOM.Dimension> = this._onLayout.event;
public openDashboard(document: sqlops.DashboardDocument): void {
this._onDidOpenDashboard.fire(document);
}
public changeToDashboard(document: sqlops.DashboardDocument): void {
this._onDidChangeToDashboard.fire(document);
}
public layout(dimension: DOM.Dimension): void {
this._onLayout.fire(dimension);
}
}

View File

@@ -1,31 +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 { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import * as sqlops from 'sqlops';
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IView, IModelView } from 'sql/services/model/modelViewService';
export const SERVICE_ID = 'dashboardViewService';
export interface IDashboardWebview extends IView {
setHtml(html: string): void;
onMessage: Event<string>;
sendMessage(message: string);
}
export interface IDashboardViewService {
_serviceBrand: any;
onRegisteredWebview: Event<IDashboardWebview>;
registerWebview(widget: IDashboardWebview);
onRegisteredModelView: Event<IModelView>;
registerModelView(widget: IModelView);
}
export const IDashboardViewService = createDecorator<IDashboardViewService>(SERVICE_ID);

View File

@@ -1,28 +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 { IDashboardViewService, IDashboardWebview } from 'sql/services/dashboard/common/dashboardViewService';
import { Event, Emitter } from 'vs/base/common/event';
import { IModelView } from 'sql/services/model/modelViewService';
export class DashboardViewService implements IDashboardViewService {
_serviceBrand: any;
private _onRegisteredWebview = new Emitter<IDashboardWebview>();
public readonly onRegisteredWebview: Event<IDashboardWebview> = this._onRegisteredWebview.event;
private _onRegisteredModelView = new Emitter<IModelView>();
public readonly onRegisteredModelView: Event<IModelView> = this._onRegisteredModelView.event;
public registerWebview(widget: IDashboardWebview) {
this._onRegisteredWebview.fire(widget);
}
registerModelView(view: IModelView) {
this._onRegisteredModelView.fire(view);
}
}

View File

@@ -1,103 +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 { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import * as sqlops from 'sqlops';
export const SERVICE_ID = 'metadataService';
export const IMetadataService = createDecorator<IMetadataService>(SERVICE_ID);
export interface IMetadataService {
_serviceBrand: any;
getMetadata(connectionUri: string): Thenable<sqlops.ProviderMetadata>;
getDatabaseNames(connectionUri: string): Thenable<string[]>;
getTableInfo(connectionUri: string, metadata: sqlops.ObjectMetadata): Thenable<sqlops.ColumnMetadata[]>;
getViewInfo(connectionUri: string, metadata: sqlops.ObjectMetadata): Thenable<sqlops.ColumnMetadata[]>;
/**
* Register a metadata provider
*/
registerProvider(providerId: string, provider: sqlops.MetadataProvider): void;
}
export class MetadataService implements IMetadataService {
public _serviceBrand: any;
private _disposables: IDisposable[] = [];
private _providers: { [handle: string]: sqlops.MetadataProvider; } = Object.create(null);
constructor( @IConnectionManagementService private _connectionService: IConnectionManagementService) {
}
public getMetadata(connectionUri: string): Thenable<sqlops.ProviderMetadata> {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
let provider = this._providers[providerId];
if (provider) {
return provider.getMetadata(connectionUri);
}
}
return Promise.resolve(undefined);
}
public getDatabaseNames(connectionUri: string): Thenable<string[]> {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
let provider = this._providers[providerId];
if (provider) {
return provider.getDatabases(connectionUri);
}
}
return Promise.resolve([]);
}
public getTableInfo(connectionUri: string, metadata: sqlops.ObjectMetadata): Thenable<sqlops.ColumnMetadata[]> {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
let provider = this._providers[providerId];
if (provider) {
return provider.getTableInfo(connectionUri, metadata);
}
}
return Promise.resolve(undefined);
}
public getViewInfo(connectionUri: string, metadata: sqlops.ObjectMetadata): Thenable<sqlops.ColumnMetadata[]> {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
let provider = this._providers[providerId];
if (provider) {
return provider.getViewInfo(connectionUri, metadata);
}
}
return Promise.resolve(undefined);
}
/**
* Register a metadata provider
*/
public registerProvider(providerId: string, provider: sqlops.MetadataProvider): void {
this._providers[providerId] = provider;
}
public dispose(): void {
this._disposables = dispose(this._disposables);
}
}

View File

@@ -1,35 +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 sqlops from 'sqlops';
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IComponentEventArgs } from 'sql/parts/modelComponents/interfaces';
import { Event } from 'vs/base/common/event';
export interface IView {
readonly id: string;
readonly connection: sqlops.connection.Connection;
readonly serverInfo: sqlops.ServerInfo;
}
export interface IModelViewEventArgs extends IComponentEventArgs {
isRootComponent: boolean;
}
export interface IModelView extends IView {
initializeModel(rootComponent: IComponentShape, validationCallback?: (componentId: string) => Thenable<boolean>): void;
clearContainer(componentId: string): void;
addToContainer(containerId: string, item: IItemConfig, index?: number): void;
removeFromContainer(containerId: string, item: IItemConfig): void;
setLayout(componentId: string, layout: any): void;
setProperties(componentId: string, properties: { [key: string]: any }): void;
setDataProvider(handle: number, componentId: string, context: any): void;
refreshDataProvider(componentId: string, item: any): void;
registerEvent(componentId: string);
onEvent: Event<IModelViewEventArgs>;
validate(componentId: string): Thenable<boolean>;
readonly onDestroy: Event<void>;
}

View File

@@ -1,23 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import * as sqlops from 'sqlops';
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IView, IModelView } from 'sql/services/model/modelViewService';
export const SERVICE_ID = 'modelViewService';
export interface IModelViewService {
_serviceBrand: any;
onRegisteredModelView: Event<IModelView>;
registerModelView(widget: IModelView);
}
export const IModelViewService = createDecorator<IModelViewService>(SERVICE_ID);

View File

@@ -1,21 +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 { IModelViewService } from 'sql/services/modelComponents/modelViewService';
import { Event, Emitter } from 'vs/base/common/event';
import { IModelView } from 'sql/services/model/modelViewService';
export class ModelViewService implements IModelViewService {
_serviceBrand: any;
private _onRegisteredModelView = new Emitter<IModelView>();
public readonly onRegisteredModelView: Event<IModelView> = this._onRegisteredModelView.event;
public registerModelView(view: IModelView) {
this._onRegisteredModelView.fire(view);
}
}

View File

@@ -1,366 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// Note: the code in the v3 and v4 namespaces has been adapted (with significant changes) from https://github.com/nteract/nteract/tree/master/packages/commutable
'use strict';
import { nb } from 'sqlops';
import * as json from 'vs/base/common/json';
import * as pfs from 'vs/base/node/pfs';
import URI from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { JSONObject } from 'sql/parts/notebook/models/jsonext';
import { OutputTypes } from 'sql/parts/notebook/models/contracts';
import { nbversion } from 'sql/parts/notebook/notebookConstants';
type MimeBundle = { [key: string]: string | string[] | undefined };
export class LocalContentManager implements nb.ContentManager {
public async getNotebookContents(notebookUri: URI): Promise<nb.INotebookContents> {
if (!notebookUri) {
return undefined;
}
// TODO validate this is an actual file URI, and error if not
let path = notebookUri.fsPath;
// Note: intentionally letting caller handle exceptions
let notebookFileBuffer = await pfs.readFile(path);
let contents: JSONObject = json.parse(notebookFileBuffer.toString());
if (contents) {
if (contents.nbformat === 4) {
return v4.readNotebook(<any>contents);
} else if (contents.nbformat === 3) {
return v3.readNotebook(<any>contents);
}
if (contents.nbformat) {
throw new TypeError(localize('nbformatNotRecognized', 'nbformat v{0}.{1} not recognized', contents.nbformat, contents.nbformat_minor));
}
}
// else, fallthrough condition
throw new TypeError(localize('nbNotSupported', 'This notebook format is not supported'));
}
public async save(notebookUri: URI, notebook: nb.INotebookContents): Promise<nb.INotebookContents> {
// Convert to JSON with pretty-print functionality
let contents = JSON.stringify(notebook, undefined, ' ');
let path = notebookUri.fsPath;
await pfs.writeFile(path, contents);
return notebook;
}
}
namespace v4 {
export function readNotebook(contents: nb.INotebookContents): nb.INotebookContents {
let notebook: nb.INotebookContents = {
cells: [],
metadata: contents.metadata,
nbformat: 4,
nbformat_minor: contents.nbformat_minor
};
for (let cell of contents.cells) {
notebook.cells.push(readCell(cell));
}
return notebook;
}
function readCell(cell: nb.ICellContents): nb.ICellContents {
switch (cell.cell_type) {
case 'markdown':
case 'raw':
return createDefaultCell(cell);
case 'code':
return createCodeCell(cell);
default:
throw new TypeError(localize('unknownCellType', 'Cell type {0} unknown', cell.cell_type));
}
}
export function createDefaultCell(cell: nb.ICellContents): nb.ICellContents {
return {
cell_type: cell.cell_type,
source: demultiline(cell.source),
metadata: cell.metadata
};
}
function createCodeCell(cell: nb.ICellContents): nb.ICellContents {
return {
cell_type: cell.cell_type,
source: demultiline(cell.source),
metadata: cell.metadata,
execution_count: cell.execution_count,
outputs: createOutputs(cell)
};
}
function createOutputs(cell: nb.ICellContents): nb.ICellOutput[] {
return cell.outputs && cell.outputs.length > 0 ? cell.outputs.map(output => createOutput(output as nb.Output)) : [];
}
function createOutput(output: nb.Output): nb.ICellOutput {
switch (output.output_type) {
case OutputTypes.ExecuteResult:
return <nb.IExecuteResult>{
output_type: output.output_type,
execution_count: output.execution_count,
data: createMimeBundle(output.data),
metadata: output.metadata
};
case OutputTypes.DisplayData:
case OutputTypes.UpdateDisplayData:
return <nb.IDisplayResult>{
output_type: output.output_type,
data: createMimeBundle(output.data),
metadata: output.metadata
};
case 'stream':
return <nb.IStreamResult>{
output_type: output.output_type,
name: output.name,
text: demultiline(output.text)
};
case 'error':
return <nb.IErrorResult>{
output_type: 'error',
ename: output.ename,
evalue: output.evalue,
// Note: this is one of the cases where the Array of strings (for
// traceback) is part of the format, not a multiline string
traceback: output.traceback
};
default:
// Should never get here
throw new TypeError(localize('unrecognizedOutput', 'Output type {0} not recognized', (<any>output).output_type));
}
}
function createMimeBundle(oldMimeBundle: MimeBundle): MimeBundle {
let mimeBundle: MimeBundle = {};
for (let key of Object.keys(oldMimeBundle)) {
mimeBundle[key] = cleanMimeData(key, oldMimeBundle[key]);
}
return mimeBundle;
}
/**
* Cleans mimedata, primarily converts an array of strings into a single string
* joined by newlines.
*
* @param key The key, usually a mime type, that is associated with the mime data.
* @param data The mime data to clean.
*
* @returns The cleaned mime data.
*/
export function cleanMimeData(key: string, data: string | string[] | undefined) {
// See https://github.com/jupyter/nbformat/blob/62d6eb8803616d198eaa2024604d1fe923f2a7b3/nbformat/v4/nbformat.v4.schema.json#L368
if (isJSONKey(key)) {
// Data stays as is for JSON types
return data;
}
if (typeof data === 'string' || Array.isArray(data)) {
return demultiline(data);
}
throw new TypeError(localize('invalidMimeData', 'Data for {0} is expected to be a string or an Array of strings', key));
}
export function demultiline(value: nb.MultilineString): string {
return Array.isArray(value) ? value.join('') : value;
}
function isJSONKey(key: string): boolean {
return /^application\/(.*\+)?json$/.test(key);
}
}
namespace v3 {
export function readNotebook(contents: Notebook): nb.INotebookContents {
let notebook: nb.INotebookContents = {
cells: [],
metadata: contents.metadata,
// Note: upgrading to v4 as we're converting to our codebase
nbformat: 4,
nbformat_minor: nbversion.MINOR_VERSION
};
if (contents.worksheets) {
for (let worksheet of contents.worksheets) {
if (worksheet.cells) {
notebook.cells.push(...worksheet.cells.map(cell => createCell(cell)));
}
}
}
return notebook;
}
function createCell(cell: Cell): nb.ICellContents {
switch (cell.cell_type) {
case 'markdown':
case 'raw':
return v4.createDefaultCell(cell);
case 'code':
return createCodeCell(cell as CodeCell);
case 'heading':
return createHeadingCell(cell);
default:
throw new TypeError(`Cell type ${(cell as any).cell_type} unknown`);
}
}
function createMimeBundle(oldMimeBundle: MimeOutput): MimeBundle {
let mimeBundle: MimeBundle = {};
for (let key of Object.keys(oldMimeBundle)) {
// v3 had non-media types for rich media
if (key in VALID_MIMETYPES) {
let newKey = VALID_MIMETYPES[key as MimeTypeKey];
mimeBundle[newKey] = v4.cleanMimeData(newKey, oldMimeBundle[key]);
}
}
return mimeBundle;
}
const createOutput = (output: Output): nb.ICellOutput => {
switch (output.output_type) {
case 'pyout':
return <nb.IExecuteResult> {
output_type: OutputTypes.ExecuteResult,
execution_count: output.prompt_number,
data: createMimeBundle(output),
metadata: output.metadata
};
case 'display_data':
return <nb.IDisplayData> {
output_type: OutputTypes.DisplayData,
data: createMimeBundle(output),
metadata: output.metadata
};
case 'stream':
// Default to stdout in all cases unless it's stderr
const name = output.stream === 'stderr' ? 'stderr' : 'stdout';
return <nb.IStreamResult> {
output_type: OutputTypes.Stream,
name: name,
text: v4.demultiline(output.text)
};
case 'pyerr':
return <nb.IErrorResult> {
output_type: OutputTypes.Error,
ename: output.ename,
evalue: output.evalue,
traceback: output.traceback
};
default:
throw new TypeError(localize('unrecognizedOutputType', 'Output type {0} not recognized', output.output_type));
}
};
function createCodeCell(cell: CodeCell): nb.ICellContents {
return <nb.ICellContents> {
cell_type: cell.cell_type,
source: v4.demultiline(cell.input),
outputs: cell.outputs.map(createOutput),
execution_count: cell.prompt_number,
metadata: cell.metadata
};
}
function createHeadingCell(cell: HeadingCell): nb.ICellContents {
// v3 heading cells are just markdown cells in v4+
return <nb.ICellContents> {
cell_type: 'markdown',
source: Array.isArray(cell.source)
? v4.demultiline(
cell.source.map(line =>
Array(cell.level)
.join('#')
.concat(' ')
.concat(line)
)
)
: cell.source,
metadata: cell.metadata
};
}
const VALID_MIMETYPES = {
text: 'text/plain',
latex: 'text/latex',
png: 'image/png',
jpeg: 'image/jpeg',
svg: 'image/svg+xml',
html: 'text/html',
javascript: 'application/x-javascript',
json: 'application/javascript',
pdf: 'application/pdf'
};
type MimeTypeKey = keyof typeof VALID_MIMETYPES;
type MimePayload = { [P in MimeTypeKey]?: nb.MultilineString };
interface MimeOutput<T extends string = string> extends MimePayload {
output_type: T;
prompt_number?: number;
metadata: object;
}
export interface ExecuteResult extends MimeOutput<'pyout'> { }
export interface DisplayData extends MimeOutput<'display_data'> { }
export interface StreamOutput {
output_type: 'stream';
stream: string;
text: nb.MultilineString;
}
export interface ErrorOutput {
output_type: 'error' | 'pyerr';
ename: string;
evalue: string;
traceback: string[];
}
export type Output = ExecuteResult | DisplayData | StreamOutput | ErrorOutput;
export interface HeadingCell {
cell_type: 'heading';
metadata: JSONObject;
source: nb.MultilineString;
level: number;
}
export interface CodeCell {
cell_type: 'code';
language: string;
collapsed: boolean;
metadata: JSONObject;
input: nb.MultilineString;
prompt_number: number;
outputs: Array<Output>;
}
export type Cell = nb.ICellContents | HeadingCell | CodeCell;
export interface Worksheet {
cells: Cell[];
metadata: object;
}
export interface Notebook {
worksheets: Worksheet[];
metadata: nb.INotebookMetadata;
nbformat: 3;
nbformat_minor: number;
}
}

View File

@@ -1,116 +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 { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { localize } from 'vs/nls';
import * as platform from 'vs/platform/registry/common/platform';
import { Event, Emitter } from 'vs/base/common/event';
export const Extensions = {
NotebookProviderContribution: 'notebook.providers'
};
export interface NotebookProviderRegistration {
provider: string;
fileExtensions: string | string[];
standardKernels: string | string[];
}
let notebookProviderType: IJSONSchema = {
type: 'object',
default: { provider: '', fileExtensions: [], standardKernels: [] },
properties: {
provider: {
description: localize('carbon.extension.contributes.notebook.provider', 'Identifier of the notebook provider.'),
type: 'string'
},
fileExtensions: {
description: localize('carbon.extension.contributes.notebook.fileExtensions', 'What file extensions should be registered to this notebook provider'),
oneOf: [
{ type: 'string' },
{
type: 'array',
items: {
type: 'string'
}
}
]
},
standardKernels: {
description: localize('carbon.extension.contributes.notebook.standardKernels', 'What kernels should be standard with this notebook provider'),
oneOf: [
{ type: 'string' },
{
type: 'array',
items: {
type: 'string'
}
}
]
}
}
};
let notebookContrib: IJSONSchema = {
description: localize('vscode.extension.contributes.notebook.providers', "Contributes notebook providers."),
oneOf: [
notebookProviderType,
{
type: 'array',
items: notebookProviderType
}
]
};
export interface INotebookProviderRegistry {
readonly registrations: NotebookProviderRegistration[];
readonly onNewRegistration: Event<{ id: string, registration: NotebookProviderRegistration }>;
registerNotebookProvider(registration: NotebookProviderRegistration): void;
}
class NotebookProviderRegistry implements INotebookProviderRegistry {
private providerIdToRegistration = new Map<string, NotebookProviderRegistration>();
private _onNewRegistration = new Emitter<{ id: string, registration: NotebookProviderRegistration }>();
public readonly onNewRegistration: Event<{ id: string, registration: NotebookProviderRegistration }> = this._onNewRegistration.event;
registerNotebookProvider(registration: NotebookProviderRegistration): void {
// Note: this method intentionally overrides default provider for a file type.
// This means that any built-in provider will be overridden by registered extensions
this.providerIdToRegistration.set(registration.provider, registration);
this._onNewRegistration.fire( { id: registration.provider, registration: registration });
}
public get registrations(): NotebookProviderRegistration[] {
let registrationArray: NotebookProviderRegistration[] = [];
this.providerIdToRegistration.forEach(p => registrationArray.push(p));
return registrationArray;
}
}
const notebookProviderRegistry = new NotebookProviderRegistry();
platform.Registry.add(Extensions.NotebookProviderContribution, notebookProviderRegistry);
ExtensionsRegistry.registerExtensionPoint<NotebookProviderRegistration | NotebookProviderRegistration[]>(Extensions.NotebookProviderContribution, [], notebookContrib).setHandler(extensions => {
function handleExtension(contrib: NotebookProviderRegistration, extension: IExtensionPointUser<any>) {
notebookProviderRegistry.registerNotebookProvider(contrib);
}
for (let extension of extensions) {
const { value } = extension;
if (Array.isArray<NotebookProviderRegistration>(value)) {
for (let command of value) {
handleExtension(command, extension);
}
} else {
handleExtension(value, extension);
}
}
});

View File

@@ -1,106 +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 sqlops from 'sqlops';
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import URI from 'vs/base/common/uri';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { RenderMimeRegistry } from 'sql/parts/notebook/outputs/registry';
import { ModelFactory } from 'sql/parts/notebook/models/modelFactory';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { NotebookInput } from 'sql/parts/notebook/notebookInput';
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
import { ICellModel, INotebookModel } from 'sql/parts/notebook/models/modelInterfaces';
export const SERVICE_ID = 'notebookService';
export const INotebookService = createDecorator<INotebookService>(SERVICE_ID);
export const DEFAULT_NOTEBOOK_PROVIDER = 'builtin';
export const DEFAULT_NOTEBOOK_FILETYPE = 'IPYNB';
export const SQL_NOTEBOOK_PROVIDER = 'sql';
export interface INotebookService {
_serviceBrand: any;
readonly onNotebookEditorAdd: Event<INotebookEditor>;
readonly onNotebookEditorRemove: Event<INotebookEditor>;
onNotebookEditorRename: Event<INotebookEditor>;
readonly isRegistrationComplete: boolean;
readonly registrationComplete: Promise<void>;
/**
* Register a metadata provider
*/
registerProvider(providerId: string, provider: INotebookProvider): void;
/**
* Register a metadata provider
*/
unregisterProvider(providerId: string): void;
getSupportedFileExtensions(): string[];
getProvidersForFileType(fileType: string): string[];
/**
* Initializes and returns a Notebook manager that can handle all important calls to open, display, and
* run cells in a notebook.
* @param providerId ID for the provider to be used to instantiate a backend notebook service
* @param uri URI for a notebook that is to be opened. Based on this an existing manager may be used, or
* a new one may need to be created
*/
getOrCreateNotebookManager(providerId: string, uri: URI): Thenable<INotebookManager>;
addNotebookEditor(editor: INotebookEditor): void;
removeNotebookEditor(editor: INotebookEditor): void;
listNotebookEditors(): INotebookEditor[];
shutdown(): void;
getMimeRegistry(): RenderMimeRegistry;
renameNotebookEditor(oldUri: URI, newUri: URI, currentEditor: INotebookEditor): void;
}
export interface INotebookProvider {
readonly providerId: string;
getNotebookManager(notebookUri: URI): Thenable<INotebookManager>;
handleNotebookClosed(notebookUri: URI): void;
}
export interface INotebookManager {
providerId: string;
readonly contentManager: sqlops.nb.ContentManager;
readonly sessionManager: sqlops.nb.SessionManager;
readonly serverManager: sqlops.nb.ServerManager;
}
export interface INotebookParams extends IBootstrapParams {
notebookUri: URI;
input: NotebookInput;
providerId: string;
providers: string[];
isTrusted: boolean;
profile?: IConnectionProfile;
modelFactory?: ModelFactory;
}
export interface INotebookEditor {
readonly notebookParams: INotebookParams;
readonly id: string;
readonly cells?: ICellModel[];
readonly modelReady: Promise<INotebookModel>;
isDirty(): boolean;
isActive(): boolean;
isVisible(): boolean;
save(): Promise<boolean>;
executeEdits(edits: ISingleNotebookEditOperation[]): boolean;
}

View File

@@ -1,449 +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 { nb } from 'sqlops';
import { localize } from 'vs/nls';
import URI from 'vs/base/common/uri';
import { Registry } from 'vs/platform/registry/common/platform';
import {
INotebookService, INotebookManager, INotebookProvider, DEFAULT_NOTEBOOK_PROVIDER,
DEFAULT_NOTEBOOK_FILETYPE, INotebookEditor, SQL_NOTEBOOK_PROVIDER
} from 'sql/services/notebook/notebookService';
import { RenderMimeRegistry } from 'sql/parts/notebook/outputs/registry';
import { standardRendererFactories } from 'sql/parts/notebook/outputs/factories';
import { LocalContentManager } from 'sql/services/notebook/localContentManager';
import { SessionManager } from 'sql/services/notebook/sessionManager';
import { Extensions, INotebookProviderRegistry, NotebookProviderRegistration } from 'sql/services/notebook/notebookRegistry';
import { Emitter, Event } from 'vs/base/common/event';
import { Memento } from 'vs/workbench/common/memento';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionManagementService, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { Disposable } from 'vs/base/common/lifecycle';
import { getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { Deferred } from 'sql/base/common/promise';
import { SqlSessionManager } from 'sql/services/notebook/sqlSessionManager';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { sqlNotebooksEnabled } from 'sql/parts/notebook/notebookUtils';
export interface NotebookProviderProperties {
provider: string;
fileExtensions: string[];
}
interface NotebookProviderCache {
[id: string]: NotebookProviderProperties;
}
interface NotebookProvidersMemento {
notebookProviderCache: NotebookProviderCache;
}
const notebookRegistry = Registry.as<INotebookProviderRegistry>(Extensions.NotebookProviderContribution);
class ProviderDescriptor {
private _instanceReady = new Deferred<INotebookProvider>();
constructor(private providerId: string, private _instance?: INotebookProvider) {
if (_instance) {
this._instanceReady.resolve(_instance);
}
}
public get instanceReady(): Promise<INotebookProvider> {
return this._instanceReady.promise;
}
public get instance(): INotebookProvider {
return this._instance;
}
public set instance(value: INotebookProvider) {
this._instance = value;
this._instanceReady.resolve(value);
}
}
export class NotebookService extends Disposable implements INotebookService {
_serviceBrand: any;
private _memento = new Memento('notebookProviders');
private _mimeRegistry: RenderMimeRegistry;
private _providers: Map<string, ProviderDescriptor> = new Map();
private _managersMap: Map<string, INotebookManager[]> = new Map();
private _onNotebookEditorAdd = new Emitter<INotebookEditor>();
private _onNotebookEditorRemove = new Emitter<INotebookEditor>();
private _onCellChanged = new Emitter<INotebookEditor>();
private _onNotebookEditorRename = new Emitter<INotebookEditor>();
private _editors = new Map<string, INotebookEditor>();
private _fileToProviders = new Map<string, NotebookProviderRegistration[]>();
private _registrationComplete = new Deferred<void>();
private _isRegistrationComplete = false;
constructor(
@IStorageService private _storageService: IStorageService,
@IExtensionService extensionService: IExtensionService,
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
@IInstantiationService private _instantiationService: IInstantiationService
) {
super();
this._register(notebookRegistry.onNewRegistration(this.updateRegisteredProviders, this));
this.registerBuiltInProvider();
if (extensionService) {
extensionService.whenInstalledExtensionsRegistered().then(() => {
this.cleanupProviders();
this._isRegistrationComplete = true;
this._registrationComplete.resolve();
});
}
if (extensionManagementService) {
this._register(extensionManagementService.onDidUninstallExtension(({ identifier }) => this.removeContributedProvidersFromCache(identifier, extensionService)));
}
}
private updateRegisteredProviders(p: { id: string; registration: NotebookProviderRegistration; }) {
let registration = p.registration;
if (!this._providers.has(p.id)) {
this._providers.set(p.id, new ProviderDescriptor(p.id));
}
if (registration.fileExtensions) {
if (Array.isArray<string>(registration.fileExtensions)) {
for (let fileType of registration.fileExtensions) {
this.addFileProvider(fileType, registration);
}
}
else {
this.addFileProvider(registration.fileExtensions, registration);
}
}
}
registerProvider(providerId: string, instance: INotebookProvider): void {
let providerDescriptor = this._providers.get(providerId);
if (providerDescriptor) {
// Update, which will resolve the promise for anyone waiting on the instance to be registered
providerDescriptor.instance = instance;
} else {
this._providers.set(providerId, new ProviderDescriptor(providerId, instance));
}
}
unregisterProvider(providerId: string): void {
this._providers.delete(providerId);
}
get isRegistrationComplete(): boolean {
return this._isRegistrationComplete;
}
get registrationComplete(): Promise<void> {
return this._registrationComplete.promise;
}
private addFileProvider(fileType: string, provider: NotebookProviderRegistration) {
let providers = this._fileToProviders.get(fileType.toUpperCase());
if (!providers) {
providers = [];
}
providers.push(provider);
this._fileToProviders.set(fileType.toUpperCase(), providers);
}
getSupportedFileExtensions(): string[] {
return Array.from(this._fileToProviders.keys());
}
getProvidersForFileType(fileType: string): string[] {
fileType = fileType.toUpperCase();
let providers = this._fileToProviders.get(fileType);
return providers ? providers.map(provider => provider.provider) : undefined;
}
public shutdown(): void {
this._managersMap.forEach(manager => {
manager.forEach(m => {
if (m.serverManager) {
// TODO should this thenable be awaited?
m.serverManager.stopServer();
}
});
});
}
async getOrCreateNotebookManager(providerId: string, uri: URI): Promise<INotebookManager> {
if (!uri) {
throw new Error(localize('notebookUriNotDefined', 'No URI was passed when creating a notebook manager'));
}
let uriString = uri.toString();
let managers: INotebookManager[] = this._managersMap.get(uriString);
// If manager already exists for a given notebook, return it
if (managers) {
let index = managers.findIndex(m => m.providerId === providerId);
if (index && index >= 0) {
return managers[index];
}
}
let newManager = await this.doWithProvider(providerId, (provider) => provider.getNotebookManager(uri));
managers = managers || [];
managers.push(newManager);
this._managersMap.set(uriString, managers);
return newManager;
}
get onNotebookEditorAdd(): Event<INotebookEditor> {
return this._onNotebookEditorAdd.event;
}
get onNotebookEditorRemove(): Event<INotebookEditor> {
return this._onNotebookEditorRemove.event;
}
get onCellChanged(): Event<INotebookEditor> {
return this._onCellChanged.event;
}
get onNotebookEditorRename(): Event<INotebookEditor> {
return this._onNotebookEditorRename.event;
}
addNotebookEditor(editor: INotebookEditor): void {
this._editors.set(editor.id, editor);
this._onNotebookEditorAdd.fire(editor);
}
removeNotebookEditor(editor: INotebookEditor): void {
if (this._editors.delete(editor.id)) {
this._onNotebookEditorRemove.fire(editor);
}
// Remove the manager from the tracked list, and let the notebook provider know that it should update its mappings
this.sendNotebookCloseToProvider(editor);
}
listNotebookEditors(): INotebookEditor[] {
let editors = [];
this._editors.forEach(e => editors.push(e));
return editors;
}
renameNotebookEditor(oldUri: URI, newUri: URI, currentEditor: INotebookEditor): void {
let oldUriKey = oldUri.toString();
if(this._editors.has(oldUriKey))
{
this._editors.delete(oldUriKey);
currentEditor.notebookParams.notebookUri = newUri;
this._editors.set(newUri.toString(), currentEditor);
this._onNotebookEditorRename.fire(currentEditor);
}
}
private sendNotebookCloseToProvider(editor: INotebookEditor): void {
let notebookUri = editor.notebookParams.notebookUri;
let uriString = notebookUri.toString();
let manager = this._managersMap.get(uriString);
if (manager) {
// As we have a manager, we can assume provider is ready
this._managersMap.delete(uriString);
manager.forEach(m => {
let provider = this._providers.get(m.providerId);
provider.instance.handleNotebookClosed(notebookUri);
});
}
}
// PRIVATE HELPERS /////////////////////////////////////////////////////
private async doWithProvider<T>(providerId: string, op: (provider: INotebookProvider) => Thenable<T>): Promise<T> {
// Make sure the provider exists before attempting to retrieve accounts
let provider: INotebookProvider = await this.getProviderInstance(providerId);
return op(provider);
}
private async getProviderInstance(providerId: string, timeout?: number): Promise<INotebookProvider> {
let providerDescriptor = this._providers.get(providerId);
let instance: INotebookProvider;
// Try get from actual provider, waiting on its registration
if (providerDescriptor) {
if (!providerDescriptor.instance) {
instance = await this.waitOnProviderAvailability(providerDescriptor);
} else {
instance = providerDescriptor.instance;
}
}
// Fall back to default if this failed
if (!instance) {
providerDescriptor = this._providers.get(DEFAULT_NOTEBOOK_PROVIDER);
instance = providerDescriptor ? providerDescriptor.instance : undefined;
}
// Should never happen, but if default wasn't registered we should throw
if (!instance) {
throw new Error(localize('notebookServiceNoProvider', 'Notebook provider does not exist'));
}
return instance;
}
private waitOnProviderAvailability(providerDescriptor: ProviderDescriptor, timeout?: number): Promise<INotebookProvider> {
// Wait up to 10 seconds for the provider to be registered
timeout = timeout || 10000;
let promises: Promise<INotebookProvider>[] = [
providerDescriptor.instanceReady,
new Promise<INotebookProvider>((resolve, reject) => setTimeout(() => resolve(), timeout))
];
return Promise.race(promises);
}
//Returns an instantiation of RenderMimeRegistry class
getMimeRegistry(): RenderMimeRegistry {
if (!this._mimeRegistry) {
return new RenderMimeRegistry({
initialFactories: standardRendererFactories
});
}
return this._mimeRegistry;
}
private get providersMemento(): NotebookProvidersMemento {
return this._memento.getMemento(this._storageService) as NotebookProvidersMemento;
}
private cleanupProviders(): void {
let knownProviders = Object.keys(notebookRegistry.registrations);
let cache = this.providersMemento.notebookProviderCache;
for (let key in cache) {
if (!knownProviders.includes(key)) {
this._providers.delete(key);
delete cache[key];
}
}
}
private registerBuiltInProvider() {
if (!sqlNotebooksEnabled()) {
let defaultProvider = new BuiltinProvider();
this.registerProvider(defaultProvider.providerId, defaultProvider);
notebookRegistry.registerNotebookProvider({
provider: defaultProvider.providerId,
fileExtensions: DEFAULT_NOTEBOOK_FILETYPE,
standardKernels: []
});
} else {
let sqlProvider = new SqlNotebookProvider(this._instantiationService);
this.registerProvider(sqlProvider.providerId, sqlProvider);
notebookRegistry.registerNotebookProvider({
provider: sqlProvider.providerId,
fileExtensions: DEFAULT_NOTEBOOK_FILETYPE,
standardKernels: ['SQL']
});
}
}
private removeContributedProvidersFromCache(identifier: IExtensionIdentifier, extensionService: IExtensionService) {
let extensionid = getIdFromLocalExtensionId(identifier.id);
extensionService.getExtensions().then(i => {
let extension = i.find(c => c.id === extensionid);
if (extension && extension.contributes['notebookProvider']) {
let id = extension.contributes['notebookProvider'].providerId;
delete this.providersMemento.notebookProviderCache[id];
}
});
}
}
export class BuiltinProvider implements INotebookProvider {
private manager: BuiltInNotebookManager;
constructor() {
this.manager = new BuiltInNotebookManager();
}
public get providerId(): string {
return DEFAULT_NOTEBOOK_PROVIDER;
}
getNotebookManager(notebookUri: URI): Thenable<INotebookManager> {
return Promise.resolve(this.manager);
}
handleNotebookClosed(notebookUri: URI): void {
// No-op
}
}
export class BuiltInNotebookManager implements INotebookManager {
private _contentManager: nb.ContentManager;
private _sessionManager: nb.SessionManager;
constructor() {
this._contentManager = new LocalContentManager();
this._sessionManager = new SessionManager();
}
public get providerId(): string {
return DEFAULT_NOTEBOOK_PROVIDER;
}
public get contentManager(): nb.ContentManager {
return this._contentManager;
}
public get serverManager(): nb.ServerManager {
return undefined;
}
public get sessionManager(): nb.SessionManager {
return this._sessionManager;
}
}
export class SqlNotebookProvider implements INotebookProvider {
private manager: SqlNotebookManager;
constructor(private _instantiationService: IInstantiationService) {
this.manager = new SqlNotebookManager(this._instantiationService);
}
public get providerId(): string {
return SQL_NOTEBOOK_PROVIDER;
}
getNotebookManager(notebookUri: URI): Thenable<INotebookManager> {
return Promise.resolve(this.manager);
}
handleNotebookClosed(notebookUri: URI): void {
// No-op
}
}
export class SqlNotebookManager implements INotebookManager {
private _contentManager: nb.ContentManager;
private _sessionManager: nb.SessionManager;
constructor(private _instantiationService: IInstantiationService) {
this._contentManager = new LocalContentManager();
this._sessionManager = new SqlSessionManager(this._instantiationService);
}
public get providerId(): string {
return SQL_NOTEBOOK_PROVIDER;
}
public get contentManager(): nb.ContentManager {
return this._contentManager;
}
public get serverManager(): nb.ServerManager {
return undefined;
}
public get sessionManager(): nb.SessionManager {
return this._sessionManager;
}
}

View File

@@ -1,216 +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 { nb } from 'sqlops';
import { localize } from 'vs/nls';
import { FutureInternal } from 'sql/parts/notebook/models/modelInterfaces';
export const noKernel: string = localize('noKernel', 'No Kernel');
const runNotebookDisabled = localize('runNotebookDisabled', 'Cannot run cells as no kernel has been configured');
let noKernelSpec: nb.IKernelSpec = ({
name: noKernel,
language: 'python',
display_name: noKernel
});
export class SessionManager implements nb.SessionManager {
public get isReady(): boolean {
return true;
}
public get ready(): Thenable<void> {
return Promise.resolve();
}
public get specs(): nb.IAllKernels {
let allKernels: nb.IAllKernels = {
defaultKernel: noKernel,
kernels: [noKernelSpec]
};
return allKernels;
}
startNew(options: nb.ISessionOptions): Thenable<nb.ISession> {
let session = new EmptySession(options);
return Promise.resolve(session);
}
shutdown(id: string): Thenable<void> {
return Promise.resolve();
}
}
export class EmptySession implements nb.ISession {
private _kernel: EmptyKernel;
private _defaultKernelLoaded = false;
public set defaultKernelLoaded(value) {
this._defaultKernelLoaded = value;
}
public get defaultKernelLoaded(): boolean {
return this._defaultKernelLoaded;
}
constructor(private options: nb.ISessionOptions) {
this._kernel = new EmptyKernel();
}
public get canChangeKernels(): boolean {
return true;
}
public get id(): string {
return this.options.kernelId || '';
}
public get path(): string {
return this.options.path;
}
public get name(): string {
return this.options.name || '';
}
public get type(): string {
return this.options.type || '';
}
public get status(): nb.KernelStatus {
return 'connected';
}
public get kernel(): nb.IKernel {
return this._kernel;
}
changeKernel(kernelInfo: nb.IKernelSpec): Thenable<nb.IKernel> {
return Promise.resolve(this.kernel);
}
}
class EmptyKernel implements nb.IKernel {
public get id(): string {
return '-1';
}
public get name(): string {
return noKernel;
}
public get supportsIntellisense(): boolean {
return false;
}
public get isReady(): boolean {
return true;
}
public get ready(): Thenable<void> {
return Promise.resolve();
}
public get info(): nb.IInfoReply {
let info: nb.IInfoReply = {
protocol_version: '',
implementation: '',
implementation_version: '',
language_info: {
name: '',
version: '',
},
banner: '',
help_links: [{
text: '',
url: ''
}]
};
return info;
}
getSpec(): Thenable<nb.IKernelSpec> {
return Promise.resolve(noKernelSpec);
}
requestExecute(content: nb.IExecuteRequest, disposeOnDone?: boolean): nb.IFuture {
return new EmptyFuture();
}
requestComplete(content: nb.ICompleteRequest): Thenable<nb.ICompleteReplyMsg> {
let response: Partial<nb.ICompleteReplyMsg> = { };
return Promise.resolve(response as nb.ICompleteReplyMsg);
}
interrupt(): Thenable<void> {
return Promise.resolve(undefined);
}
}
export class EmptyFuture implements FutureInternal {
get inProgress(): boolean {
return false;
}
get msg(): nb.IMessage {
return undefined;
}
get done(): Thenable<nb.IShellMessage> {
let msg: nb.IShellMessage = {
channel: 'shell',
type: 'shell',
content: runNotebookDisabled,
header: undefined,
metadata: undefined,
parent_header: undefined
};
return Promise.resolve(msg);
}
sendInputReply(content: nb.IInputReply): void {
// no-op
}
dispose() {
// No-op
}
setReplyHandler(handler: nb.MessageHandler<nb.IShellMessage>): void {
// no-op
}
setStdInHandler(handler: nb.MessageHandler<nb.IStdinMessage>): void {
// no-op
}
setIOPubHandler(handler: nb.MessageHandler<nb.IIOPubMessage>): void {
setTimeout(() => {
let msg: nb.IIOPubMessage = {
channel: 'iopub',
type: 'iopub',
header: <nb.IHeader> {
msg_id: '0',
msg_type: 'error'
},
content: <nb.IErrorResult> {
ename: localize('errorName', 'Error'),
evalue: runNotebookDisabled,
output_type: 'error'
},
metadata: undefined,
parent_header: undefined
};
handler.handle(msg);
}, 10);
}
registerMessageHook(hook: (msg: nb.IIOPubMessage) => boolean | Thenable<boolean>): void {
// no-op
}
removeMessageHook(hook: (msg: nb.IIOPubMessage) => boolean | Thenable<boolean>): void {
// no-op
}
}

View File

@@ -1,325 +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 { nb, QueryExecuteSubsetResult, IDbColumn, DbCellValue } from 'sqlops';
import { localize } from 'vs/nls';
import { FutureInternal } from 'sql/parts/notebook/models/modelInterfaces';
import QueryRunner, { EventType } from 'sql/parts/query/execution/queryRunner';
import { IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import Severity from 'vs/base/common/severity';
import * as Utils from 'sql/parts/connection/common/utils';
import { Deferred } from 'sql/base/common/promise';
import { Disposable } from 'vs/base/common/lifecycle';
import { mssqlProviderName } from 'sql/parts/connection/common/constants';
export const sqlKernel: string = localize('sqlKernel', 'SQL');
export const sqlKernelError: string = localize("sqlKernelError", "SQL kernel error");
let sqlKernelSpec: nb.IKernelSpec = ({
name: sqlKernel,
language: 'sql',
display_name: sqlKernel
});
export interface SQLData {
columns: Array<string>;
rows: Array<Array<string>>;
}
export class SqlSessionManager implements nb.SessionManager {
constructor(private _instantiationService: IInstantiationService) {}
public get isReady(): boolean {
return true;
}
public get ready(): Thenable<void> {
return Promise.resolve();
}
public get specs(): nb.IAllKernels {
let allKernels: nb.IAllKernels = {
defaultKernel: sqlKernel,
kernels: [sqlKernelSpec]
};
return allKernels;
}
startNew(options: nb.ISessionOptions): Thenable<nb.ISession> {
let session = new SqlSession(options, this._instantiationService);
return Promise.resolve(session);
}
shutdown(id: string): Thenable<void> {
return Promise.resolve();
}
}
export class SqlSession implements nb.ISession {
private _kernel: SqlKernel;
private _defaultKernelLoaded = false;
public set defaultKernelLoaded(value) {
this._defaultKernelLoaded = value;
}
public get defaultKernelLoaded(): boolean {
return this._defaultKernelLoaded;
}
constructor(private options: nb.ISessionOptions, private _instantiationService: IInstantiationService) {
this._kernel = this._instantiationService.createInstance(SqlKernel);
}
public get canChangeKernels(): boolean {
return true;
}
public get id(): string {
return this.options.kernelId || '';
}
public get path(): string {
return this.options.path;
}
public get name(): string {
return this.options.name || '';
}
public get type(): string {
return this.options.type || '';
}
public get status(): nb.KernelStatus {
return 'connected';
}
public get kernel(): nb.IKernel {
return this._kernel;
}
changeKernel(kernelInfo: nb.IKernelSpec): Thenable<nb.IKernel> {
return Promise.resolve(this.kernel);
}
}
class SqlKernel extends Disposable implements nb.IKernel {
private _queryRunner: QueryRunner;
private _columns: IDbColumn[];
private _rows: DbCellValue[][];
constructor(@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IErrorMessageService private _errorMessageService: IErrorMessageService) {
super();
}
public get id(): string {
return '-1';
}
public get name(): string {
return sqlKernel;
}
public get supportsIntellisense(): boolean {
return true;
}
public get isReady(): boolean {
// should we be checking on the tools service status here?
return true;
}
public get ready(): Thenable<void> {
return Promise.resolve();
}
public get info(): nb.IInfoReply {
let info: nb.IInfoReply = {
protocol_version: '',
implementation: '',
implementation_version: '',
language_info: {
name: 'sql',
version: '',
},
banner: '',
help_links: [{
text: '',
url: ''
}]
};
return info;
}
getSpec(): Thenable<nb.IKernelSpec> {
return Promise.resolve(sqlKernelSpec);
}
requestExecute(content: nb.IExecuteRequest, disposeOnDone?: boolean): nb.IFuture {
if (this._queryRunner) {
this._queryRunner.runQuery(content.code);
} else {
let connections = this._connectionManagementService.getActiveConnections();
let connectionProfile = connections.find(connection => connection.providerName === mssqlProviderName);
let connectionUri = Utils.generateUri(connectionProfile, 'notebook');
this._queryRunner = this._instantiationService.createInstance(QueryRunner, connectionUri, undefined);
this._connectionManagementService.connect(connectionProfile, connectionUri).then((result) =>
{
this.addQueryEventListeners(this._queryRunner);
this._queryRunner.runQuery(content.code);
});
}
return new SQLFuture(this._queryRunner);
}
requestComplete(content: nb.ICompleteRequest): Thenable<nb.ICompleteReplyMsg> {
let response: Partial<nb.ICompleteReplyMsg> = { };
return Promise.resolve(response as nb.ICompleteReplyMsg);
}
interrupt(): Thenable<void> {
return Promise.resolve(undefined);
}
private addQueryEventListeners(queryRunner: QueryRunner): void {
this._register(queryRunner.addListener(EventType.COMPLETE, () => {
this.queryComplete().catch(error => {
this._errorMessageService.showDialog(Severity.Error, sqlKernelError, error);
});
}));
this._register(queryRunner.addListener(EventType.MESSAGE, message => {
if (message.isError) {
this._errorMessageService.showDialog(Severity.Error, sqlKernelError, message.message);
}
}));
}
private async queryComplete(): Promise<void> {
let batches = this._queryRunner.batchSets;
// currently only support 1 batch set 1 resultset
if (batches.length > 0) {
let batch = batches[0];
if (batch.resultSetSummaries.length > 0
&& batch.resultSetSummaries[0].rowCount > 0
) {
let resultset = batch.resultSetSummaries[0];
this._columns = resultset.columnInfo;
let rows: QueryExecuteSubsetResult;
try {
rows = await this._queryRunner.getQueryRows(0, resultset.rowCount, batch.id, resultset.id);
} catch (e) {
return Promise.reject(e);
}
this._rows = rows.resultSubset.rows;
}
}
// TODO issue #2746 should ideally show a warning inside the dialog if have no data
}
}
export class SQLFuture extends Disposable implements FutureInternal {
private _msg: nb.IMessage = undefined;
constructor(private _queryRunner: QueryRunner) {
super();
}
get inProgress(): boolean {
return !this._queryRunner.hasCompleted;
}
get msg(): nb.IMessage {
return this._msg;
}
get done(): Thenable<nb.IShellMessage> {
let deferred = new Deferred<nb.IShellMessage> ();
try {
this._register(this._queryRunner.onBatchEnd(e => {
let msg: nb.IShellMessage = {
channel: 'shell',
type: 'execute_reply',
content: { status: 'ok' },
header: undefined,
metadata: {},
parent_header: undefined
};
this._msg = msg;
deferred.resolve(msg);
}));
} catch {
return Promise.resolve(undefined);
}
return deferred.promise;
}
sendInputReply(content: nb.IInputReply): void {
// no-op
}
setReplyHandler(handler: nb.MessageHandler<nb.IShellMessage>): void {
// no-op
}
setStdInHandler(handler: nb.MessageHandler<nb.IStdinMessage>): void {
// no-op
}
setIOPubHandler(handler: nb.MessageHandler<nb.IIOPubMessage>): void {
this._register(this._queryRunner.onBatchEnd(batch => {
this._queryRunner.getQueryRows(0, batch.resultSetSummaries[0].rowCount, 0, 0).then(d => {
let data:SQLData = {
columns: batch.resultSetSummaries[0].columnInfo.map(c => c.columnName),
rows: d.resultSubset.rows.map(r => r.map(c => c.displayValue))
};
let table: HTMLTableElement = document.createElement('table');
table.createTHead();
table.createTBody();
let hrow = <HTMLTableRowElement>table.insertRow();
// headers
for (let column of data.columns) {
var cell = hrow.insertCell();
cell.innerHTML = column;
}
for (let row in data.rows) {
let hrow = <HTMLTableRowElement>table.insertRow();
for (let column in data.columns) {
var cell = hrow.insertCell();
cell.innerHTML = data.rows[row][column];
}
}
let tableHtml = '<table>' + table.innerHTML + '</table>';
let msg: nb.IIOPubMessage = {
channel: 'iopub',
type: 'iopub',
header: <nb.IHeader> {
msg_id: undefined,
msg_type: 'execute_result'
},
content: <nb.IExecuteResult> {
output_type: 'execute_result',
metadata: {},
execution_count: 0,
data: { 'text/html' : tableHtml},
},
metadata: undefined,
parent_header: undefined
};
handler.handle(msg);
});
}));
}
registerMessageHook(hook: (msg: nb.IIOPubMessage) => boolean | Thenable<boolean>): void {
// no-op
}
removeMessageHook(hook: (msg: nb.IIOPubMessage) => boolean | Thenable<boolean>): void {
// no-op
}
}

View File

@@ -1,115 +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 { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ScriptOperation } from 'sql/workbench/common/taskUtilities';
import * as sqlops from 'sqlops';
import { error } from 'sql/base/common/log';
export const SERVICE_ID = 'scriptingService';
export const IScriptingService = createDecorator<IScriptingService>(SERVICE_ID);
export interface IScriptingService {
_serviceBrand: any;
script(connectionUri: string, metadata: sqlops.ObjectMetadata, operation: ScriptOperation, paramDetails: sqlops.ScriptingParamDetails): Thenable<sqlops.ScriptingResult>;
/**
* Register a scripting provider
*/
registerProvider(providerId: string, provider: sqlops.ScriptingProvider): void;
/**
* Specifies whether a provider with a given ID has been registered or not
*/
isProviderRegistered(providerId: string): boolean;
/**
* Callback method for when scripting is complete
*/
onScriptingComplete(handle: number, scriptingCompleteResult: sqlops.ScriptingCompleteResult): void;
/**
* Returns the result for an operation if the operation failed
*/
getOperationFailedResult(operationId: string): sqlops.ScriptingCompleteResult;
}
export class ScriptingService implements IScriptingService {
public _serviceBrand: any;
private disposables: IDisposable[] = [];
private _providers: { [handle: string]: sqlops.ScriptingProvider; } = Object.create(null);
private failedScriptingOperations: { [operationId: string]: sqlops.ScriptingCompleteResult } = {};
constructor( @IConnectionManagementService private _connectionService: IConnectionManagementService) { }
/**
* Call the service for scripting based on provider and scripting operation
* @param connectionUri
* @param metadata
* @param operation
* @param paramDetails
*/
public script(connectionUri: string, metadata: sqlops.ObjectMetadata, operation: ScriptOperation, paramDetails: sqlops.ScriptingParamDetails): Thenable<sqlops.ScriptingResult> {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
let provider = this._providers[providerId];
if (provider) {
return provider.scriptAsOperation(connectionUri, operation, metadata, paramDetails);
}
}
return Promise.resolve(undefined);
}
/**
* Callback method for when scripting is complete
* @param handle
* @param scriptingCompleteResult
*/
public onScriptingComplete(handle: number, scriptingCompleteResult: sqlops.ScriptingCompleteResult): void {
if (scriptingCompleteResult && scriptingCompleteResult.hasError && scriptingCompleteResult.errorMessage) {
error(`Scripting failed. error: ${scriptingCompleteResult.errorMessage}`);
if (scriptingCompleteResult.operationId) {
this.failedScriptingOperations[scriptingCompleteResult.operationId] = scriptingCompleteResult;
}
}
}
/**
* Returns the result for an operation if the operation failed
* @param operationId Operation Id
*/
public getOperationFailedResult(operationId: string): sqlops.ScriptingCompleteResult {
if (operationId && operationId in this.failedScriptingOperations) {
return this.failedScriptingOperations[operationId];
} else {
return undefined;
}
}
/**
* Register a scripting provider
*/
public registerProvider(providerId: string, provider: sqlops.ScriptingProvider): void {
this._providers[providerId] = provider;
}
public isProviderRegistered(providerId: string): boolean {
let provider = this._providers[providerId];
return !!provider;
}
public dispose(): void {
this.disposables = dispose(this.disposables);
}
}

View File

@@ -1,89 +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 { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import * as Constants from 'sql/common/constants';
import * as sqlops from 'sqlops';
export const SERVICE_ID = 'serializationService';
export interface SerializationProviderEvents {
onSaveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable<sqlops.SaveResultRequestResult>;
}
export const ISerializationService = createDecorator<ISerializationService>(SERVICE_ID);
export interface ISerializationService {
_serviceBrand: any;
saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable<sqlops.SaveResultRequestResult>;
disabledSaveAs(): Thenable<sqlops.SaveResultRequestResult>;
addEventListener(handle: number, events: SerializationProviderEvents): IDisposable;
getSerializationFeatureMetadataProvider(ownerUri: string): sqlops.FeatureMetadataProvider;
}
export class SerializationService implements ISerializationService {
_serviceBrand: any;
private disposables: IDisposable[] = [];
private _serverEvents: { [handle: number]: SerializationProviderEvents; } = Object.create(null);
private _lastHandle: number;
constructor(
@IConnectionManagementService private _connectionService: IConnectionManagementService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService
) {
}
public addEventListener(handle: number, events: SerializationProviderEvents): IDisposable {
this._lastHandle = handle;
this._serverEvents[handle] = events;
return {
dispose: () => {
}
};
}
public saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable<sqlops.SaveResultRequestResult> {
if (this._serverEvents === undefined || this._serverEvents[this._lastHandle] === undefined) {
return this.disabledSaveAs();
}
return this._serverEvents[this._lastHandle].onSaveAs(saveFormat, savePath, results, appendToFile);
}
public disabledSaveAs(): Thenable<sqlops.SaveResultRequestResult> {
return Promise.resolve({ messages: Constants.SerializationDisabled });
}
public getSerializationFeatureMetadataProvider(ownerUri: string): sqlops.FeatureMetadataProvider {
let providerId: string = this._connectionService.getProviderIdFromUri(ownerUri);
let providerCapabilities = this._capabilitiesService.getLegacyCapabilities(providerId);
if (providerCapabilities) {
return providerCapabilities.features.find(f => f.featureName === SERVICE_ID);
}
return undefined;
}
public dispose(): void {
this.disposables = dispose(this.disposables);
}
}