diff --git a/src/sql/sqlops.d.ts b/src/sql/sqlops.d.ts index 0ab39faa66..e482bb57cc 100644 --- a/src/sql/sqlops.d.ts +++ b/src/sql/sqlops.d.ts @@ -1848,6 +1848,19 @@ declare module 'sqlops' { * @param {Account} updatedAccount Account object with updated properties */ export function accountUpdated(updatedAccount: Account): void; + + /** + * Gets all added accounts. + * @returns {Thenable} Promise to return the accounts + */ + export function getAllAccounts(): Thenable; + + /** + * 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 + */ + export function getSecurityToken(account: AccountWithProviderHandle): Thenable<{}>; } /** @@ -1915,6 +1928,21 @@ declare module 'sqlops' { isStale: boolean; } + /** + * Represents an account with account provider's handle + */ + export interface AccountWithProviderHandle { + /** + * Account + */ + account: Account; + + /** + * Account's provider handle + */ + providerHandle: number; + } + // - ACCOUNT PROVIDER ////////////////////////////////////////////////// /** * Error to be used when the user has cancelled the prompt or refresh methods. When diff --git a/src/sql/sqlops.proposed.d.ts b/src/sql/sqlops.proposed.d.ts index 1428c91f46..7118bdb177 100644 --- a/src/sql/sqlops.proposed.d.ts +++ b/src/sql/sqlops.proposed.d.ts @@ -1220,6 +1220,6 @@ declare module 'sqlops' { * returns the connection otherwise returns undefined * @param callback */ - export function openConnectionDialog(provider?: string[]): Thenable; + export function openConnectionDialog(provider?: string[], initialConnectionProfile?: IConnectionProfile): Thenable; } } diff --git a/src/sql/workbench/api/node/extHostAccountManagement.ts b/src/sql/workbench/api/node/extHostAccountManagement.ts index 2bf8302f21..38c5418534 100644 --- a/src/sql/workbench/api/node/extHostAccountManagement.ts +++ b/src/sql/workbench/api/node/extHostAccountManagement.ts @@ -64,6 +64,33 @@ export class ExtHostAccountManagement extends ExtHostAccountManagementShape { this._proxy.$accountUpdated(updatedAccount); } + public $getAllAccounts(): Thenable { + if (Object.keys(this._providers).length === 0) { + throw new Error('No account providers registered.'); + } + + let accountWithProviderHandles: sqlops.AccountWithProviderHandle[] = []; + let promises: Thenable[] = []; + + for (let providerKey in this._providers) { + let providerHandle = parseInt(providerKey); + let provider = this._providers[providerHandle]; + + promises.push(this._proxy.$getAccountsForProvider(provider.metadata.id).then( + (accounts) => { + accounts.forEach((account) => { + accountWithProviderHandles.push({ + account: account, + providerHandle: providerHandle + }); + }); + } + )); + } + + return Promise.all(promises).then(() => accountWithProviderHandles); + } + public $registerAccountProvider(providerMetadata: sqlops.AccountProviderMetadata, provider: sqlops.AccountProvider): Disposable { let self = this; diff --git a/src/sql/workbench/api/node/extHostConnectionManagement.ts b/src/sql/workbench/api/node/extHostConnectionManagement.ts index 0c3f17bd4a..6d73064365 100644 --- a/src/sql/workbench/api/node/extHostConnectionManagement.ts +++ b/src/sql/workbench/api/node/extHostConnectionManagement.ts @@ -32,8 +32,8 @@ export class ExtHostConnectionManagement extends ExtHostConnectionManagementShap return this._proxy.$getCredentials(connectionId); } - public $openConnectionDialog(providers?: string[]): Thenable { - return this._proxy.$openConnectionDialog(providers); + public $openConnectionDialog(providers?: string[], initialConnectionProfile?: sqlops.IConnectionProfile): Thenable { + return this._proxy.$openConnectionDialog(providers, initialConnectionProfile); } public $listDatabases(connectionId: string): Thenable { diff --git a/src/sql/workbench/api/node/mainThreadAccountManagement.ts b/src/sql/workbench/api/node/mainThreadAccountManagement.ts index dad2cc4890..1b4a881df5 100644 --- a/src/sql/workbench/api/node/mainThreadAccountManagement.ts +++ b/src/sql/workbench/api/node/mainThreadAccountManagement.ts @@ -46,6 +46,10 @@ export class MainThreadAccountManagement implements MainThreadAccountManagementS this._accountManagementService.accountUpdated(updatedAccount); } + public $getAccountsForProvider(providerId: string): Thenable { + return this._accountManagementService.getAccountsForProvider(providerId); + } + public $registerAccountProvider(providerMetadata: sqlops.AccountProviderMetadata, handle: number): Thenable { let self = this; diff --git a/src/sql/workbench/api/node/mainThreadConnectionManagement.ts b/src/sql/workbench/api/node/mainThreadConnectionManagement.ts index 832b763329..1b5cc67dac 100644 --- a/src/sql/workbench/api/node/mainThreadConnectionManagement.ts +++ b/src/sql/workbench/api/node/mainThreadConnectionManagement.ts @@ -51,8 +51,8 @@ export class MainThreadConnectionManagement implements MainThreadConnectionManag } - public async $openConnectionDialog(providers: string[]): Promise { - let connectionProfile = await this._connectionDialogService.openDialogAndWait(this._connectionManagementService, { connectionType: 1, providers: providers }); + public async $openConnectionDialog(providers: string[], initialConnectionProfile?: IConnectionProfile): Promise { + let connectionProfile = await this._connectionDialogService.openDialogAndWait(this._connectionManagementService, { connectionType: 1, providers: providers }, initialConnectionProfile); return connectionProfile ? { connectionId: connectionProfile.id, options: connectionProfile.options, diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index de30d7fa01..d706216467 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -90,6 +90,12 @@ export function createApiFactory( }, accountUpdated(updatedAccount: sqlops.Account): void { return extHostAccountManagement.$accountUpdated(updatedAccount); + }, + getAllAccounts(): Thenable { + return extHostAccountManagement.$getAllAccounts(); + }, + getSecurityToken(account: sqlops.AccountWithProviderHandle): Thenable<{}> { + return extHostAccountManagement.$getSecurityToken(account.providerHandle, account.account); } }; @@ -104,8 +110,8 @@ export function createApiFactory( getCredentials(connectionId: string): Thenable<{ [name: string]: string }> { return extHostConnectionManagement.$getCredentials(connectionId); }, - openConnectionDialog(providers?: string[]): Thenable { - return extHostConnectionManagement.$openConnectionDialog(providers); + openConnectionDialog(providers?: string[], initialConnectionProfile?: sqlops.IConnectionProfile): Thenable { + return extHostConnectionManagement.$openConnectionDialog(providers, initialConnectionProfile); }, listDatabases(connectionId: string): Thenable { return extHostConnectionManagement.$listDatabases(connectionId); diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index f954f6b8a7..455eddbbaa 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -446,6 +446,8 @@ export interface MainThreadAccountManagementShape extends IDisposable { $endAutoOAuthDeviceCode(): void; $accountUpdated(updatedAccount: sqlops.Account): void; + + $getAccountsForProvider(providerId: string): Thenable; } export interface MainThreadResourceProviderShape extends IDisposable { @@ -499,7 +501,7 @@ export interface MainThreadConnectionManagementShape extends IDisposable { $getActiveConnections(): Thenable; $getCurrentConnection(): Thenable; $getCredentials(connectionId: string): Thenable<{ [name: string]: string }>; - $openConnectionDialog(providers: string[]): Thenable; + $openConnectionDialog(providers: string[], initialConnectionProfile?: sqlops.IConnectionProfile): Thenable; $listDatabases(connectionId: string): Thenable; $getConnectionString(connectionId: string, includePassword: boolean): Thenable; $getUriForConnection(connectionId: string): Thenable; @@ -687,4 +689,4 @@ export interface ExtHostQueryEditorShape { export interface MainThreadQueryEditorShape extends IDisposable { $connect(fileUri: string, connectionId: string): Thenable; $runQuery(fileUri: string): void; -} +} \ No newline at end of file diff --git a/src/sqltest/workbench/api/extHostAccountManagement.test.ts b/src/sqltest/workbench/api/extHostAccountManagement.test.ts index 39ed82ee33..869f4bd00c 100644 --- a/src/sqltest/workbench/api/extHostAccountManagement.test.ts +++ b/src/sqltest/workbench/api/extHostAccountManagement.test.ts @@ -256,6 +256,91 @@ suite('ExtHostAccountManagement', () => { }) .then(() => done(), (err) => done(err)); }); + + // GETALLACCOUNTS TESTS /////////////////////////////////////////////////////// + test('GetAllAccounts - Success', (done) => { + let mockAccountProviderMetadata = { + id: 'azure', + displayName: 'Azure' + }; + + let mockAccount1 = { + key: { + providerId: mockAccountProviderMetadata.id, + accountId: 'azure_account_1' + }, + displayInfo: { + contextualDisplayName: 'Microsoft Account', + accountType: 'microsoft', + displayName: 'Azure Account 1' + }, + properties: [], + isStale: false + }; + let mockAccount2 = { + key: { + providerId: mockAccountProviderMetadata.id, + accountId: 'azure_account_2' + }, + displayInfo: { + contextualDisplayName: 'Work/School Account', + accountType: 'microsoft', + displayName: 'Azure Account 2' + }, + properties: [], + isStale: false + }; + let mockAccounts = [mockAccount1, mockAccount2]; + + let expectedAccounts = [ + { + account: mockAccount1, + providerHandle: 0 + }, + { + account: mockAccount2, + providerHandle: 0 + } + ]; + + let mockAccountManagementService = getMockAccountManagementService(mockAccounts); + instantiationService.stub(IAccountManagementService, mockAccountManagementService.object); + let accountManagementService = instantiationService.createInstance(MainThreadAccountManagement); + threadService.set(SqlMainContext.MainThreadAccountManagement, accountManagementService); + + // Setup: Create ext host account management with registered account provider + let extHost = new ExtHostAccountManagement(threadService); + extHost.$registerAccountProvider(mockAccountProviderMetadata, new AccountProviderStub()); + + // If: I get all accounts + extHost.$getAllAccounts() + .then((accounts) => { + // Then: The call should have been passed to the account management service + mockAccountManagementService.verify( + (obj) => obj.getAccountsForProvider(TypeMoq.It.isAny()), + TypeMoq.Times.once() + ); + + assert.ok(Array.isArray(accounts)); + assert.equal(accounts.length, expectedAccounts.length); + assert.deepStrictEqual(accounts, expectedAccounts); + }) + .then(() => done(), (err) => done(err)); + }); + + test('GetAllAccounts - No account providers', (done) => { + // Setup: Create ext host account management with no registered account providers + let extHost = new ExtHostAccountManagement(threadService); + + // If: I get all accounts + // Then: It should throw + assert.throws( + () => extHost.$getAllAccounts(), + (error) => { + return error.message === 'No account providers registered.'; + }); + done(); + }); }); function getMockAccountProvider(): TypeMoq.Mock { @@ -271,3 +356,12 @@ function getMockAccountProvider(): TypeMoq.Mock { return mock; } + +function getMockAccountManagementService(accounts: sqlops.Account[]): TypeMoq.Mock { + let mockAccountManagementService = TypeMoq.Mock.ofType(AccountManagementTestService); + + mockAccountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(accounts)); + + return mockAccountManagementService; +} \ No newline at end of file