mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-22 09:35:37 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
369
src/sql/services/accountManagement/accountManagementService.ts
Normal file
369
src/sql/services/accountManagement/accountManagementService.ts
Normal file
@@ -0,0 +1,369 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 data from 'data';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import AccountStore from 'sql/services/accountManagement/accountStore';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
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 { ISqlOAuthService } from 'sql/common/sqlOAuthService';
|
||||
import { AccountDialogController } from 'sql/parts/accountManagement/accountDialog/accountDialogController';
|
||||
import { AccountListStatusbarItem } from 'sql/parts/accountManagement/accountListStatusbar/accountListStatusbarItem';
|
||||
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes';
|
||||
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
|
||||
import { warn } from 'sql/base/common/log';
|
||||
|
||||
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 _mementoContext: Memento;
|
||||
private _oAuthCallbacks: { [eventId: string]: { resolve, reject } } = {};
|
||||
private _oAuthEventId: number = 0;
|
||||
|
||||
// EVENT EMITTERS //////////////////////////////////////////////////////
|
||||
private _addAccountProviderEmitter: Emitter<AccountProviderAddedEventParams>;
|
||||
public get addAccountProviderEvent(): Event<AccountProviderAddedEventParams> { return this._addAccountProviderEmitter.event; }
|
||||
|
||||
private _removeAccountProviderEmitter: Emitter<data.AccountProviderMetadata>;
|
||||
public get removeAccountProviderEvent(): Event<data.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,
|
||||
@ISqlOAuthService private _oAuthService: ISqlOAuthService
|
||||
) {
|
||||
let self = this;
|
||||
|
||||
// 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<data.AccountProviderMetadata>();
|
||||
this._updateAccountListEmitter = new Emitter<UpdateAccountListEventParams>();
|
||||
|
||||
// Register status bar item
|
||||
// FEATURE FLAG TOGGLE
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
let statusbarDescriptor = new statusbar.StatusbarItemDescriptor(
|
||||
AccountListStatusbarItem,
|
||||
statusbar.StatusbarAlignment.LEFT,
|
||||
15000 /* Highest Priority */
|
||||
);
|
||||
(<statusbar.IStatusbarRegistry>platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(statusbarDescriptor);
|
||||
}
|
||||
|
||||
// Register event handler for OAuth completion
|
||||
this._oAuthService.registerOAuthCallback((event, args) => {
|
||||
self.onOAuthResponse(args);
|
||||
});
|
||||
}
|
||||
|
||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||
/**
|
||||
* 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<data.Account> {
|
||||
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) {
|
||||
// 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<data.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<data.Account[]> {
|
||||
let self = this;
|
||||
|
||||
// Make sure the provider exists before attempting to retrieve accounts
|
||||
if (!this._providers[providerId]) {
|
||||
return Promise.reject(new Error(nls.localize('accountManagementNoProvider', 'Account provider does not exist'))).then();
|
||||
}
|
||||
|
||||
// 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
|
||||
* @return {Thenable<{}>} Promise to return the security token
|
||||
*/
|
||||
public getSecurityToken(account: data.Account): Thenable<{}> {
|
||||
return this.doWithProvider(account.key.providerId, provider => {
|
||||
return provider.provider.getSecurityToken(account);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: data.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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a browser window to perform the OAuth authentication
|
||||
* @param {string} url URL to visit that will perform the OAuth authentication
|
||||
* @param {boolean} silent Whether or not to perform authentication silently using browser's cookies
|
||||
* @return {Thenable<string>} Promise to return a authentication token on successful authentication
|
||||
*/
|
||||
public performOAuthAuthorization(url: string, silent: boolean): Thenable<string> {
|
||||
let self = this;
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
// TODO: replace with uniqid
|
||||
let eventId: string = `oauthEvent${self._oAuthEventId++}`;
|
||||
self._oAuthCallbacks[eventId] = {
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
|
||||
self._oAuthService.performOAuthAuthorization(eventId, url, silent);
|
||||
});
|
||||
}
|
||||
|
||||
// SERVICE MANAGEMENT METHODS //////////////////////////////////////////
|
||||
/**
|
||||
* Called by main thread to register an account provider from extension
|
||||
* @param {data.AccountProviderMetadata} providerMetadata Metadata of the provider that is being registered
|
||||
* @param {data.AccountProvider} provider References to the methods of the provider
|
||||
*/
|
||||
public registerProvider(providerMetadata: data.AccountProviderMetadata, provider: data.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: data.Account[]) => {
|
||||
return provider.initialize(accounts);
|
||||
})
|
||||
.then((accounts: data.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: data.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> {
|
||||
// Make sure the provider exists before attempting to retrieve accounts
|
||||
let provider = this._providers[providerId];
|
||||
if (!provider) {
|
||||
return Promise.reject(new Error(nls.localize('accountManagementNoProvider', 'Account provider does not exist'))).then();
|
||||
}
|
||||
|
||||
return op(provider);
|
||||
}
|
||||
|
||||
private fireAccountListUpdate(provider: AccountProviderWithMetadata, sort: boolean) {
|
||||
// Step 1) Get and sort the list
|
||||
if (sort) {
|
||||
provider.accounts.sort((a: data.Account, b: data.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 onOAuthResponse(args: object): void {
|
||||
// Verify the arguments are correct
|
||||
if (!args || args['eventId'] === undefined) {
|
||||
warn('Received invalid OAuth event response args');
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the event
|
||||
let eventId: string = args['eventId'];
|
||||
let eventCallbacks = this._oAuthCallbacks[eventId];
|
||||
if (!eventCallbacks) {
|
||||
warn('Received OAuth event response for non-existent eventId');
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the args
|
||||
let error: string = args['error'];
|
||||
let code: string = args['code'];
|
||||
if (error) {
|
||||
eventCallbacks.reject(error);
|
||||
} else {
|
||||
eventCallbacks.resolve(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins together an account provider, its metadata, and its accounts, used in the provider list
|
||||
*/
|
||||
export interface AccountProviderWithMetadata {
|
||||
metadata: data.AccountProviderMetadata;
|
||||
provider: data.AccountProvider;
|
||||
accounts: data.Account[];
|
||||
}
|
||||
204
src/sql/services/accountManagement/accountStore.ts
Normal file
204
src/sql/services/accountManagement/accountStore.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 data from 'data';
|
||||
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: data.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<data.Account[]> {
|
||||
let self = this;
|
||||
|
||||
return this.doOperation(() => {
|
||||
return self.readFromMemento()
|
||||
.then(accounts => accounts.filter(account => account.key.providerId === providerId));
|
||||
});
|
||||
}
|
||||
|
||||
public getAllAccounts(): Thenable<data.Account[]> {
|
||||
let self = this;
|
||||
|
||||
return this.doOperation(() => {
|
||||
return self.readFromMemento();
|
||||
});
|
||||
}
|
||||
|
||||
public remove(key: data.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: data.AccountKey, updateOperation: (account: data.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: data.AccountKey, key2: data.AccountKey): boolean {
|
||||
// Provider ID and Account ID must match
|
||||
return key1.providerId === key2.providerId && key1.accountId === key2.accountId;
|
||||
}
|
||||
|
||||
private static mergeAccounts(source: data.Account, target: data.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: data.Account[], accountToAdd: data.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: data.Account[], accountToRemove: data.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: data.Account[], accountToUpdate: data.AccountKey, updateOperation: (account: data.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<data.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: data.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: data.Account[];
|
||||
}
|
||||
58
src/sql/services/accountManagement/eventTypes.ts
Normal file
58
src/sql/services/accountManagement/eventTypes.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 data from 'data';
|
||||
|
||||
/**
|
||||
* 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: data.Account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters that go along with an account provider being added
|
||||
*/
|
||||
export interface AccountProviderAddedEventParams {
|
||||
/**
|
||||
* The provider that was registered
|
||||
*/
|
||||
addedProvider: data.AccountProviderMetadata;
|
||||
|
||||
/**
|
||||
* The accounts that were rehydrated with the provider
|
||||
*/
|
||||
initialAccounts: data.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: data.Account[];
|
||||
}
|
||||
81
src/sql/services/accountManagement/interfaces.ts
Normal file
81
src/sql/services/accountManagement/interfaces.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 data from 'data';
|
||||
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 //////////////////////////////////////////
|
||||
addAccount(providerId: string): Thenable<data.Account>;
|
||||
getAccountProviderMetadata(): Thenable<data.AccountProviderMetadata[]>;
|
||||
getAccountsForProvider(providerId: string): Thenable<data.Account[]>;
|
||||
getSecurityToken(account: data.Account): Thenable<{}>;
|
||||
removeAccount(accountKey: data.AccountKey): Thenable<boolean>;
|
||||
|
||||
// UI METHODS //////////////////////////////////////////////////////////
|
||||
openAccountListDialog(): Thenable<void>;
|
||||
performOAuthAuthorization(url: string, silent: boolean): Thenable<string>;
|
||||
|
||||
// SERVICE MANAGEMENT METHODS /////////////////////////////////////////
|
||||
registerProvider(providerMetadata: data.AccountProviderMetadata, provider: data.AccountProvider): void;
|
||||
shutdown(): void;
|
||||
unregisterProvider(providerMetadata: data.AccountProviderMetadata): void;
|
||||
|
||||
// EVENTING ////////////////////////////////////////////////////////////
|
||||
readonly addAccountProviderEvent: Event<AccountProviderAddedEventParams>;
|
||||
readonly removeAccountProviderEvent: Event<data.AccountProviderMetadata>;
|
||||
readonly updateAccountListEvent: Event<UpdateAccountListEventParams>;
|
||||
}
|
||||
|
||||
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: data.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<data.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<data.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: data.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: data.AccountKey, updateOperation: (account: data.Account) => void): Thenable<boolean>;
|
||||
}
|
||||
60
src/sql/services/angularEventing/angularEventingService.ts
Normal file
60
src/sql/services/angularEventing/angularEventingService.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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
|
||||
}
|
||||
|
||||
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: AngularEventType) => 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): void;
|
||||
}
|
||||
|
||||
export class AngularEventingService implements IAngularEventingService {
|
||||
public _serviceBrand: any;
|
||||
private _angularMap = new Map<string, Subject<AngularEventType>>();
|
||||
|
||||
public onAngularEvent(uri: string, cb: (event: AngularEventType) => void): Subscription {
|
||||
let subject: Subject<AngularEventType>;
|
||||
if (!this._angularMap.has(uri)) {
|
||||
subject = new Subject<AngularEventType>();
|
||||
this._angularMap.set(uri, subject);
|
||||
} else {
|
||||
subject = this._angularMap.get(uri);
|
||||
}
|
||||
let sub = subject.subscribe(cb);
|
||||
return sub;
|
||||
}
|
||||
|
||||
public sendAngularEvent(uri: string, event: AngularEventType): 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/sql/services/bootstrap/bootstrapParams.ts
Normal file
31
src/sql/services/bootstrap/bootstrapParams.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DataService } from 'sql/parts/grid/services/dataService';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
|
||||
export interface BootstrapParams {
|
||||
}
|
||||
|
||||
export interface QueryComponentParams extends BootstrapParams {
|
||||
dataService: DataService;
|
||||
}
|
||||
|
||||
export interface EditDataComponentParams extends BootstrapParams {
|
||||
dataService: DataService;
|
||||
}
|
||||
|
||||
export interface DashboardComponentParams extends BootstrapParams {
|
||||
connection: IConnectionProfile;
|
||||
ownerUri: string;
|
||||
}
|
||||
|
||||
export interface TaskDialogComponentParams extends BootstrapParams {
|
||||
ownerUri: string;
|
||||
}
|
||||
|
||||
export interface QueryPlanParams extends BootstrapParams {
|
||||
planXml: string;
|
||||
}
|
||||
114
src/sql/services/bootstrap/bootstrapService.ts
Normal file
114
src/sql/services/bootstrap/bootstrapService.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { NgModuleRef } from '@angular/core';
|
||||
import { BootstrapParams } from 'sql/services/bootstrap/bootstrapParams';
|
||||
import { IConnectionManagementService, IConnectionDialogService, IErrorMessageService }
|
||||
from 'sql/parts/connection/common/connectionManagement';
|
||||
import { IMetadataService } from 'sql/services/metadata/metadataService';
|
||||
import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService';
|
||||
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
||||
import { IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
|
||||
import { IScriptingService } from 'sql/services/scripting/scriptingService';
|
||||
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
|
||||
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
|
||||
import { IAdminService } from 'sql/parts/admin/common/adminService';
|
||||
import { IDisasterRecoveryService, IDisasterRecoveryUiService, IRestoreDialogController } from 'sql/parts/disasterRecovery/common/interfaces';
|
||||
import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces';
|
||||
import { ISqlOAuthService } from 'sql/common/sqlOAuthService';
|
||||
import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
|
||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
||||
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
|
||||
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export const BOOTSTRAP_SERVICE_ID = 'bootstrapService';
|
||||
export const IBootstrapService = createDecorator<IBootstrapService>(BOOTSTRAP_SERVICE_ID);
|
||||
|
||||
/*
|
||||
* Handles logic for bootstrapping and passing singleton services to Angular components.
|
||||
*/
|
||||
export interface IBootstrapService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
connectionManagementService: IConnectionManagementService;
|
||||
metadataService: IMetadataService;
|
||||
objectExplorerService: IObjectExplorerService;
|
||||
scriptingService: IScriptingService;
|
||||
queryEditorService: IQueryEditorService;
|
||||
connectionDialogService: IConnectionDialogService;
|
||||
queryModelService: IQueryModelService;
|
||||
adminService: IAdminService;
|
||||
disasterRecoveryService: IDisasterRecoveryService;
|
||||
disasterRecoveryUiService: IDisasterRecoveryUiService;
|
||||
keybindingService: IKeybindingService;
|
||||
contextKeyService: IContextKeyService;
|
||||
contextMenuService: IContextMenuService;
|
||||
themeService: IWorkbenchThemeService;
|
||||
editorService: IWorkbenchEditorService;
|
||||
errorMessageService: IErrorMessageService;
|
||||
partService: IPartService;
|
||||
queryManagementService: IQueryManagementService;
|
||||
instantiationService: IInstantiationService;
|
||||
angularEventingService: IAngularEventingService;
|
||||
configurationService: IConfigurationService;
|
||||
insightsDialogService: IInsightsDialogService;
|
||||
contextViewService: IContextViewService;
|
||||
restoreDialogService: IRestoreDialogController;
|
||||
messageService: IMessageService;
|
||||
workspaceContextService: IWorkspaceContextService;
|
||||
accountManagementService: IAccountManagementService;
|
||||
windowsService: IWindowsService;
|
||||
sqlOAuthService: ISqlOAuthService;
|
||||
windowService: IWindowService;
|
||||
fileBrowserService: IFileBrowserService;
|
||||
fileBrowserDialogService: IFileBrowserDialogController;
|
||||
telemetryService: ITelemetryService;
|
||||
storageService: IStorageService;
|
||||
clipboardService: IClipboardService;
|
||||
capabilitiesService: ICapabilitiesService;
|
||||
|
||||
/*
|
||||
* Bootstraps the Angular module described. Components that need singleton services should inject the
|
||||
* 'BootstrapService' dependency to obtain a reference to this class. Components that need dynamic parameters
|
||||
* should wrap them in an object and pass them in through the "params" parameter.
|
||||
*
|
||||
* moduleType: The TypeScript type of the module to bootstrap
|
||||
* container: The HTML container to append the selector HTMLElement
|
||||
* selectorString: The tag name and class used to create the element, e.g. 'tagName.cssClassName'
|
||||
* params: The parameters to be associated with the given id
|
||||
* input: Optional editor input. If specified, will listen to its onDispose event and destroy the module when this happens
|
||||
* callbackSetModule:Optional. If specified, will be used to set the moduleRef
|
||||
* Returns the unique selector string that this module will bootstrap with.
|
||||
*/
|
||||
bootstrap(moduleType: any, container: HTMLElement, selectorString: string, params: BootstrapParams, input?: IEditorInput, callbackSetModule?: (value: NgModuleRef<{}>) => void): string;
|
||||
|
||||
/*
|
||||
* Gets the "params" entry associated with the given id and unassociates the id/entry pair.
|
||||
* Returns undefined if no entry is found.
|
||||
*/
|
||||
getBootstrapParams(id: string): any;
|
||||
|
||||
/*
|
||||
* Gets the next unique selector given the baseSelectorString. A unique selector is the baseSelectorString with a
|
||||
* number appended. E.g. if baseSelectorString='query', valid unique selectors could be query0, query1, query2, etc.
|
||||
*/
|
||||
getUniqueSelector(baseSelectorString: string): string;
|
||||
}
|
||||
174
src/sql/services/bootstrap/bootstrapServiceImpl.ts
Normal file
174
src/sql/services/bootstrap/bootstrapServiceImpl.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { NgModuleRef } from '@angular/core';
|
||||
import { platformBrowserDynamic, } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { BootstrapParams } from 'sql/services/bootstrap/bootstrapParams';
|
||||
import { IConnectionManagementService, IConnectionDialogService, IErrorMessageService }
|
||||
from 'sql/parts/connection/common/connectionManagement';
|
||||
import { IMetadataService } from 'sql/services/metadata/metadataService';
|
||||
import { IObjectExplorerService } from 'sql/parts/registeredServer/common/objectExplorerService';
|
||||
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
||||
import { IScriptingService } from 'sql/services/scripting/scriptingService';
|
||||
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
|
||||
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
|
||||
import { IAdminService } from 'sql/parts/admin/common/adminService';
|
||||
import { IDisasterRecoveryService, IDisasterRecoveryUiService, IRestoreDialogController } from 'sql/parts/disasterRecovery/common/interfaces';
|
||||
import { IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
|
||||
import { IInsightsDialogService } from 'sql/parts/insights/common/interfaces';
|
||||
import { ISqlOAuthService } from 'sql/common/sqlOAuthService';
|
||||
import { IFileBrowserService, IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
|
||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
||||
|
||||
import { $ } from 'vs/base/browser/dom';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from './bootstrapService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IMessageService } from 'vs/platform/message/common/message';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
|
||||
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export class BootstrapService implements IBootstrapService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
// Maps uniqueSelectors (as opposed to selectors) to BootstrapParams
|
||||
private _bootstrapParameterMap: Map<string, BootstrapParams>;
|
||||
|
||||
// Maps selectors (as opposed to uniqueSelectors) to a queue of uniqueSelectors
|
||||
private _selectorQueueMap: Map<string, string[]>;
|
||||
|
||||
// Maps selectors (as opposed to uniqueSelectors) to the next available uniqueSelector ID number
|
||||
private _selectorCountMap: Map<string, number>;
|
||||
|
||||
constructor(
|
||||
@IConnectionManagementService public connectionManagementService: IConnectionManagementService,
|
||||
@IMetadataService public metadataService: IMetadataService,
|
||||
@IObjectExplorerService public objectExplorerService: IObjectExplorerService,
|
||||
@IScriptingService public scriptingService: IScriptingService,
|
||||
@IQueryEditorService public queryEditorService: IQueryEditorService,
|
||||
@IAdminService public adminService: IAdminService,
|
||||
@IWorkbenchThemeService public themeService: IWorkbenchThemeService,
|
||||
@IWorkbenchEditorService public editorService: IWorkbenchEditorService,
|
||||
@IDisasterRecoveryService public disasterRecoveryService: IDisasterRecoveryService,
|
||||
@IDisasterRecoveryUiService public disasterRecoveryUiService: IDisasterRecoveryUiService,
|
||||
@IRestoreDialogController public restoreDialogService: IRestoreDialogController,
|
||||
@IConnectionDialogService public connectionDialogService: IConnectionDialogService,
|
||||
@IQueryModelService public queryModelService: IQueryModelService,
|
||||
@IKeybindingService public keybindingService: IKeybindingService,
|
||||
@IContextKeyService public contextKeyService: IContextKeyService,
|
||||
@IContextMenuService public contextMenuService: IContextMenuService,
|
||||
@IErrorMessageService public errorMessageService: IErrorMessageService,
|
||||
@IPartService public partService: IPartService,
|
||||
@IQueryManagementService public queryManagementService: IQueryManagementService,
|
||||
@IInstantiationService public instantiationService: IInstantiationService,
|
||||
@IAngularEventingService public angularEventingService: IAngularEventingService,
|
||||
@IConfigurationService public configurationService: IConfigurationService,
|
||||
@IInsightsDialogService public insightsDialogService: IInsightsDialogService,
|
||||
@IContextViewService public contextViewService: IContextViewService,
|
||||
@IMessageService public messageService: IMessageService,
|
||||
@IWorkspaceContextService public workspaceContextService: IWorkspaceContextService,
|
||||
@IAccountManagementService public accountManagementService: IAccountManagementService,
|
||||
@IWindowsService public windowsService: IWindowsService,
|
||||
@ISqlOAuthService public sqlOAuthService: ISqlOAuthService,
|
||||
@IFileBrowserService public fileBrowserService: IFileBrowserService,
|
||||
@IFileBrowserDialogController public fileBrowserDialogService: IFileBrowserDialogController,
|
||||
@IWindowService public windowService: IWindowService,
|
||||
@ITelemetryService public telemetryService: ITelemetryService,
|
||||
@IStorageService public storageService: IStorageService,
|
||||
@IClipboardService public clipboardService: IClipboardService,
|
||||
@ICapabilitiesService public capabilitiesService: ICapabilitiesService
|
||||
) {
|
||||
this._bootstrapParameterMap = new Map<string, BootstrapParams>();
|
||||
this._selectorQueueMap = new Map<string, string[]>();
|
||||
this._selectorCountMap = new Map<string, number>();
|
||||
}
|
||||
|
||||
public bootstrap(moduleType: any, container: HTMLElement, selectorString: string, params: BootstrapParams, input?: IEditorInput, callbackSetModule?: (value: NgModuleRef<{}>) => void): string {
|
||||
// Create the uniqueSelectorString
|
||||
let uniqueSelectorString: string = this._getUniqueSelectorString(selectorString);
|
||||
let selector: HTMLElement = $(uniqueSelectorString);
|
||||
container.appendChild(selector);
|
||||
|
||||
// Associate the elementId
|
||||
this._setUniqueSelector(selectorString, uniqueSelectorString);
|
||||
|
||||
// Associate the params
|
||||
this._bootstrapParameterMap.set(uniqueSelectorString, params);
|
||||
|
||||
// Perform the bootsrap
|
||||
let providers = [{ provide: BOOTSTRAP_SERVICE_ID, useValue: this }];
|
||||
|
||||
platformBrowserDynamic(providers).bootstrapModule(moduleType).then(moduleRef => {
|
||||
if (input) {
|
||||
input.onDispose(() => {
|
||||
moduleRef.destroy();
|
||||
});
|
||||
}
|
||||
if (callbackSetModule) {
|
||||
callbackSetModule(moduleRef);
|
||||
}
|
||||
});
|
||||
|
||||
return uniqueSelectorString;
|
||||
}
|
||||
|
||||
public getBootstrapParams(id: string): any {
|
||||
let idLowercase = id.toLowerCase();
|
||||
let params: BootstrapParams = this._bootstrapParameterMap.get(idLowercase);
|
||||
this._bootstrapParameterMap.delete(idLowercase);
|
||||
return params;
|
||||
}
|
||||
|
||||
public getUniqueSelector(selectorString: string): string {
|
||||
let idArray = this._selectorQueueMap.get(selectorString);
|
||||
if (!idArray) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let id: string = idArray.shift();
|
||||
|
||||
if (idArray.length === 0) {
|
||||
this._selectorQueueMap.delete(selectorString);
|
||||
} else {
|
||||
this._selectorQueueMap.set(selectorString, idArray);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private _getUniqueSelectorString(selectorString: string): string {
|
||||
let count: number = this._selectorCountMap.get(selectorString);
|
||||
if (!count) {
|
||||
this._selectorCountMap.set(selectorString, 1);
|
||||
count = 0;
|
||||
} else {
|
||||
this._selectorCountMap.set(selectorString, count + 1);
|
||||
}
|
||||
let casedString = selectorString + count.toString();
|
||||
return casedString.toLowerCase();
|
||||
}
|
||||
|
||||
private _setUniqueSelector(selectorString: string, elementId: string) {
|
||||
let idArray = this._selectorQueueMap.get(selectorString);
|
||||
if (!idArray) {
|
||||
idArray = [];
|
||||
}
|
||||
idArray.push(elementId);
|
||||
this._selectorQueueMap.set(selectorString, idArray);
|
||||
}
|
||||
}
|
||||
166
src/sql/services/capabilities/capabilitiesService.ts
Normal file
166
src/sql/services/capabilities/capabilitiesService.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import data = require('data');
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
|
||||
export const SERVICE_ID = 'capabilitiesService';
|
||||
export const HOST_NAME = 'sqlops';
|
||||
export const HOST_VERSION = '1.0';
|
||||
|
||||
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(): data.DataProtocolServerCapabilities[];
|
||||
|
||||
/**
|
||||
* Register a capabilities provider
|
||||
*/
|
||||
registerProvider(provider: data.CapabilitiesProvider): void;
|
||||
|
||||
/**
|
||||
* Returns true if the feature is available for given connection
|
||||
*/
|
||||
isFeatureAvailable(action: Action, connectionManagementInfo: ConnectionManagementInfo): boolean;
|
||||
|
||||
/**
|
||||
* Event raised when a provider is registered
|
||||
*/
|
||||
onProviderRegisteredEvent: Event<data.DataProtocolServerCapabilities>;
|
||||
|
||||
/**
|
||||
* Promise fulfilled when Capabilities are ready
|
||||
*/
|
||||
onCapabilitiesReady(): Promise<void>;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Capabilities service implementation class. This class provides the ability
|
||||
* to discover the DMP capabilties that a DMP provider offers.
|
||||
*/
|
||||
export class CapabilitiesService implements ICapabilitiesService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
private _providers: data.CapabilitiesProvider[] = [];
|
||||
|
||||
private _capabilities: data.DataProtocolServerCapabilities[] = [];
|
||||
|
||||
private _onProviderRegistered: Emitter<data.DataProtocolServerCapabilities>;
|
||||
|
||||
private _clientCapabilties: data.DataProtocolClientCapabilities = {
|
||||
|
||||
hostName: HOST_NAME,
|
||||
hostVersion: HOST_VERSION
|
||||
};
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
private _onCapabilitiesReady: Deferred<void>;
|
||||
|
||||
// Due to absence of a way to infer the number of expected data tools extensions from the package.json, this is being hard-coded
|
||||
// TODO: a better mechanism to populate the expected number of capabilities
|
||||
private _expectedCapabilitiesCount: number = 1;
|
||||
|
||||
private _registeredCapabilities: number = 0;
|
||||
|
||||
constructor() {
|
||||
this._onProviderRegistered = new Emitter<data.DataProtocolServerCapabilities>();
|
||||
this.disposables.push(this._onProviderRegistered);
|
||||
this._onCapabilitiesReady = new Deferred();
|
||||
}
|
||||
|
||||
public onCapabilitiesReady(): Promise<void> {
|
||||
return this._onCapabilitiesReady.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of registered server capabilities
|
||||
*/
|
||||
public getCapabilities(): data.DataProtocolServerCapabilities[] {
|
||||
return this._capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the capabilities provider and query the provider for its capabilities
|
||||
* @param provider
|
||||
*/
|
||||
public registerProvider(provider: data.CapabilitiesProvider): void {
|
||||
this._providers.push(provider);
|
||||
|
||||
// request the capabilities from server
|
||||
provider.getServerCapabilities(this._clientCapabilties).then(serverCapabilities => {
|
||||
this._capabilities.push(serverCapabilities);
|
||||
this._onProviderRegistered.fire(serverCapabilities);
|
||||
this._registeredCapabilities++;
|
||||
this.resolveCapabilitiesIfReady();
|
||||
});
|
||||
}
|
||||
|
||||
private resolveCapabilitiesIfReady(): void {
|
||||
if (this._registeredCapabilities === this._expectedCapabilitiesCount) {
|
||||
this._onCapabilitiesReady.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: Action, 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Event Emitters
|
||||
public get onProviderRegisteredEvent(): Event<data.DataProtocolServerCapabilities> {
|
||||
return this._onProviderRegistered.event;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
80
src/sql/services/credentials/credentialsService.ts
Normal file
80
src/sql/services/credentials/credentialsService.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import data = require('data');
|
||||
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<data.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<data.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<data.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);
|
||||
}
|
||||
}
|
||||
103
src/sql/services/metadata/metadataService.ts
Normal file
103
src/sql/services/metadata/metadataService.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import data = require('data');
|
||||
|
||||
export const SERVICE_ID = 'metadataService';
|
||||
|
||||
export const IMetadataService = createDecorator<IMetadataService>(SERVICE_ID);
|
||||
|
||||
export interface IMetadataService {
|
||||
_serviceBrand: any;
|
||||
|
||||
getMetadata(connectionUri: string): Thenable<data.ProviderMetadata>;
|
||||
|
||||
getDatabaseNames(connectionUri: string): Thenable<string[]>;
|
||||
|
||||
getTableInfo(connectionUri: string, metadata: data.ObjectMetadata): Thenable<data.ColumnMetadata[]>;
|
||||
|
||||
getViewInfo(connectionUri: string, metadata: data.ObjectMetadata): Thenable<data.ColumnMetadata[]>;
|
||||
|
||||
/**
|
||||
* Register a metadata provider
|
||||
*/
|
||||
registerProvider(providerId: string, provider: data.MetadataProvider): void;
|
||||
}
|
||||
|
||||
export class MetadataService implements IMetadataService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
private _disposables: IDisposable[] = [];
|
||||
|
||||
private _providers: { [handle: string]: data.MetadataProvider; } = Object.create(null);
|
||||
|
||||
constructor(@IConnectionManagementService private _connectionService: IConnectionManagementService) {
|
||||
}
|
||||
|
||||
public getMetadata(connectionUri: string): Thenable<data.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(undefined);
|
||||
}
|
||||
|
||||
public getTableInfo(connectionUri: string, metadata: data.ObjectMetadata): Thenable<data.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: data.ObjectMetadata): Thenable<data.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: data.MetadataProvider): void {
|
||||
this._providers[providerId] = provider;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._disposables = dispose(this._disposables);
|
||||
}
|
||||
}
|
||||
114
src/sql/services/scripting/scriptingService.ts
Normal file
114
src/sql/services/scripting/scriptingService.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 data = require('data');
|
||||
import { warn, 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: data.ObjectMetadata, operation: ScriptOperation, paramDetails: data.ScriptingParamDetails): Thenable<data.ScriptingResult>;
|
||||
|
||||
/**
|
||||
* Register a scripting provider
|
||||
*/
|
||||
registerProvider(providerId: string, provider: data.ScriptingProvider): void;
|
||||
|
||||
/**
|
||||
* Callback method for when scripting is complete
|
||||
*/
|
||||
onScriptingComplete(handle: number, scriptingCompleteResult: data.ScriptingCompleteResult): void;
|
||||
|
||||
/**
|
||||
* Returns the result for an operation if the operation failed
|
||||
*/
|
||||
getOperationFailedResult(operationId: string): data.ScriptingCompleteResult;
|
||||
}
|
||||
|
||||
export class ScriptingService implements IScriptingService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
private _providers: { [handle: string]: data.ScriptingProvider; } = Object.create(null);
|
||||
|
||||
private failedScriptingOperations: { [operationId: string]: data.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: data.ObjectMetadata, operation: ScriptOperation, paramDetails: data.ScriptingParamDetails): Thenable<data.ScriptingResult> {
|
||||
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
|
||||
|
||||
if (providerId) {
|
||||
let provider = this._providers[providerId];
|
||||
if (provider) {
|
||||
switch (operation) {
|
||||
case (ScriptOperation.Select):
|
||||
return provider.scriptAsSelect(connectionUri, metadata, paramDetails);
|
||||
case (ScriptOperation.Create):
|
||||
return provider.scriptAsCreate(connectionUri, metadata, paramDetails);
|
||||
case (ScriptOperation.Delete):
|
||||
return provider.scriptAsDelete(connectionUri, metadata, paramDetails);
|
||||
default:
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for when scripting is complete
|
||||
* @param handle
|
||||
* @param scriptingCompleteResult
|
||||
*/
|
||||
public onScriptingComplete(handle: number, scriptingCompleteResult: data.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): data.ScriptingCompleteResult {
|
||||
if (operationId && operationId in this.failedScriptingOperations) {
|
||||
return this.failedScriptingOperations[operationId];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a scripting provider
|
||||
*/
|
||||
public registerProvider(providerId: string, provider: data.ScriptingProvider): void {
|
||||
this._providers[providerId] = provider;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
89
src/sql/services/serialization/serializationService.ts
Normal file
89
src/sql/services/serialization/serializationService.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 data from 'data';
|
||||
|
||||
export const SERVICE_ID = 'serializationService';
|
||||
|
||||
export interface SerializationProviderEvents {
|
||||
onSaveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable<data.SaveResultRequestResult>;
|
||||
}
|
||||
|
||||
export const ISerializationService = createDecorator<ISerializationService>(SERVICE_ID);
|
||||
|
||||
export interface ISerializationService {
|
||||
_serviceBrand: any;
|
||||
|
||||
saveAs(saveFormat: string, savePath: string, results: string, appendToFile: boolean): Thenable<data.SaveResultRequestResult>;
|
||||
|
||||
disabledSaveAs(): Thenable<data.SaveResultRequestResult>;
|
||||
|
||||
addEventListener(handle: number, events: SerializationProviderEvents): IDisposable;
|
||||
|
||||
getSerializationFeatureMetadataProvider(ownerUri: string): data.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<data.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<data.SaveResultRequestResult> {
|
||||
return Promise.resolve({messages: Constants.SerializationDisabled});
|
||||
|
||||
}
|
||||
|
||||
public getSerializationFeatureMetadataProvider(ownerUri: string) : data.FeatureMetadataProvider {
|
||||
let providerId: string = this._connectionService.getProviderIdFromUri(ownerUri);
|
||||
let providerCapabilities = this._capabilitiesService.getCapabilities().find(c => c.providerName === providerId);
|
||||
|
||||
if (providerCapabilities) {
|
||||
return providerCapabilities.features.find(f => f.featureName === SERVICE_ID);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user