mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-05 09:35:39 -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>;
|
||||
}
|
||||
Reference in New Issue
Block a user